Home | History | Annotate | Download | only in fuelgauge
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.fuelgauge;
     18 
     19 import android.app.Activity;
     20 import android.app.ActivityManager;
     21 import android.app.Fragment;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.content.pm.ApplicationInfo;
     28 import android.content.pm.PackageInfo;
     29 import android.content.pm.PackageManager;
     30 import android.content.pm.ResolveInfo;
     31 import android.content.pm.UserInfo;
     32 import android.content.res.Resources;
     33 import android.net.Uri;
     34 import android.os.AsyncTask;
     35 import android.os.Bundle;
     36 import android.os.RemoteException;
     37 import android.os.ServiceManager;
     38 import android.os.UserHandle;
     39 import android.os.UserManager;
     40 import android.support.v7.preference.PreferenceScreen;
     41 import android.util.Log;
     42 import android.view.View;
     43 import android.webkit.IWebViewUpdateService;
     44 import android.widget.Button;
     45 
     46 import com.android.internal.annotations.VisibleForTesting;
     47 import com.android.internal.logging.nano.MetricsProto;
     48 import com.android.settings.DeviceAdminAdd;
     49 import com.android.settings.R;
     50 import com.android.settings.SettingsActivity;
     51 import com.android.settings.Utils;
     52 import com.android.settings.applications.LayoutPreference;
     53 import com.android.settings.core.PreferenceController;
     54 import com.android.settings.core.instrumentation.MetricsFeatureProvider;
     55 import com.android.settings.core.lifecycle.Lifecycle;
     56 import com.android.settings.core.lifecycle.LifecycleObserver;
     57 import com.android.settings.core.lifecycle.events.OnDestroy;
     58 import com.android.settings.core.lifecycle.events.OnPause;
     59 import com.android.settings.core.lifecycle.events.OnResume;
     60 import com.android.settings.enterprise.DevicePolicyManagerWrapper;
     61 import com.android.settings.overlay.FeatureFactory;
     62 import com.android.settingslib.RestrictedLockUtils;
     63 import com.android.settingslib.applications.AppUtils;
     64 import com.android.settingslib.applications.ApplicationsState;
     65 
     66 import java.util.ArrayList;
     67 import java.util.HashSet;
     68 import java.util.List;
     69 
     70 /**
     71  * Controller to control the uninstall button and forcestop button. All fragments that use
     72  * this controller should implement {@link ButtonActionDialogFragment.AppButtonsDialogListener} and
     73  * handle {@link Fragment#onActivityResult(int, int, Intent)}
     74  *
     75  * An easy way to handle them is to delegate them to {@link #handleDialogClick(int)} and
     76  * {@link #handleActivityResult(int, int, Intent)} in this controller.
     77  */
     78 //TODO(b/35810915): Make InstalledAppDetails use this controller
     79 public class AppButtonsPreferenceController extends PreferenceController implements
     80         LifecycleObserver, OnResume, OnPause, OnDestroy, View.OnClickListener,
     81         ApplicationsState.Callbacks {
     82     public static final String APP_CHG = "chg";
     83 
     84     private static final String TAG = "AppButtonsPrefCtl";
     85     private static final String KEY_ACTION_BUTTONS = "action_buttons";
     86     private static final boolean LOCAL_LOGV = false;
     87 
     88     @VisibleForTesting
     89     final HashSet<String> mHomePackages = new HashSet<>();
     90     @VisibleForTesting
     91     ApplicationsState mState;
     92     @VisibleForTesting
     93     ApplicationsState.AppEntry mAppEntry;
     94     @VisibleForTesting
     95     PackageInfo mPackageInfo;
     96     @VisibleForTesting
     97     Button mForceStopButton;
     98     @VisibleForTesting
     99     Button mUninstallButton;
    100     @VisibleForTesting
    101     String mPackageName;
    102     @VisibleForTesting
    103     boolean mDisableAfterUninstall = false;
    104 
    105     private final int mRequestUninstall;
    106     private final int mRequestRemoveDeviceAdmin;
    107 
    108     private ApplicationsState.Session mSession;
    109     private DevicePolicyManagerWrapper mDpm;
    110     private UserManager mUserManager;
    111     private PackageManager mPm;
    112     private SettingsActivity mActivity;
    113     private Fragment mFragment;
    114     private RestrictedLockUtils.EnforcedAdmin mAppsControlDisallowedAdmin;
    115     private MetricsFeatureProvider mMetricsFeatureProvider;
    116 
    117     private LayoutPreference mButtonsPref;
    118     private int mUserId;
    119     private boolean mUpdatedSysApp = false;
    120     private boolean mListeningToPackageRemove = false;
    121     private boolean mFinishing = false;
    122     private boolean mAppsControlDisallowedBySystem;
    123 
    124     public AppButtonsPreferenceController(SettingsActivity activity, Fragment fragment,
    125             Lifecycle lifecycle, String packageName, ApplicationsState state,
    126             DevicePolicyManagerWrapper dpm, UserManager userManager,
    127             PackageManager packageManager, int requestUninstall, int requestRemoveDeviceAdmin) {
    128         super(activity);
    129 
    130         if (!(fragment instanceof ButtonActionDialogFragment.AppButtonsDialogListener)) {
    131             throw new IllegalArgumentException(
    132                     "Fragment should implement AppButtonsDialogListener");
    133         }
    134 
    135         mMetricsFeatureProvider = FeatureFactory.getFactory(activity).getMetricsFeatureProvider();
    136 
    137         mState = state;
    138         mDpm = dpm;
    139         mUserManager = userManager;
    140         mPm = packageManager;
    141         mPackageName = packageName;
    142         mActivity = activity;
    143         mFragment = fragment;
    144         mUserId = UserHandle.myUserId();
    145         mRequestUninstall = requestUninstall;
    146         mRequestRemoveDeviceAdmin = requestRemoveDeviceAdmin;
    147 
    148         if (packageName != null) {
    149             mAppEntry = mState.getEntry(packageName, mUserId);
    150             mSession = mState.newSession(this);
    151             lifecycle.addObserver(this);
    152         } else {
    153             mFinishing = true;
    154         }
    155     }
    156 
    157     @Override
    158     public boolean isAvailable() {
    159         // TODO(b/37313605): Re-enable once this controller supports instant apps
    160         return mAppEntry != null && !AppUtils.isInstant(mAppEntry.info);
    161     }
    162 
    163     @Override
    164     public void displayPreference(PreferenceScreen screen) {
    165         super.displayPreference(screen);
    166         if (isAvailable()) {
    167             mButtonsPref = (LayoutPreference) screen.findPreference(KEY_ACTION_BUTTONS);
    168 
    169             mUninstallButton = (Button) mButtonsPref.findViewById(R.id.left_button);
    170             mUninstallButton.setText(R.string.uninstall_text);
    171 
    172             mForceStopButton = (Button) mButtonsPref.findViewById(R.id.right_button);
    173             mForceStopButton.setText(R.string.force_stop);
    174             mForceStopButton.setEnabled(false);
    175         }
    176     }
    177 
    178     @Override
    179     public String getPreferenceKey() {
    180         return KEY_ACTION_BUTTONS;
    181     }
    182 
    183     @Override
    184     public void onResume() {
    185         mSession.resume();
    186         if (isAvailable() && !mFinishing) {
    187             mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(mActivity,
    188                     UserManager.DISALLOW_APPS_CONTROL, mUserId);
    189             mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(mActivity,
    190                     UserManager.DISALLOW_APPS_CONTROL, mUserId);
    191 
    192             if (!refreshUi()) {
    193                 setIntentAndFinish(true);
    194             }
    195         }
    196     }
    197 
    198     @Override
    199     public void onPause() {
    200         mSession.pause();
    201     }
    202 
    203     @Override
    204     public void onDestroy() {
    205         stopListeningToPackageRemove();
    206         mSession.release();
    207     }
    208 
    209     @Override
    210     public void onClick(View v) {
    211         final String packageName = mAppEntry.info.packageName;
    212         final int id = v.getId();
    213         if (id == R.id.left_button) {
    214             // Uninstall
    215             if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    216                 stopListeningToPackageRemove();
    217                 Intent uninstallDaIntent = new Intent(mActivity, DeviceAdminAdd.class);
    218                 uninstallDaIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
    219                         packageName);
    220                 mMetricsFeatureProvider.action(mActivity,
    221                         MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
    222                 mFragment.startActivityForResult(uninstallDaIntent, mRequestRemoveDeviceAdmin);
    223                 return;
    224             }
    225             RestrictedLockUtils.EnforcedAdmin admin =
    226                     RestrictedLockUtils.checkIfUninstallBlocked(mActivity,
    227                             packageName, mUserId);
    228             boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
    229                     RestrictedLockUtils.hasBaseUserRestriction(mActivity, packageName, mUserId);
    230             if (admin != null && !uninstallBlockedBySystem) {
    231                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mActivity, admin);
    232             } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    233                 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
    234                     // If the system app has an update and this is the only user on the device,
    235                     // then offer to downgrade the app, otherwise only offer to disable the
    236                     // app for this user.
    237                     if (mUpdatedSysApp && isSingleUser()) {
    238                         showDialogInner(ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE);
    239                     } else {
    240                         showDialogInner(ButtonActionDialogFragment.DialogType.DISABLE);
    241                     }
    242                 } else {
    243                     mMetricsFeatureProvider.action(
    244                             mActivity,
    245                             mAppEntry.info.enabled
    246                                     ? MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP
    247                                     : MetricsProto.MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
    248                     AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
    249                             PackageManager.COMPONENT_ENABLED_STATE_DEFAULT));
    250                 }
    251             } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
    252                 uninstallPkg(packageName, true, false);
    253             } else {
    254                 uninstallPkg(packageName, false, false);
    255             }
    256         } else if (id == R.id.right_button) {
    257             // force stop
    258             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    259                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    260                         mActivity, mAppsControlDisallowedAdmin);
    261             } else {
    262                 showDialogInner(ButtonActionDialogFragment.DialogType.FORCE_STOP);
    263             }
    264         }
    265     }
    266 
    267     public void handleActivityResult(int requestCode, int resultCode, Intent data) {
    268         if (requestCode == mRequestUninstall) {
    269             if (mDisableAfterUninstall) {
    270                 mDisableAfterUninstall = false;
    271                 AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
    272                         PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
    273             }
    274             refreshAndFinishIfPossible();
    275         } else if (requestCode == mRequestRemoveDeviceAdmin) {
    276             refreshAndFinishIfPossible();
    277         }
    278     }
    279 
    280     public void handleDialogClick(int id) {
    281         switch (id) {
    282             case ButtonActionDialogFragment.DialogType.DISABLE:
    283                 mMetricsFeatureProvider.action(mActivity,
    284                         MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
    285                 AsyncTask.execute(new DisableChangerRunnable(mPm, mAppEntry.info.packageName,
    286                         PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER));
    287                 break;
    288             case ButtonActionDialogFragment.DialogType.SPECIAL_DISABLE:
    289                 mMetricsFeatureProvider.action(mActivity,
    290                         MetricsProto.MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
    291                 uninstallPkg(mAppEntry.info.packageName, false, true);
    292                 break;
    293             case ButtonActionDialogFragment.DialogType.FORCE_STOP:
    294                 forceStopPackage(mAppEntry.info.packageName);
    295                 break;
    296         }
    297     }
    298 
    299     @Override
    300     public void onRunningStateChanged(boolean running) {
    301 
    302     }
    303 
    304     @Override
    305     public void onPackageListChanged() {
    306         refreshUi();
    307     }
    308 
    309     @Override
    310     public void onRebuildComplete(ArrayList<ApplicationsState.AppEntry> apps) {
    311 
    312     }
    313 
    314     @Override
    315     public void onPackageIconChanged() {
    316 
    317     }
    318 
    319     @Override
    320     public void onPackageSizeChanged(String packageName) {
    321 
    322     }
    323 
    324     @Override
    325     public void onAllSizesComputed() {
    326 
    327     }
    328 
    329     @Override
    330     public void onLauncherInfoChanged() {
    331 
    332     }
    333 
    334     @Override
    335     public void onLoadEntriesCompleted() {
    336 
    337     }
    338 
    339     @VisibleForTesting
    340     void retrieveAppEntry() {
    341         mAppEntry = mState.getEntry(mPackageName, mUserId);
    342         if (mAppEntry != null) {
    343             try {
    344                 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
    345                         PackageManager.MATCH_DISABLED_COMPONENTS |
    346                                 PackageManager.MATCH_ANY_USER |
    347                                 PackageManager.GET_SIGNATURES |
    348                                 PackageManager.GET_PERMISSIONS);
    349 
    350                 mPackageName = mAppEntry.info.packageName;
    351             } catch (PackageManager.NameNotFoundException e) {
    352                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
    353                 mPackageInfo = null;
    354             }
    355         } else {
    356             mPackageInfo = null;
    357         }
    358     }
    359 
    360     @VisibleForTesting
    361     void updateUninstallButton() {
    362         final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    363         boolean enabled = true;
    364         if (isBundled) {
    365             enabled = handleDisableable(mUninstallButton);
    366         } else {
    367             if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
    368                     && mUserManager.getUsers().size() >= 2) {
    369                 // When we have multiple users, there is a separate menu
    370                 // to uninstall for all users.
    371                 enabled = false;
    372             }
    373         }
    374         // If this is a device admin, it can't be uninstalled or disabled.
    375         // We do this here so the text of the button is still set correctly.
    376         if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    377             enabled = false;
    378         }
    379 
    380         // We don't allow uninstalling DO/PO on *any* users, because if it's a system app,
    381         // "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
    382         // will clear data on all users.
    383         if (isProfileOrDeviceOwner(mPackageInfo.packageName)) {
    384             enabled = false;
    385         }
    386 
    387         // Don't allow uninstalling the device provisioning package.
    388         if (Utils.isDeviceProvisioningPackage(mContext.getResources(),
    389                 mAppEntry.info.packageName)) {
    390             enabled = false;
    391         }
    392 
    393         // If the uninstall intent is already queued, disable the uninstall button
    394         if (mDpm.isUninstallInQueue(mPackageName)) {
    395             enabled = false;
    396         }
    397 
    398         // Home apps need special handling.  Bundled ones we don't risk downgrading
    399         // because that can interfere with home-key resolution.  Furthermore, we
    400         // can't allow uninstallation of the only home app, and we don't want to
    401         // allow uninstallation of an explicitly preferred one -- the user can go
    402         // to Home settings and pick a different one, after which we'll permit
    403         // uninstallation of the now-not-default one.
    404         if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
    405             if (isBundled) {
    406                 enabled = false;
    407             } else {
    408                 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
    409                 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);
    410                 if (currentDefaultHome == null) {
    411                     // No preferred default, so permit uninstall only when
    412                     // there is more than one candidate
    413                     enabled = (mHomePackages.size() > 1);
    414                 } else {
    415                     // There is an explicit default home app -- forbid uninstall of
    416                     // that one, but permit it for installed-but-inactive ones.
    417                     enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
    418                 }
    419             }
    420         }
    421 
    422         if (mAppsControlDisallowedBySystem) {
    423             enabled = false;
    424         }
    425 
    426         if (isFallbackPackage(mAppEntry.info.packageName)) {
    427             enabled = false;
    428         }
    429 
    430         mUninstallButton.setEnabled(enabled);
    431         if (enabled) {
    432             // Register listener
    433             mUninstallButton.setOnClickListener(this);
    434         }
    435     }
    436 
    437     /**
    438      * Finish this fragment and return data if possible
    439      */
    440     private void setIntentAndFinish(boolean appChanged) {
    441         if (LOCAL_LOGV) {
    442             Log.i(TAG, "appChanged=" + appChanged);
    443         }
    444         Intent intent = new Intent();
    445         intent.putExtra(APP_CHG, appChanged);
    446         mActivity.finishPreferencePanel(mFragment, Activity.RESULT_OK, intent);
    447         mFinishing = true;
    448     }
    449 
    450     private void refreshAndFinishIfPossible() {
    451         if (!refreshUi()) {
    452             setIntentAndFinish(true);
    453         } else {
    454             startListeningToPackageRemove();
    455         }
    456     }
    457 
    458     @VisibleForTesting
    459     boolean isFallbackPackage(String packageName) {
    460         try {
    461             IWebViewUpdateService webviewUpdateService =
    462                     IWebViewUpdateService.Stub.asInterface(
    463                             ServiceManager.getService("webviewupdate"));
    464             if (webviewUpdateService.isFallbackPackage(packageName)) {
    465                 return true;
    466             }
    467         } catch (RemoteException e) {
    468             throw new RuntimeException(e);
    469         }
    470 
    471         return false;
    472     }
    473 
    474     @VisibleForTesting
    475     void updateForceStopButton() {
    476         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    477             // User can't force stop device admin.
    478             Log.w(TAG, "User can't force stop device admin");
    479             updateForceStopButtonInner(false);
    480         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
    481             // If the app isn't explicitly stopped, then always show the
    482             // force stop button.
    483             Log.w(TAG, "App is not explicitly stopped");
    484             updateForceStopButtonInner(true);
    485         } else {
    486             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
    487                     Uri.fromParts("package", mAppEntry.info.packageName, null));
    488             intent.putExtra(Intent.EXTRA_PACKAGES, new String[]{mAppEntry.info.packageName});
    489             intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
    490             intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
    491             Log.d(TAG, "Sending broadcast to query restart status for "
    492                     + mAppEntry.info.packageName);
    493             mActivity.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
    494                     mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
    495         }
    496     }
    497 
    498     @VisibleForTesting
    499     void updateForceStopButtonInner(boolean enabled) {
    500         if (mAppsControlDisallowedBySystem) {
    501             mForceStopButton.setEnabled(false);
    502         } else {
    503             mForceStopButton.setEnabled(enabled);
    504             mForceStopButton.setOnClickListener(this);
    505         }
    506     }
    507 
    508     @VisibleForTesting
    509     void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
    510         stopListeningToPackageRemove();
    511         // Create new intent to launch Uninstaller activity
    512         Uri packageUri = Uri.parse("package:" + packageName);
    513         Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageUri);
    514         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
    515 
    516         mMetricsFeatureProvider.action(
    517                 mActivity, MetricsProto.MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
    518         mFragment.startActivityForResult(uninstallIntent, mRequestUninstall);
    519         mDisableAfterUninstall = andDisable;
    520     }
    521 
    522     @VisibleForTesting
    523     void forceStopPackage(String pkgName) {
    524         FeatureFactory.getFactory(mContext).getMetricsFeatureProvider().action(mContext,
    525                 MetricsProto.MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
    526         ActivityManager am = (ActivityManager) mActivity.getSystemService(
    527                 Context.ACTIVITY_SERVICE);
    528         Log.d(TAG, "Stopping package " + pkgName);
    529         am.forceStopPackage(pkgName);
    530         int userId = UserHandle.getUserId(mAppEntry.info.uid);
    531         mState.invalidatePackage(pkgName, userId);
    532         ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId);
    533         if (newEnt != null) {
    534             mAppEntry = newEnt;
    535         }
    536         updateForceStopButton();
    537     }
    538 
    539     @VisibleForTesting
    540     boolean handleDisableable(Button button) {
    541         boolean disableable = false;
    542         // Try to prevent the user from bricking their phone
    543         // by not allowing disabling of apps signed with the
    544         // system cert and any launcher app in the system.
    545         if (mHomePackages.contains(mAppEntry.info.packageName)
    546                 || isSystemPackage(mActivity.getResources(), mPm, mPackageInfo)) {
    547             // Disable button for core system applications.
    548             button.setText(R.string.disable_text);
    549         } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
    550             button.setText(R.string.disable_text);
    551             disableable = true;
    552         } else {
    553             button.setText(R.string.enable_text);
    554             disableable = true;
    555         }
    556 
    557         return disableable;
    558     }
    559 
    560     @VisibleForTesting
    561     boolean isSystemPackage(Resources resources, PackageManager pm, PackageInfo packageInfo) {
    562         return Utils.isSystemPackage(resources, pm, packageInfo);
    563     }
    564 
    565     private boolean isDisabledUntilUsed() {
    566         return mAppEntry.info.enabledSetting
    567                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
    568     }
    569 
    570     private void showDialogInner(@ButtonActionDialogFragment.DialogType int id) {
    571         ButtonActionDialogFragment newFragment = ButtonActionDialogFragment.newInstance(id);
    572         newFragment.setTargetFragment(mFragment, 0);
    573         newFragment.show(mActivity.getFragmentManager(), "dialog " + id);
    574     }
    575 
    576     /** Returns whether there is only one user on this device, not including the system-only user */
    577     private boolean isSingleUser() {
    578         final int userCount = mUserManager.getUserCount();
    579         return userCount == 1
    580                 || (mUserManager.isSplitSystemUser() && userCount == 2);
    581     }
    582 
    583     /** Returns if the supplied package is device owner or profile owner of at least one user */
    584     private boolean isProfileOrDeviceOwner(String packageName) {
    585         List<UserInfo> userInfos = mUserManager.getUsers();
    586         if (mDpm.isDeviceOwnerAppOnAnyUser(packageName)) {
    587             return true;
    588         }
    589         for (int i = 0, size = userInfos.size(); i < size; i++) {
    590             ComponentName cn = mDpm.getProfileOwnerAsUser(userInfos.get(i).id);
    591             if (cn != null && cn.getPackageName().equals(packageName)) {
    592                 return true;
    593             }
    594         }
    595         return false;
    596     }
    597 
    598     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
    599         @Override
    600         public void onReceive(Context context, Intent intent) {
    601             final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
    602             Log.d(TAG, "Got broadcast response: Restart status for "
    603                     + mAppEntry.info.packageName + " " + enabled);
    604             updateForceStopButtonInner(enabled);
    605         }
    606     };
    607 
    608     private boolean signaturesMatch(String pkg1, String pkg2) {
    609         if (pkg1 != null && pkg2 != null) {
    610             try {
    611                 final int match = mPm.checkSignatures(pkg1, pkg2);
    612                 if (match >= PackageManager.SIGNATURE_MATCH) {
    613                     return true;
    614                 }
    615             } catch (Exception e) {
    616                 // e.g. named alternate package not found during lookup;
    617                 // this is an expected case sometimes
    618             }
    619         }
    620         return false;
    621     }
    622 
    623     @VisibleForTesting
    624     boolean refreshUi() {
    625         if (mPackageName == null) {
    626             return false;
    627         }
    628         retrieveAppEntry();
    629         if (mAppEntry == null || mPackageInfo == null) {
    630             return false;
    631         }
    632         // Get list of "home" apps and trace through any meta-data references
    633         List<ResolveInfo> homeActivities = new ArrayList<>();
    634         mPm.getHomeActivities(homeActivities);
    635         mHomePackages.clear();
    636         for (int i = 0, size = homeActivities.size(); i < size; i++) {
    637             ResolveInfo ri = homeActivities.get(i);
    638             final String activityPkg = ri.activityInfo.packageName;
    639             mHomePackages.add(activityPkg);
    640 
    641             // Also make sure to include anything proxying for the home app
    642             final Bundle metadata = ri.activityInfo.metaData;
    643             if (metadata != null) {
    644                 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
    645                 if (signaturesMatch(metaPkg, activityPkg)) {
    646                     mHomePackages.add(metaPkg);
    647                 }
    648             }
    649         }
    650 
    651         updateUninstallButton();
    652         updateForceStopButton();
    653 
    654         return true;
    655     }
    656 
    657     private void startListeningToPackageRemove() {
    658         if (mListeningToPackageRemove) {
    659             return;
    660         }
    661         mListeningToPackageRemove = true;
    662         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
    663         filter.addDataScheme("package");
    664         mActivity.registerReceiver(mPackageRemovedReceiver, filter);
    665     }
    666 
    667     private void stopListeningToPackageRemove() {
    668         if (!mListeningToPackageRemove) {
    669             return;
    670         }
    671         mListeningToPackageRemove = false;
    672         mActivity.unregisterReceiver(mPackageRemovedReceiver);
    673     }
    674 
    675 
    676     /**
    677      * Changes the status of disable/enable for a package
    678      */
    679     private class DisableChangerRunnable implements Runnable {
    680         final PackageManager mPm;
    681         final String mPackageName;
    682         final int mState;
    683 
    684         public DisableChangerRunnable(PackageManager pm, String packageName, int state) {
    685             mPm = pm;
    686             mPackageName = packageName;
    687             mState = state;
    688         }
    689 
    690         @Override
    691         public void run() {
    692             mPm.setApplicationEnabledSetting(mPackageName, mState, 0);
    693         }
    694     }
    695 
    696     /**
    697      * Receiver to listen to the remove action for packages
    698      */
    699     private final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
    700         @Override
    701         public void onReceive(Context context, Intent intent) {
    702             String packageName = intent.getData().getSchemeSpecificPart();
    703             if (!mFinishing && mAppEntry.info.packageName.equals(packageName)) {
    704                 mActivity.finishAndRemoveTask();
    705             }
    706         }
    707     };
    708 
    709 }
    710