android framework13-settings[06 apps]

244 阅读10分钟

1.简介

  • 设置里的应用界面
  • 这里主要是研究一下某个app的详情页,uninstall按钮点击以后都干了啥。

2.AppDashboardFragment.java

image.png

如果没有recent apps的话,显示效果如下

image.png

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

image.png

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