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