1.简介
- 设置里的应用界面
- 这里主要是研究一下某个app的详情页,uninstall按钮点击以后都干了啥。
2.AppDashboardFragment.java
如果没有recent apps的话,显示效果如下
2.1.apps.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="apps_screen"
android:title="@string/apps_dashboard_title">
<!-- 没有最近打开的app信息的话显示这个,有的话显示下边那个-->
<Preference
android:key="all_app_infos"
android:title="@string/all_apps"
android:summary="@string/summary_placeholder"
android:order="-999"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
settings:keywords="@string/keywords_applications_settings"/>
<PreferenceCategory
android:key="recent_apps_category"
android:title="@string/recent_app_category_title"
android:order="-998"
settings:searchable="false">
<!-- Placeholder for a list of recent apps -->
<!-- See all apps -->
<Preference
android:key="see_all_apps"
android:title="@string/default_see_all_apps_title"
android:icon="@drawable/ic_chevron_right_24dp"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
android:order="5"
settings:searchable="false">
</Preference>
</PreferenceCategory>
<PreferenceCategory
android:key="general_category"
android:title="@string/category_name_general"
android:order="-997"
android:visibility="gone"
settings:searchable="false"/>
<Preference
android:key="default_apps"
android:title="@string/app_default_dashboard_title"
android:order="-996"
settings:controller="com.android.settings.applications.DefaultAppsPreferenceController">
<intent android:action="android.settings.MANAGE_DEFAULT_APPS_SETTINGS"/>
</Preference>
<PreferenceCategory
android:key="dashboard_tile_placeholder"
android:order="10"/>
<Preference
android:key="hibernated_apps"
android:title="@string/unused_apps"
android:summary="@string/summary_placeholder"
android:order="15"
settings:keywords="app_hibernation_key"
settings:controller="com.android.settings.applications.HibernatedAppsPreferenceController">
<intent android:action="android.intent.action.MANAGE_UNUSED_APPS"/>
</Preference>
<Preference
android:key="app_battery_usage"
android:order="17"
android:title="@string/app_battery_usage_title"
android:summary="@string/app_battery_usage_summary"
settings:controller="com.android.settings.applications.AppBatteryUsagePreferenceController"
android:fragment="com.android.settings.applications.manageapplications.ManageApplications">
<extra
android:name="classname"
android:value="com.android.settings.Settings$AppBatteryUsageActivity"/>
</Preference>
<Preference
android:key="special_access"
android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
android:title="@string/special_access"
android:order="20"
settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
</PreferenceScreen>
2.2.相关代码
>buildPreferenceControllers
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
//这个controller就是查询recent apps数据的
controllers.add(new AppsPreferenceController(context));
return controllers;
}
>onAttach
public void onAttach(Context context) {
super.onAttach(context);
use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
mAppsPreferenceController = use(AppsPreferenceController.class);
mAppsPreferenceController.setFragment(this /* fragment */);
getSettingsLifecycle().addObserver(mAppsPreferenceController);
final HibernatedAppsPreferenceController hibernatedAppsPreferenceController =
use(HibernatedAppsPreferenceController.class);
getSettingsLifecycle().addObserver(hibernatedAppsPreferenceController);
}
3.AppsPreferenceController.java
3.1.initPreferences
获取组件
private void initPreferences(PreferenceScreen screen) {
mRecentAppsCategory = screen.findPreference(KEY_RECENT_APPS_CATEGORY);
mGeneralCategory = screen.findPreference(KEY_GENERAL_CATEGORY);
mAllAppsInfoPref = screen.findPreference(KEY_ALL_APP_INFO);
mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
mRecentAppsCategory.setVisible(false);
mGeneralCategory.setVisible(false);
mAllAppsInfoPref.setVisible(false);
mSeeAllPref.setVisible(false);
}
3.2.refreshUi
void refreshUi() {
loadAllAppsCount();
mRecentApps = loadRecentApps();
//具体显示效果可以看开头的两张图,
if (!mRecentApps.isEmpty()) {
displayRecentApps();
mAllAppsInfoPref.setVisible(false);
mRecentAppsCategory.setVisible(true);
mGeneralCategory.setVisible(true);
mSeeAllPref.setVisible(true);
} else {
mAllAppsInfoPref.setVisible(true);
mRecentAppsCategory.setVisible(false);
mGeneralCategory.setVisible(false);
mSeeAllPref.setVisible(false);
}
}
3.3.loadAllAppsCount
void loadAllAppsCount() {
// Show total number of installed apps as See all's summary.
new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
mContext.getPackageManager()) {
@Override
protected void onCountComplete(int num) {
if (!mRecentApps.isEmpty()) {
//show all %d apps.
mSeeAllPref.setTitle(
mContext.getResources().getQuantityString(R.plurals.see_all_apps_title,
num, num));
} else {
// %d apps installed
mAllAppsInfoPref.setSummary(mContext.getString(R.string.apps_summary, num));
}
}
}.execute();
}
3.4.loadRecentApps
List<UsageStats> loadRecentApps() {
final RecentAppStatsMixin recentAppStatsMixin = new RecentAppStatsMixin(mContext,
SHOW_RECENT_APP_COUNT);
recentAppStatsMixin.loadDisplayableRecentApps(SHOW_RECENT_APP_COUNT);
return recentAppStatsMixin.mRecentApps;
}
3.5.displayRecentApps
private void displayRecentApps() {
if (mRecentAppsCategory != null) {
final Map<String, Preference> existedAppPreferences = new ArrayMap<>();
final int prefCount = mRecentAppsCategory.getPreferenceCount();
//see_all 那个是自带的,其他的都是动态添加了,这里获取旧数据
for (int i = 0; i < prefCount; i++) {
final Preference pref = mRecentAppsCategory.getPreference(i);
final String key = pref.getKey();
if (!TextUtils.equals(key, KEY_SEE_ALL)) {
existedAppPreferences.put(key, pref);
}
}
int showAppsCount = 0;
for (UsageStats stat : mRecentApps) {
final String pkgName = stat.getPackageName();
final ApplicationsState.AppEntry appEntry =
mApplicationsState.getEntry(pkgName, mUserId);
if (appEntry == null) {
continue;
}
boolean rebindPref = true;
Preference pref = existedAppPreferences.remove(pkgName);
//旧数据没有,创建新的,
if (pref == null) {
pref = new AppPreference(mContext);
rebindPref = false;
}
//设置显示的内容
pref.setKey(pkgName);
pref.setTitle(appEntry.label);
pref.setIcon(Utils.getBadgedIcon(mContext, appEntry.info));
pref.setSummary(StringUtil.formatRelativeTime(mContext,
System.currentTimeMillis() - stat.getLastTimeUsed(), false,
RelativeDateTimeFormatter.Style.SHORT));
pref.setOrder(showAppsCount++);
//点击事件,跳转到应用详情页
pref.setOnPreferenceClickListener(preference -> {
AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
mContext.getString(R.string.application_info_label),
pkgName, appEntry.info.uid,
mHost, 1001 /*RequestCode*/, getMetricsCategory());
return true;
});
if (!rebindPref) {
mRecentAppsCategory.addPreference(pref);
}
}
//移除过时的选项
for (Preference unusedPref : existedAppPreferences.values()) {
mRecentAppsCategory.removePreference(unusedPref);
}
}
}
3.6.startAppInfoFragment
AppInfoBase.java -跳转到详情页
public static void startAppInfoFragment(Class<?> fragment, String title,
String pkg, int uid, Fragment source, int request, int sourceMetricsCategory) {
final Bundle args = new Bundle();
args.putString(AppInfoBase.ARG_PACKAGE_NAME, pkg);
args.putInt(AppInfoBase.ARG_PACKAGE_UID, uid);
new SubSettingLauncher(source.getContext())
.setDestination(fragment.getName())
.setSourceMetricsCategory(sourceMetricsCategory)
.setTitleText(title)
.setArguments(args)
.setUserHandle(new UserHandle(UserHandle.getUserId(uid)))
.setResultListener(source, request)
.launch();
}
4.RecentAppStatsMixin.java
最近使用的app的查询工具类
4.1.RecentAppStatsMixin
public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
static {
SKIP_SYSTEM_PACKAGES.addAll(Arrays.asList(
"android",
"com.android.phone",
SETTINGS_PACKAGE_NAME,
"com.android.systemui",
"com.android.providers.calendar",
"com.android.providers.media"
));
}
public RecentAppStatsMixin(Context context, int maximumApps) {
mContext = context;
mMaximumApps = maximumApps;
mUserId = UserHandle.myUserId();
mPm = mContext.getPackageManager();
mPowerManager = mContext.getSystemService(PowerManager.class);
mUsageStatsManager = mContext.getSystemService(UsageStatsManager.class);
mApplicationsState = ApplicationsState.getInstance(
(Application) mContext.getApplicationContext());
mRecentApps = new ArrayList<>();
mAppStatsListeners = new ArrayList<>();
}
4.2.loadDisplayableRecentApps
void loadDisplayableRecentApps(int number) {
mRecentApps.clear();
mCalendar = Calendar.getInstance();
//过去一天
mCalendar.add(Calendar.DAY_OF_YEAR, -1);
//省电模式返回空
final List<UsageStats> mStats = mPowerManager.isPowerSaveMode()
? new ArrayList<>()
: mUsageStatsManager.queryUsageStats(
//查询过去一天的数据
UsageStatsManager.INTERVAL_BEST, mCalendar.getTimeInMillis(),
System.currentTimeMillis());
final Map<String, UsageStats> map = new ArrayMap<>();
final int statCount = mStats.size();
for (int i = 0; i < statCount; i++) {
final UsageStats pkgStats = mStats.get(i);
if (!shouldIncludePkgInRecents(pkgStats)) {
continue;
}
final String pkgName = pkgStats.getPackageName();
final UsageStats existingStats = map.get(pkgName);
if (existingStats == null) {
map.put(pkgName, pkgStats);
} else {
existingStats.add(pkgStats);
}
}
final List<UsageStats> packageStats = new ArrayList<>();
packageStats.addAll(map.values());
//排序
Collections.sort(packageStats, this /* comparator */);
int count = 0;
for (UsageStats stat : packageStats) {
//根据包名查找应用信息
final ApplicationsState.AppEntry appEntry = mApplicationsState.getEntry(
stat.getPackageName(), mUserId);
if (appEntry == null) {
continue;
}
mRecentApps.add(stat);
count++;
if (count >= number) {
break;
}
}
}
>compare
数据排序按照最后使用的时间
public final int compare(UsageStats a, UsageStats b) {
// return by descending order
return Long.compare(b.getLastTimeUsed(), a.getLastTimeUsed());
}
4.3.shouldIncludePkgInRecents
排除一些特殊的应用,比如几个系统应用,桌面啥的。
private boolean shouldIncludePkgInRecents(UsageStats stat) {
final String pkgName = stat.getPackageName();
if (stat.getLastTimeUsed() < mCalendar.getTimeInMillis()) {
Log.d(TAG, "Invalid timestamp (usage time is more than 24 hours ago), skipping "
+ pkgName);
return false;
}
if (SKIP_SYSTEM_PACKAGES.contains(pkgName)) {
Log.d(TAG, "System package, skipping " + pkgName);
return false;
}
if (AppUtils.isHiddenSystemModule(mContext, pkgName)) {
return false;
}
final Intent launchIntent = new Intent().addCategory(Intent.CATEGORY_LAUNCHER)
.setPackage(pkgName);
if (mPm.resolveActivity(launchIntent, 0) == null) {
// Not visible on launcher -> likely not a user visible app, skip if non-instant.
final ApplicationsState.AppEntry appEntry =
mApplicationsState.getEntry(pkgName, mUserId);
if (appEntry == null || appEntry.info == null || !AppUtils.isInstant(appEntry.info)) {
Log.d(TAG, "Not a user visible or instant app, skipping " + pkgName);
return false;
}
}
return true;
}
5.AppInfoDashboardFragment
5.1.app_info_settings.xml
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="installed_app_detail_settings_screen">
<com.android.settingslib.widget.LayoutPreference
android:key="header_view"
android:layout="@layout/settings_entity_header"
android:selectable="false"
android:order="-10000" />
<com.android.settingslib.widget.LayoutPreference
android:key="instant_app_buttons"
android:layout="@layout/instant_app_buttons"
android:selectable="false"
android:order="-9999" />
<com.android.settingslib.widget.ActionButtonsPreference
android:key="action_buttons"
android:order="-9998" />
<Preference
android:key="app_settings_link"
android:title="@string/app_settings_link"
settings:controller="com.android.settings.applications.appinfo.AppSettingPreferenceController" />
<Preference
android:key="all_services_settings"
android:title="@string/app_info_all_services_label"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppAllServicesPreferenceController" />
<Preference
android:key="notification_settings"
android:title="@string/notifications_label"
settings:controller="com.android.settings.applications.appinfo.AppNotificationPreferenceController" />
<com.android.settings.widget.FixedLineSummaryPreference
android:key="permission_settings"
android:title="@string/permissions_label"
android:summary="@string/summary_placeholder"
settings:summaryLineCount="1"
settings:controller="com.android.settings.applications.appinfo.AppPermissionPreferenceController" />
<Preference
android:key="storage_settings"
android:title="@string/storage_settings_for_app"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppStoragePreferenceController" />
<com.android.settings.applications.AppDomainsPreference
android:key="instant_app_launch_supported_domain_urls"
android:title="@string/app_launch_supported_domain_urls_title"
android:selectable="true"
settings:controller="com.android.settings.applications.appinfo.InstantAppDomainsPreferenceController" />
<Preference
android:key="data_settings"
android:title="@string/data_usage_app_summary_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppDataUsagePreferenceController" />
<Preference
android:key="time_spent_in_app"
android:title="@string/time_spent_in_app_pref_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.TimeSpentInAppPreferenceController" />
<Preference
android:key="battery"
android:title="@string/app_battery_usage_title"
android:summary="@string/summary_placeholder" />
<Preference
android:key="app_language_setting"
android:title="@string/app_locale_preference_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AppLocalePreferenceController" />
<Preference
android:key="preferred_settings"
android:title="@string/launch_by_default"
android:summary="@string/summary_placeholder"
android:selectable="true"
settings:controller="com.android.settings.applications.appinfo.AppOpenByDefaultPreferenceController" />
<Preference
android:key="memory"
android:title="@string/memory_settings_title"
android:summary="@string/summary_placeholder"
android:enabled="false" />
<!-- Default apps shortcuts -->
<Preference
android:key="default_home"
android:title="@string/home_app"
android:summary="@string/summary_placeholder" />
<Preference
android:key="default_browser"
android:title="@string/default_browser_title"
android:summary="@string/summary_placeholder" />
<Preference
android:key="default_phone_app"
android:title="@string/default_phone_title"
android:summary="@string/default_phone_title" />
<Preference
android:key="default_emergency_app"
android:title="@string/default_emergency_app"
android:summary="@string/summary_placeholder" />
<Preference
android:key="default_sms_app"
android:title="@string/sms_application_title"
android:summary="@string/summary_placeholder" />
<PreferenceCategory
android:key="app_hibernation_info"
android:title="@string/unused_apps_category"
settings:controller=
"com.android.settings.applications.appinfo.AppHibernationPreferenceCategoryController">
<SwitchPreference
android:key="hibernation_switch"
android:title="@string/unused_apps_switch"
android:summary="@string/unused_apps_switch_summary"
settings:controller=
"com.android.settings.applications.appinfo.HibernationSwitchPreferenceController" />
</PreferenceCategory>
<!-- Advanced apps settings -->
<PreferenceCategory
android:key="advanced_app_info"
android:title="@string/advanced_apps"
settings:controller="com.android.settings.applications.appinfo.AdvancedAppInfoPreferenceCategoryController">
<Preference
android:key="system_alert_window"
android:title="@string/draw_overlay"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.DrawOverlayDetailPreferenceController" />
<Preference
android:key="write_settings_apps"
android:title="@string/write_settings"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.WriteSystemSettingsPreferenceController" />
<Preference
android:key="picture_in_picture"
android:title="@string/picture_in_picture_app_detail_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController" />
<Preference
android:key="install_other_apps"
android:title="@string/install_other_apps"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.ExternalSourceDetailPreferenceController" />
<Preference
android:key="interact_across_profiles"
android:title="@string/interact_across_profiles_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController" />
<Preference
android:key="alarms_and_reminders"
android:title="@string/alarms_and_reminders_title"
android:summary="@string/summary_placeholder"
settings:controller="com.android.settings.applications.appinfo.AlarmsAndRemindersDetailPreferenceController" />
</PreferenceCategory>
<!-- App installer info -->
<PreferenceCategory
android:key="app_installer"
android:title="@string/app_install_details_group_title"
settings:controller="com.android.settings.applications.appinfo.AppInstallerPreferenceCategoryController">
<Preference
android:key="app_info_store"
android:title="@string/app_install_details_title"
settings:controller="com.android.settings.applications.appinfo.AppInstallerInfoPreferenceController" />
</PreferenceCategory>
<Preference
android:key="app_version"
android:selectable="false"
android:order="9999"
settings:controller="com.android.settings.applications.appinfo.AppVersionPreferenceController"
settings:allowDividerAbove="true"
settings:enableCopying="true"/>
</PreferenceScreen>
5.2.createPreferenceControllers
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
retrieveAppEntry();
if (mPackageInfo == null) {
return null;
}
final String packageName = getPackageName();
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final Lifecycle lifecycle = getSettingsLifecycle();
//这个是控制应用名字和图标的
controllers.add(
new AppHeaderViewPreferenceController(context, this, packageName, lifecycle));
for (AbstractPreferenceController controller : controllers) {
mCallbacks.add((Callback) controller);
}
// The following are controllers for preferences that don't need to refresh the preference
// state when app state changes.
mInstantAppButtonPreferenceController =
new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle);
controllers.add(mInstantAppButtonPreferenceController);
//这个是那3个按钮,open,uninstall,force stop
mAppButtonsPreferenceController = new AppButtonsPreferenceController(
(SettingsActivity) getActivity(), this, lifecycle, packageName, mState,
REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
controllers.add(mAppButtonsPreferenceController);
controllers.add(new AppBatteryPreferenceController(
context, this, packageName, getUid(), lifecycle));
controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
return controllers;
}
6.AppButtonsPreferenceController.java
6.1.initButtonPreference
3个按钮是个自定义的东西,如下,设置按钮文字图标以及点击事件
private void initButtonPreference() {
mButtonsPref = ((ActionButtonsPreference) mScreen.findPreference(
KEY_ACTION_BUTTONS))
.setButton1Text(R.string.launch_instant_app)
.setButton1Icon(R.drawable.ic_settings_open)
.setButton1OnClickListener(v -> launchApplication())
.setButton2Text(R.string.uninstall_text)
.setButton2Icon(R.drawable.ic_settings_delete)
.setButton2OnClickListener(new UninstallAndDisableButtonListener())
.setButton3Text(R.string.force_stop)
.setButton3Icon(R.drawable.ic_settings_force_stop)
.setButton3OnClickListener(new ForceStopButtonListener())
.setButton3Enabled(false);
}
6.2.onResume
public void onResume() {
if (isAvailable()) {
mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
mActivity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
mActivity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
//刷新ui
if (!refreshUi()) {
setIntentAndFinish(true, false);
}
}
}
6.3.refreshUi
boolean refreshUi() {
if (mPackageName == null) {
return false;
}
retrieveAppEntry();
if (mAppEntry == null || mPackageInfo == null) {
return false;
}
// Get list of "home" apps and trace through any meta-data references
List<ResolveInfo> homeActivities = new ArrayList<>();
mPm.getHomeActivities(homeActivities);
mHomePackages.clear();
//获取桌面程序
for (int i = 0, size = homeActivities.size(); i < size; i++) {
ResolveInfo ri = homeActivities.get(i);
final String activityPkg = ri.activityInfo.packageName;
mHomePackages.add(activityPkg);
// Also make sure to include anything proxying for the home app
final Bundle metadata = ri.activityInfo.metaData;
if (metadata != null) {
final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
if (signaturesMatch(metaPkg, activityPkg)) {
mHomePackages.add(metaPkg);
}
}
}
// When the app was installed from instant state, buttons preferences could be null.
if (mButtonsPref == null) {
initButtonPreference();
mButtonsPref.setVisible(true);
}
updateOpenButton();
updateUninstallButton();
updateForceStopButton();
return true;
}
>updateOpenButton
能找到启动页,那么open按钮可见
void updateOpenButton() {
mAppLaunchIntent = mPm.getLaunchIntentForPackage(mPackageName);
mButtonsPref.setButton1Visible(mAppLaunchIntent != null);
}
6.4.updateUninstallButton
这个按钮有两种状态,disable或者uninstall,比如系统应用的话是无法卸载的,只有可能是disable,
void updateUninstallButton() {
final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean enabled = true;
if (isBundled) {
enabled = handleDisableable();
} else {
if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
&& mUserManager.getUsers().size() >= 2) {
// When we have multiple users, there is a separate menu
// to uninstall for all users.
enabled = false;
}
}
// If this is a device admin, it can't be uninstalled or disabled.
// We do this here so the text of the button is still set correctly.
if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
enabled = false;
}
// We don't allow uninstalling DO/PO on *any* users if it's a system app, because
// "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
// will clear data on all users.
if (isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) {
if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mPackageInfo.packageName)) {
enabled = false;
}
// We allow uninstalling if the calling user is not a DO/PO and if it's not a system app,
// because this will not have device-wide consequences.
} else {
if (Utils.isProfileOrDeviceOwner(mDpm, mPackageInfo.packageName, mUserId)) {
enabled = false;
}
}
// Don't allow uninstalling the device provisioning package.
if (Utils.isDeviceProvisioningPackage(mContext.getResources(),
mAppEntry.info.packageName)) {
enabled = false;
}
// If the uninstall intent is already queued, disable the uninstall button
if (mDpm.isUninstallInQueue(mPackageName)) {
enabled = false;
}
// Home apps need special handling. Bundled ones we don't risk downgrading
// because that can interfere with home-key resolution. Furthermore, we
// can't allow uninstallation of the only home app, and we don't want to
// allow uninstallation of an explicitly preferred one -- the user can go
// to Home settings and pick a different one, after which we'll permit
// uninstallation of the now-not-default one.
if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
if (isBundled) {
enabled = false;
} else {
ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);
if (currentDefaultHome == null) {
// No preferred default, so permit uninstall only when
// there is more than one candidate
enabled = (mHomePackages.size() > 1);
} else {
// There is an explicit default home app -- forbid uninstall of
// that one, but permit it for installed-but-inactive ones.
enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
}
}
}
if (mAppsControlDisallowedBySystem) {
enabled = false;
}
// Resource overlays can be uninstalled iff they are public
// (installed on /data) and disabled. ("Enabled" means they
// are in use by resource management.) If they are
// system/vendor, they can never be uninstalled. :-(
if (mAppEntry.info.isResourceOverlay()) {
if (isBundled) {
enabled = false;
} else {
String pkgName = mAppEntry.info.packageName;
UserHandle user = UserHandle.getUserHandleForUid(mAppEntry.info.uid);
OverlayInfo overlayInfo = mOverlayManager.getOverlayInfo(pkgName, user);
if (overlayInfo != null && overlayInfo.isEnabled()) {
ApplicationsState.AppEntry targetEntry =
mState.getEntry(overlayInfo.targetPackageName,
UserHandle.getUserId(mAppEntry.info.uid));
if (targetEntry != null) {
enabled = false;
}
}
}
}
mButtonsPref.setButton2Enabled(enabled);
}
>handleDisableable
boolean handleDisableable() {
boolean disableable = false;
//桌面应用或者是系统核心应用
if (mHomePackages.contains(mAppEntry.info.packageName)
|| isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) {
//不能卸载,所以文字改成disable
mButtonsPref.setButton2Text(R.string.disable_text)
.setButton2Icon(R.drawable.ic_settings_disable);
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
mButtonsPref.setButton2Text(R.string.disable_text)
.setButton2Icon(R.drawable.ic_settings_disable);
disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
.contains(mAppEntry.info.packageName);
} else {
mButtonsPref.setButton2Text(R.string.enable_text)
.setButton2Icon(R.drawable.ic_settings_enable);
disableable = true;
}
return disableable;
}
>isDisabledUntilUsed
使用之前都是不可用状态,比如输入法
private boolean isDisabledUntilUsed() {
return mAppEntry.info.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
}
6.5.updateForceStopButton
强制停止按钮状态的更新,按钮有可能不可用。
void updateForceStopButton() {
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
// User can't force stop device admin.
Log.w(TAG, "User can't force stop device admin");
updateForceStopButtonInner(false /* enabled */);
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
// If the app isn't explicitly stopped, then always show the
// force stop button.
Log.w(TAG, "App is not explicitly stopped");
updateForceStopButtonInner(true /* enabled */);
} else {
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
Uri.fromParts("package", mAppEntry.info.packageName, null));
intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{mAppEntry.info.packageName});
intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
Log.d(TAG, "Sending broadcast to query restart status for "
+ mAppEntry.info.packageName);
mActivity.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}
6.5.launchApplication
private void launchApplication() {
if (mAppLaunchIntent != null) {
if (mAccessedFromAutoRevoke) {
}
mContext.startActivityAsUser(mAppLaunchIntent, new UserHandle(mUserId));
mMetricsFeatureProvider.action(mActivity,
SettingsEnums.ACTION_APP_INFO_OPEN, mPackageName);
}
}
6.6.UninstallAndDisableButtonListener
uninstall 或者disable 按钮的点击事件
private class UninstallAndDisableButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (mAccessedFromAutoRevoke) {
}
final String packageName = mAppEntry.info.packageName;
// Uninstall
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
stopListeningToPackageRemove();
Intent uninstallDaIntent = new Intent(mActivity, DeviceAdminAdd.class);
uninstallDaIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
packageName);
mMetricsFeatureProvider.action(mActivity,
SettingsEnums.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN,
getPackageNameForMetric());
mFragment.startActivityForResult(uninstallDaIntent, mRequestRemoveDeviceAdmin);
return;
}
RestrictedLockUtils.EnforcedAdmin admin =
RestrictedLockUtilsInternal.checkIfUninstallBlocked(mActivity,
packageName, mUserId);
boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
RestrictedLockUtilsInternal.hasBaseUserRestriction(mActivity, packageName,
mUserId);
if (admin != null && !uninstallBlockedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity, admin);
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
//系统应用,
if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
// If the system app has an update and this is the only user on the device,
// then offer to downgrade the app, otherwise only offer to disable the
// app for this user.
//disable,这里弹框提示文字是一样的,具体见下边6.9
if (mUpdatedSysApp && isSingleUser()) {
showDialogInner(ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE);
} else {
showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE);
}
} else {
//变为清单文件里默认的状态
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT));
}
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
//卸载
uninstallPkg(packageName, true, false);
} else {
uninstallPkg(packageName, false, false);
}
}
}
>DisableChangerRunnable
public DisableChangerRunnable(PackageManager pm, String packageName, int state) {
mPm = pm;
mPackageName = packageName;
mState = state;
}
@Override
public void run() {
mPm.setApplicationEnabledSetting(mPackageName, mState, 0);
}
>uninstallPkg
void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
stopListeningToPackageRemove();
// Create new intent to launch Uninstaller activity
Uri packageUri = Uri.parse("package:" + packageName);
//具体见小节8
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
mDisableAfterUninstall = andDisable;
}
>onActivityResult
AppInfoDashboardFragment.java
- 最后还是交给controller来处理卸载结果的
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_UNINSTALL) {
// Refresh option menu
getActivity().invalidateOptionsMenu();
}
if (mAppButtonsPreferenceController != null) {
mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
}
}
>handleActivityResult
public void handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == mRequestUninstall) {
if (mDisableAfterUninstall) {
mDisableAfterUninstall = false;
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
}
refreshAndFinishIfPossible(true);
} else if (requestCode == mRequestRemoveDeviceAdmin) {
refreshAndFinishIfPossible(false);
}
}
6.7.ForceStopButtonListener
private class ForceStopButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
// 受保护的,无法stop
if (mPm.isPackageStateProtected(mAppEntry.info.packageName, mUserId)) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity,
RestrictedLockUtilsInternal.getDeviceOwner(mActivity));
return;
}
if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
mActivity, mAppsControlDisallowedAdmin);
} else {
//显示stop确认对话框,见6.9
showDialogInner(ButtonActionDialogFragment.DialogType.FORCE_STOP);
}
}
}
>forceStopPackage
void forceStopPackage(String pkgName) {
ActivityManager am = (ActivityManager) mActivity.getSystemService(
Context.ACTIVITY_SERVICE);
Log.d(TAG, "Stopping package " + pkgName);
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
mState.invalidatePackage(pkgName, userId);
ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId);
if (newEnt != null) {
mAppEntry = newEnt;
}
updateForceStopButton();
}
6.8.handleDialogClick
6.9里的弹框点击事件在这里处理
public void handleDialogClick(int id) {
switch (id) {
case ButtonActionDialogFragment.DialogType.DISABLE:
//应用状态改为disable
AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
break;
case ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE:
//卸载app
uninstallPkg(mAppEntry.info.packageName, false, true);
break;
case ButtonActionDialogFragment.DialogType.FORCE_STOP:
forceStopPackage(mAppEntry.info.packageName);
break;
}
}
6.9.ButtonActionDialogFragment.java
//点击事件交给原本的fragment处理
public void onClick(DialogInterface dialog, int which) {
// When it's in a multi-window mode, force stopping an app will lead to an activity
// recreate, and the dialog fragment will also be recreated. So dismiss the dialog before
// stopping the app.
if (mId == ButtonActionDialogFragment.DialogType.FORCE_STOP) {
dialog.dismiss();
}
final AppButtonsDialogListener lsn =
(AppButtonsDialogListener) getTargetFragment();
lsn.handleDialogClick(mId);
}
private AlertDialog createDialog(int id) {
final Context context = getContext();
switch (id) {
case DialogType.DISABLE:
case DialogType.SPECIAL_DISABLE:
return new AlertDialog.Builder(context)
.setMessage(R.string.app_disable_dlg_text)
.setPositiveButton(R.string.app_disable_dlg_positive, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case DialogType.FORCE_STOP:
return new AlertDialog.Builder(context)
.setTitle(R.string.force_stop_dlg_title)
.setMessage(R.string.force_stop_dlg_text)
.setPositiveButton(R.string.dlg_ok, this)
.setNegativeButton(R.string.dlg_cancel, null)
.create();
}
return null;
}
7.RestrictedLockUtilsInternal.java
7.1.checkIfUninstallBlocked
就是检查应用的卸载功能是否受限,也就是不可以卸载。
public static EnforcedAdmin checkIfUninstallBlocked(Context context,
String packageName, int userId) {
EnforcedAdmin allAppsControlDisallowedAdmin = checkIfRestrictionEnforced(context,
UserManager.DISALLOW_APPS_CONTROL, userId);
//先判断用户有没有disallow apps control 限制,有的话返回
if (allAppsControlDisallowedAdmin != null) {
return allAppsControlDisallowedAdmin;
}
EnforcedAdmin allAppsUninstallDisallowedAdmin = checkIfRestrictionEnforced(context,
UserManager.DISALLOW_UNINSTALL_APPS, userId);
//再判断用户有没有disallow uninstall apps限制,有的话返回
if (allAppsUninstallDisallowedAdmin != null) {
return allAppsUninstallDisallowedAdmin;
}
IPackageManager ipm = AppGlobals.getPackageManager();
try {
//继续判断是否不允许卸载
if (ipm.getBlockUninstallForUser(packageName, userId)) {
return getProfileOrDeviceOwner(context, getUserHandleOf(userId));
}
} catch (RemoteException e) {
// Nothing to do
}
return null;
}
>DISALLOW_APPS_CONTROL
简单说,就是不允许用户在设置里或桌面修改应用,下边的几种操作不被允许:
- 卸载,禁止,清除缓存,清除数据,强制停止,清除默认值
- 另外这个值默认是false的
/**
* Specifies if a user is disallowed from modifying
* applications in Settings or launchers. The following actions will not be allowed when this
* restriction is enabled:
* <li>uninstalling apps</li>
* <li>disabling apps</li>
* <li>clearing app caches</li>
* <li>clearing app data</li>
* <li>force stopping apps</li>
* <li>clearing app defaults</li>
* <p>
* The default value is <code>false</code>.
*
* <p><strong>Note:</strong> The user will still be able to perform those actions via other
* means (such as adb). Third party apps will also be able to uninstall apps via the
* {@link android.content.pm.PackageInstaller}. {@link #DISALLOW_UNINSTALL_APPS} or
* {@link DevicePolicyManager#setUninstallBlocked(ComponentName, String, boolean)} should be
* used to prevent the user from uninstalling apps completely, and
* {@link DevicePolicyManager#addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}
* to add a default intent handler for a given intent filter.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
>DISALLOW_UNINSTALL_APPS
这个很直观,就是不允许卸载,默认值是false
/**
* Specifies if a user is disallowed from uninstalling applications.
* The default value is <code>false</code>.
*
* <p>Key for user restrictions.
* <p>Type: Boolean
* @see DevicePolicyManager#addUserRestriction(ComponentName, String)
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
7.2.hasBaseUserRestriction
public static boolean hasBaseUserRestriction(Context context,
String userRestriction, int userId) {
final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
return um.hasBaseUserRestriction(userRestriction, UserHandle.of(userId));
}
>UserManager.java
public boolean hasBaseUserRestriction(@UserRestrictionKey @NonNull String restrictionKey,
@NonNull UserHandle userHandle) {
try {
return mService.hasBaseUserRestriction(restrictionKey, userHandle.getIdentifier());
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
>UserManagerService.java
public boolean hasBaseUserRestriction(String restrictionKey, @UserIdInt int userId) {
checkCreateUsersPermission("hasBaseUserRestriction");
//这里返回的是false,别人是正经的规定的key值,我们这里传个包名过来,肯定不在集合里。
if (!UserRestrictionsUtils.isValidRestriction(restrictionKey)) {
return false;
}
synchronized (mRestrictionsLock) {
Bundle bundle = mBaseUserRestrictions.getRestrictions(userId);
return (bundle != null && bundle.getBoolean(restrictionKey, false));
}
}
8.UninstallerActivity
<activity android:name=".UninstallerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/Theme.AlertDialogActivity.NoActionBar"
android:excludeFromRecents="true"
android:noHistory="true"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="android.intent.action.DELETE" />
<action android:name="android.intent.action.UNINSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>
</activity>
这个action已建议弃用了
/**
* Activity Action: Launch application uninstaller.
* <p>
* Input: The data must be a package: URI whose scheme specific part is
* the package name of the current installed package to be uninstalled.
* You can optionally supply {@link #EXTRA_RETURN_RESULT}.
* <p>
* Output: If {@link #EXTRA_RETURN_RESULT}, returns whether the uninstall
* succeeded.
* <p>
* Requires {@link android.Manifest.permission#REQUEST_DELETE_PACKAGES}
* since {@link Build.VERSION_CODES#P}.
*
* @deprecated Use {@link android.content.pm.PackageInstaller#uninstall(String, IntentSender)}
* instead
*/
@Deprecated
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_UNINSTALL_PACKAGE = "android.intent.action.UNINSTALL_PACKAGE";
8.1.onCreate
主要就是获取数据,以及各种判定,数据异常啥的显示对应的提示或者finish,最后一切正常的话显示个对话框。
mDialogInfo = new DialogInfo();
mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
try {
mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
PackageManager.MATCH_ANY_USER, mDialogInfo.user.getIdentifier());
}
showConfirmationDialog();
}
>showConfirmationDialog
private void showConfirmationDialog() {
if (isTv()) {
} else {
showDialogFragment(new UninstallAlertDialogFragment(), 0, 0);
}
}
8.2.UninstallAlertDialogFragment.java
对话框的内容显示,有各种条件判断,这里就不看了,就看下点击事件
>onClick
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
((UninstallerActivity) getActivity()).startUninstallProgress(
mKeepData != null && mKeepData.isChecked());
} else {
((UninstallerActivity) getActivity()).dispatchAborted();
}
}
8.3.startUninstallProgress
public void startUninstallProgress(boolean keepData) {
boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
if (isTv()) {
} else if (returnResult || mDialogInfo.callback != null || getCallingActivity() != null) {
Intent newIntent = new Intent(this, UninstallUninstalling.class);
//我们会走这里
newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
newIntent.putExtra(UninstallUninstalling.EXTRA_KEEP_DATA, keepData);
//callback不为null
newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
if (returnResult) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
if (returnResult || getCallingActivity() != null) {
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
startActivity(newIntent);
} else {
int uninstallId;
try {
uninstallId = UninstallEventReceiver.getNewId(this);
} catch (EventResultPersister.OutOfIdsException e) {
showGenericError();
return;
}
Intent broadcastIntent = new Intent(this, UninstallFinish.class);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
PendingIntent pendingIntent =
PendingIntent.getBroadcast(this, uninstallId, broadcastIntent,
PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
getString(R.string.uninstalling_notification_channel),
NotificationManager.IMPORTANCE_MIN);
notificationManager.createNotificationChannel(uninstallingChannel);
Notification uninstallingNotification =
(new Notification.Builder(this, UNINSTALLING_CHANNEL))
.setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
.setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
.build();
notificationManager.notify(uninstallId, uninstallingNotification);
try {
Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
int flags = mDialogInfo.allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mDialogInfo.appInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
getPackageName(), flags, pendingIntent.getIntentSender(),
mDialogInfo.user.getIdentifier());
} catch (Exception e) {
notificationManager.cancel(uninstallId);
Log.e(TAG, "Cannot start uninstall", e);
showGenericError();
}
}
}
8.4.UninstallUninstalling.java
//显示个dialog,uninstalling xxxx
DialogFragment dialog = new UninstallUninstallingFragment();
dialog.setCancelable(false);
dialog.show(transaction, "dialog");
mUninstallId = UninstallEventReceiver.addObserver(this,
EventResultPersister.GENERATE_NEW_ID, this);
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId);
broadcastIntent.setPackage(getPackageName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT
| PendingIntent.FLAG_MUTABLE);
int flags = allUsers ? PackageManager.DELETE_ALL_USERS : 0;
flags |= keepData ? PackageManager.DELETE_KEEP_DATA : 0;
try {
//这里最终获取的是PackageInstallerService.java
ActivityThread.getPackageManager().getPackageInstaller().uninstall(
new VersionedPackage(mAppInfo.packageName,
PackageManager.VERSION_CODE_HIGHEST),
getPackageName(), flags, pendingIntent.getIntentSender(),
user.getIdentifier());
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
9.PackageInstallerService.java
9.1.uninstall
public void uninstall(VersionedPackage versionedPackage, String callerPackageName, int flags,
IntentSender statusReceiver, int userId) {
final Computer snapshot = mPm.snapshotComputer();
final int callingUid = Binder.getCallingUid();
snapshot.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall");
if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) {
mAppOps.checkPackage(callingUid, callerPackageName);
}
// Check whether the caller is device owner or affiliated profile owner, in which case we do
// it silently.
DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
final boolean canSilentlyInstallPackage =
dpmi != null && dpmi.canSilentlyInstallPackage(callerPackageName, callingUid);
final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext,
statusReceiver, versionedPackage.getPackageName(),
canSilentlyInstallPackage, userId);
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DELETE_PACKAGES)
== PackageManager.PERMISSION_GRANTED) {
//调用者有删除包的权限,那么直接开始删除
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
} else if (canSilentlyInstallPackage) {
// Allow the device owner and affiliated profile owner to silently delete packages
// Need to clear the calling identity to get DELETE_PACKAGES permission
//这里是有静默卸载的用户,一样直接卸载
final long ident = Binder.clearCallingIdentity();
try {
mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
} finally {
Binder.restoreCallingIdentity(ident);
}
} else {
ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
//api28以上的,调用者需要有下边的权限
mContext.enforceCallingOrSelfPermission(Manifest.permission.REQUEST_DELETE_PACKAGES,
null);
}
// Take a short detour to confirm with user
//这好像又回到小节8去了,多了个callback参数
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.setData(Uri.fromParts("package", versionedPackage.getPackageName(), null));
intent.putExtra(PackageInstaller.EXTRA_CALLBACK, adapter.getBinder().asBinder());
adapter.onUserActionRequired(intent);
}
}
10.总结
- apps页面的ui,数据获取简单查看
- app info页面,主要学习下3个按钮的点击事件,open, uninstall/disable,force stop