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