• 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"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.settings.applications.appinfo;
18 
19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
20 
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.AlertDialog;
24 import android.app.Dialog;
25 import android.app.DialogFragment;
26 import android.app.admin.DevicePolicyManager;
27 import android.content.BroadcastReceiver;
28 import android.content.Context;
29 import android.content.DialogInterface;
30 import android.content.Intent;
31 import android.content.IntentFilter;
32 import android.content.pm.ApplicationInfo;
33 import android.content.pm.PackageInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.PackageManager.NameNotFoundException;
36 import android.content.pm.UserInfo;
37 import android.net.Uri;
38 import android.os.AsyncTask;
39 import android.os.Bundle;
40 import android.os.UserHandle;
41 import android.os.UserManager;
42 import android.support.annotation.VisibleForTesting;
43 import android.text.TextUtils;
44 import android.util.Log;
45 import android.view.Menu;
46 import android.view.MenuInflater;
47 import android.view.MenuItem;
48 
49 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
50 import com.android.settings.DeviceAdminAdd;
51 import com.android.settings.R;
52 import com.android.settings.SettingsActivity;
53 import com.android.settings.SettingsPreferenceFragment;
54 import com.android.settings.applications.manageapplications.ManageApplications;
55 import com.android.settings.core.SubSettingLauncher;
56 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
57 import com.android.settings.dashboard.DashboardFragment;
58 import com.android.settingslib.RestrictedLockUtils;
59 import com.android.settingslib.applications.AppUtils;
60 import com.android.settingslib.applications.ApplicationsState;
61 import com.android.settingslib.applications.ApplicationsState.AppEntry;
62 import com.android.settingslib.core.AbstractPreferenceController;
63 import com.android.settingslib.core.lifecycle.Lifecycle;
64 
65 import java.lang.ref.WeakReference;
66 import java.util.ArrayList;
67 import java.util.Arrays;
68 import java.util.List;
69 
70 /**
71  * Dashboard fragment to display application information from Settings. This activity presents
72  * extended information associated with a package like code, data, total size, permissions
73  * used by the application and also the set of default launchable activities.
74  * For system applications, an option to clear user data is displayed only if data size is > 0.
75  * System applications that do not want clear user data do not have this option.
76  * For non-system applications, there is no option to clear data. Instead there is an option to
77  * uninstall the application.
78  */
79 public class AppInfoDashboardFragment extends DashboardFragment
80         implements ApplicationsState.Callbacks {
81 
82     private static final String TAG = "AppInfoDashboard";
83 
84     // Menu identifiers
85     @VisibleForTesting
86     static final int UNINSTALL_ALL_USERS_MENU = 1;
87     @VisibleForTesting
88     static final int UNINSTALL_UPDATES = 2;
89     static final int INSTALL_INSTANT_APP_MENU = 3;
90 
91     // Result code identifiers
92     @VisibleForTesting
93     static final int REQUEST_UNINSTALL = 0;
94     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
95 
96     static final int SUB_INFO_FRAGMENT = 1;
97 
98     static final int LOADER_CHART_DATA = 2;
99     static final int LOADER_STORAGE = 3;
100     static final int LOADER_BATTERY = 4;
101 
102     // Dialog identifiers used in showDialog
103     private static final int DLG_BASE = 0;
104     private static final int DLG_FORCE_STOP = DLG_BASE + 1;
105     private static final int DLG_DISABLE = DLG_BASE + 2;
106     private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
107     static final int DLG_CLEAR_INSTANT_APP = DLG_BASE + 4;
108 
109     public static final String ARG_PACKAGE_NAME = "package";
110     public static final String ARG_PACKAGE_UID = "uid";
111 
112     private static final boolean localLOGV = false;
113 
114     private EnforcedAdmin mAppsControlDisallowedAdmin;
115     private boolean mAppsControlDisallowedBySystem;
116 
117     private ApplicationsState mState;
118     private ApplicationsState.Session mSession;
119     private ApplicationsState.AppEntry mAppEntry;
120     private PackageInfo mPackageInfo;
121     private int mUserId;
122     private String mPackageName;
123 
124     private DevicePolicyManager mDpm;
125     private UserManager mUserManager;
126     private PackageManager mPm;
127 
128     private boolean mFinishing;
129     private boolean mListeningToPackageRemove;
130 
131 
132     private boolean mInitialized;
133     private boolean mShowUninstalled;
134     private boolean mUpdatedSysApp = false;
135     private boolean mDisableAfterUninstall;
136 
137     private List<Callback> mCallbacks = new ArrayList<>();
138 
139     private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController;
140     private AppActionButtonPreferenceController mAppActionButtonPreferenceController;
141 
142     /**
143      * Callback to invoke when app info has been changed.
144      */
145     public interface Callback {
refreshUi()146         void refreshUi();
147     }
148 
isDisabledUntilUsed()149     private boolean isDisabledUntilUsed() {
150         return mAppEntry.info.enabledSetting
151                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
152     }
153 
154     @Override
onAttach(Context context)155     public void onAttach(Context context) {
156         super.onAttach(context);
157         final String packageName = getPackageName();
158         use(TimeSpentInAppPreferenceController.class).setPackageName(packageName);
159 
160         use(AppDataUsagePreferenceController.class).setParentFragment(this);
161         final AppInstallerInfoPreferenceController installer =
162                 use(AppInstallerInfoPreferenceController.class);
163         installer.setPackageName(packageName);
164         installer.setParentFragment(this);
165         use(AppInstallerPreferenceCategoryController.class).setChildren(Arrays.asList(installer));
166         use(AppNotificationPreferenceController.class).setParentFragment(this);
167         use(AppOpenByDefaultPreferenceController.class).setParentFragment(this);
168         use(AppPermissionPreferenceController.class).setParentFragment(this);
169         use(AppPermissionPreferenceController.class).setPackageName(packageName);
170         use(AppSettingPreferenceController.class)
171                 .setPackageName(packageName)
172                 .setParentFragment(this);
173         use(AppStoragePreferenceController.class).setParentFragment(this);
174         use(AppVersionPreferenceController.class).setParentFragment(this);
175         use(InstantAppDomainsPreferenceController.class).setParentFragment(this);
176 
177         final WriteSystemSettingsPreferenceController writeSystemSettings =
178                 use(WriteSystemSettingsPreferenceController.class);
179         writeSystemSettings.setParentFragment(this);
180 
181         final DrawOverlayDetailPreferenceController drawOverlay =
182                 use(DrawOverlayDetailPreferenceController.class);
183         drawOverlay.setParentFragment(this);
184 
185         final PictureInPictureDetailPreferenceController pip =
186                 use(PictureInPictureDetailPreferenceController.class);
187         pip.setPackageName(packageName);
188         pip.setParentFragment(this);
189         final ExternalSourceDetailPreferenceController externalSource =
190                 use(ExternalSourceDetailPreferenceController.class);
191         externalSource.setPackageName(packageName);
192         externalSource.setParentFragment(this);
193 
194         use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList(
195                 writeSystemSettings, drawOverlay, pip, externalSource));
196     }
197 
198     @Override
onCreate(Bundle icicle)199     public void onCreate(Bundle icicle) {
200         super.onCreate(icicle);
201         mFinishing = false;
202         final Activity activity = getActivity();
203         mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
204         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
205         mPm = activity.getPackageManager();
206 
207         if (!ensurePackageInfoAvailable(activity)) {
208             return;
209         }
210 
211         startListeningToPackageRemove();
212 
213         setHasOptionsMenu(true);
214     }
215 
216     @Override
onDestroy()217     public void onDestroy() {
218         stopListeningToPackageRemove();
219         super.onDestroy();
220     }
221 
222     @Override
getMetricsCategory()223     public int getMetricsCategory() {
224         return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS;
225     }
226 
227     @Override
onResume()228     public void onResume() {
229         super.onResume();
230         final Activity activity = getActivity();
231         mAppsControlDisallowedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(activity,
232                 UserManager.DISALLOW_APPS_CONTROL, mUserId);
233         mAppsControlDisallowedBySystem = RestrictedLockUtils.hasBaseUserRestriction(activity,
234                 UserManager.DISALLOW_APPS_CONTROL, mUserId);
235 
236         if (!refreshUi()) {
237             setIntentAndFinish(true, true);
238         }
239     }
240 
241     @Override
getPreferenceScreenResId()242     protected int getPreferenceScreenResId() {
243         return R.xml.app_info_settings;
244     }
245 
246     @Override
getLogTag()247     protected String getLogTag() {
248         return TAG;
249     }
250 
251     @Override
createPreferenceControllers(Context context)252     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
253         retrieveAppEntry();
254         if (mPackageInfo == null) {
255             return null;
256         }
257         final String packageName = getPackageName();
258         final List<AbstractPreferenceController> controllers = new ArrayList<>();
259         final Lifecycle lifecycle = getLifecycle();
260 
261         // The following are controllers for preferences that needs to refresh the preference state
262         // when app state changes.
263         controllers.add(
264                 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle));
265         mAppActionButtonPreferenceController =
266                 new AppActionButtonPreferenceController(context, this, packageName);
267         controllers.add(mAppActionButtonPreferenceController);
268 
269         for (AbstractPreferenceController controller : controllers) {
270             mCallbacks.add((Callback) controller);
271         }
272 
273         // The following are controllers for preferences that don't need to refresh the preference
274         // state when app state changes.
275         mInstantAppButtonPreferenceController =
276                 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle);
277         controllers.add(mInstantAppButtonPreferenceController);
278         controllers.add(new AppBatteryPreferenceController(context, this, packageName, lifecycle));
279         controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
280         controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
281         controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
282         controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
283         controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
284         controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
285 
286         return controllers;
287     }
288 
addToCallbackList(Callback callback)289     void addToCallbackList(Callback callback) {
290         if (callback != null) {
291             mCallbacks.add(callback);
292         }
293     }
294 
getAppEntry()295     ApplicationsState.AppEntry getAppEntry() {
296         return mAppEntry;
297     }
298 
setAppEntry(ApplicationsState.AppEntry appEntry)299     void setAppEntry(ApplicationsState.AppEntry appEntry) {
300         mAppEntry = appEntry;
301     }
302 
getPackageInfo()303     PackageInfo getPackageInfo() {
304         return mPackageInfo;
305     }
306 
307     @Override
onPackageSizeChanged(String packageName)308     public void onPackageSizeChanged(String packageName) {
309         if (!TextUtils.equals(packageName, mPackageName)) {
310             Log.d(TAG, "Package change irrelevant, skipping");
311             return;
312         }
313         refreshUi();
314     }
315 
316     /**
317      * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment
318      * will finish.
319      *
320      * @return true if packageInfo is available.
321      */
322     @VisibleForTesting
ensurePackageInfoAvailable(Activity activity)323     boolean ensurePackageInfoAvailable(Activity activity) {
324         if (mPackageInfo == null) {
325             mFinishing = true;
326             Log.w(TAG, "Package info not available. Is this package already uninstalled?");
327             activity.finishAndRemoveTask();
328             return false;
329         }
330         return true;
331     }
332 
333     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)334     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
335         super.onCreateOptionsMenu(menu, inflater);
336         menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
337                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
338         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
339                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
340     }
341 
342     @Override
onPrepareOptionsMenu(Menu menu)343     public void onPrepareOptionsMenu(Menu menu) {
344         if (mFinishing) {
345             return;
346         }
347         super.onPrepareOptionsMenu(menu);
348         menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry));
349         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
350         final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
351         final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean(
352                 R.bool.config_disable_uninstall_update);
353         uninstallUpdatesItem.setVisible(
354                 mUpdatedSysApp && !mAppsControlDisallowedBySystem && !uninstallUpdateDisabled);
355         if (uninstallUpdatesItem.isVisible()) {
356             RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(),
357                     uninstallUpdatesItem, mAppsControlDisallowedAdmin);
358         }
359     }
360 
361     @Override
onOptionsItemSelected(MenuItem item)362     public boolean onOptionsItemSelected(MenuItem item) {
363         switch (item.getItemId()) {
364             case UNINSTALL_ALL_USERS_MENU:
365                 uninstallPkg(mAppEntry.info.packageName, true, false);
366                 return true;
367             case UNINSTALL_UPDATES:
368                 uninstallPkg(mAppEntry.info.packageName, false, false);
369                 return true;
370         }
371         return super.onOptionsItemSelected(item);
372     }
373 
374     @Override
onActivityResult(int requestCode, int resultCode, Intent data)375     public void onActivityResult(int requestCode, int resultCode, Intent data) {
376         super.onActivityResult(requestCode, resultCode, data);
377         switch (requestCode) {
378             case REQUEST_UNINSTALL:
379                 // Refresh option menu
380                 getActivity().invalidateOptionsMenu();
381 
382                 if (mDisableAfterUninstall) {
383                     mDisableAfterUninstall = false;
384                     new DisableChanger(this, mAppEntry.info,
385                             PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
386                             .execute((Object) null);
387                 }
388                 if (!refreshUi()) {
389                     onPackageRemoved();
390                 } else {
391                     startListeningToPackageRemove();
392                 }
393                 break;
394             case REQUEST_REMOVE_DEVICE_ADMIN:
395                 if (!refreshUi()) {
396                     setIntentAndFinish(true, true);
397                 } else {
398                     startListeningToPackageRemove();
399                 }
400                 break;
401         }
402     }
403 
404     @VisibleForTesting
shouldShowUninstallForAll(AppEntry appEntry)405     boolean shouldShowUninstallForAll(AppEntry appEntry) {
406         boolean showIt = true;
407         if (mUpdatedSysApp) {
408             showIt = false;
409         } else if (appEntry == null) {
410             showIt = false;
411         } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
412             showIt = false;
413         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
414             showIt = false;
415         } else if (UserHandle.myUserId() != 0) {
416             showIt = false;
417         } else if (mUserManager.getUsers().size() < 2) {
418             showIt = false;
419         } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2
420                 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
421             showIt = false;
422         } else if (AppUtils.isInstant(appEntry.info)) {
423             showIt = false;
424         }
425         return showIt;
426     }
427 
428     @VisibleForTesting
refreshUi()429     boolean refreshUi() {
430         retrieveAppEntry();
431         if (mAppEntry == null) {
432             return false; // onCreate must have failed, make sure to exit
433         }
434 
435         if (mPackageInfo == null) {
436             return false; // onCreate must have failed, make sure to exit
437         }
438 
439         mState.ensureIcon(mAppEntry);
440 
441         // Update the preference summaries.
442         for (Callback callback : mCallbacks) {
443             callback.refreshUi();
444         }
445 
446         if (!mInitialized) {
447             // First time init: are we displaying an uninstalled app?
448             mInitialized = true;
449             mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0;
450         } else {
451             // All other times: if the app no longer exists then we want
452             // to go away.
453             try {
454                 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
455                         mAppEntry.info.packageName,
456                         PackageManager.MATCH_DISABLED_COMPONENTS
457                                 | PackageManager.MATCH_ANY_USER);
458                 if (!mShowUninstalled) {
459                     // If we did not start out with the app uninstalled, then
460                     // it transitioning to the uninstalled state for the current
461                     // user means we should go away as well.
462                     return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
463                 }
464             } catch (NameNotFoundException e) {
465                 return false;
466             }
467         }
468 
469         return true;
470     }
471 
472     @VisibleForTesting
createDialog(int id, int errorCode)473     AlertDialog createDialog(int id, int errorCode) {
474         switch (id) {
475             case DLG_DISABLE:
476                 return new AlertDialog.Builder(getActivity())
477                         .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
478                         .setPositiveButton(R.string.app_disable_dlg_positive,
479                                 new DialogInterface.OnClickListener() {
480                                     public void onClick(DialogInterface dialog, int which) {
481                                         // Disable the app
482                                         mMetricsFeatureProvider.action(getContext(),
483                                                 MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
484                                         new DisableChanger(AppInfoDashboardFragment.this,
485                                                 mAppEntry.info,
486                                                 PackageManager
487                                                         .COMPONENT_ENABLED_STATE_DISABLED_USER)
488                                                 .execute((Object) null);
489                                     }
490                                 })
491                         .setNegativeButton(R.string.dlg_cancel, null)
492                         .create();
493             case DLG_SPECIAL_DISABLE:
494                 return new AlertDialog.Builder(getActivity())
495                         .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
496                         .setPositiveButton(R.string.app_disable_dlg_positive,
497                                 new DialogInterface.OnClickListener() {
498                                     public void onClick(DialogInterface dialog, int which) {
499                                         // Disable the app and ask for uninstall
500                                         mMetricsFeatureProvider.action(getContext(),
501                                                 MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
502                                         uninstallPkg(mAppEntry.info.packageName,
503                                                 false, true);
504                                     }
505                                 })
506                         .setNegativeButton(R.string.dlg_cancel, null)
507                         .create();
508             case DLG_FORCE_STOP:
509                 return new AlertDialog.Builder(getActivity())
510                         .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
511                         .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
512                         .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
513                             public void onClick(DialogInterface dialog, int which) {
514                                 // Force stop
515                                 forceStopPackage(mAppEntry.info.packageName);
516                             }
517                         })
518                         .setNegativeButton(R.string.dlg_cancel, null)
519                         .create();
520         }
521         return mInstantAppButtonPreferenceController.createDialog(id);
522     }
523 
524     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
525         stopListeningToPackageRemove();
526         // Create new intent to launch Uninstaller activity
527         final Uri packageURI = Uri.parse("package:" + packageName);
528         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
529         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
530         mMetricsFeatureProvider.action(
531                 getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
532         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
533         mDisableAfterUninstall = andDisable;
534     }
535 
536     private void forceStopPackage(String pkgName) {
537         mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
538         final ActivityManager am = (ActivityManager) getActivity().getSystemService(
539                 Context.ACTIVITY_SERVICE);
540         Log.d(TAG, "Stopping package " + pkgName);
541         am.forceStopPackage(pkgName);
542         final int userId = UserHandle.getUserId(mAppEntry.info.uid);
543         mState.invalidatePackage(pkgName, userId);
544         final AppEntry newEnt = mState.getEntry(pkgName, userId);
545         if (newEnt != null) {
546             mAppEntry = newEnt;
547         }
548         mAppActionButtonPreferenceController.checkForceStop(mAppEntry, mPackageInfo);
549     }
550 
551     public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args,
552             SettingsPreferenceFragment caller, AppEntry appEntry) {
553         // start new fragment to display extended information
554         if (args == null) {
555             args = new Bundle();
556         }
557         args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
558         args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
559         new SubSettingLauncher(caller.getContext())
560                 .setDestination(fragment.getName())
561                 .setArguments(args)
562                 .setTitle(title)
563                 .setResultListener(caller, SUB_INFO_FRAGMENT)
564                 .setSourceMetricsCategory(caller.getMetricsCategory())
565                 .launch();
566     }
567 
568     void handleUninstallButtonClick() {
569         if (mAppEntry == null) {
570             setIntentAndFinish(true, true);
571             return;
572         }
573         final String packageName = mAppEntry.info.packageName;
574         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
575             stopListeningToPackageRemove();
576             final Activity activity = getActivity();
577             final Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class);
578             uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
579                     mPackageName);
580             mMetricsFeatureProvider.action(
581                     activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
582             activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN);
583             return;
584         }
585         final EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(),
586                 packageName, mUserId);
587         final boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
588                 RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId);
589         if (admin != null && !uninstallBlockedBySystem) {
590             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin);
591         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
592             if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
593                 // If the system app has an update and this is the only user on the device,
594                 // then offer to downgrade the app, otherwise only offer to disable the
595                 // app for this user.
596                 if (mUpdatedSysApp && isSingleUser()) {
597                     showDialogInner(DLG_SPECIAL_DISABLE, 0);
598                 } else {
599                     showDialogInner(DLG_DISABLE, 0);
600                 }
601             } else {
602                 mMetricsFeatureProvider.action(
603                         getActivity(),
604                         MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
605                 new DisableChanger(this, mAppEntry.info,
606                         PackageManager.COMPONENT_ENABLED_STATE_ENABLED)
607                         .execute((Object) null);
608             }
609         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
610             uninstallPkg(packageName, true, false);
611         } else {
612             uninstallPkg(packageName, false, false);
613         }
614     }
615 
616     void handleForceStopButtonClick() {
617         if (mAppEntry == null) {
618             setIntentAndFinish(true, true);
619             return;
620         }
621         if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
622             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
623                     getActivity(), mAppsControlDisallowedAdmin);
624         } else {
625             showDialogInner(DLG_FORCE_STOP, 0);
626             //forceStopPackage(mAppInfo.packageName);
627         }
628     }
629 
630     /** Returns whether there is only one user on this device, not including the system-only user */
631     private boolean isSingleUser() {
632         final int userCount = mUserManager.getUserCount();
633         return userCount == 1 || (mUserManager.isSplitSystemUser() && userCount == 2);
634     }
635 
636     private void onPackageRemoved() {
637         getActivity().finishActivity(SUB_INFO_FRAGMENT);
638         getActivity().finishAndRemoveTask();
639     }
640 
641     @VisibleForTesting
642     int getNumberOfUserWithPackageInstalled(String packageName) {
643         final List<UserInfo> userInfos = mUserManager.getUsers(true);
644         int count = 0;
645 
646         for (final UserInfo userInfo : userInfos) {
647             try {
648                 // Use this API to check whether user has this package
649                 final ApplicationInfo info = mPm.getApplicationInfoAsUser(
650                         packageName, PackageManager.GET_META_DATA, userInfo.id);
651                 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
652                     count++;
653                 }
654             } catch (NameNotFoundException e) {
655                 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id);
656             }
657         }
658 
659         return count;
660     }
661 
662     private static class DisableChanger extends AsyncTask<Object, Object, Object> {
663         final PackageManager mPm;
664         final WeakReference<AppInfoDashboardFragment> mActivity;
665         final ApplicationInfo mInfo;
666         final int mState;
667 
668         DisableChanger(AppInfoDashboardFragment activity, ApplicationInfo info, int state) {
669             mPm = activity.mPm;
670             mActivity = new WeakReference<AppInfoDashboardFragment>(activity);
671             mInfo = info;
672             mState = state;
673         }
674 
675         @Override
676         protected Object doInBackground(Object... params) {
677             mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
678             return null;
679         }
680     }
681 
682     private String getPackageName() {
683         if (mPackageName != null) {
684             return mPackageName;
685         }
686         final Bundle args = getArguments();
687         mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
688         if (mPackageName == null) {
689             final Intent intent = (args == null) ?
690                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
691             if (intent != null) {
692                 mPackageName = intent.getData().getSchemeSpecificPart();
693             }
694         }
695         return mPackageName;
696     }
697 
698     @VisibleForTesting
699     void retrieveAppEntry() {
700         final Activity activity = getActivity();
701         if (activity == null) {
702             return;
703         }
704         if (mState == null) {
705             mState = ApplicationsState.getInstance(activity.getApplication());
706             mSession = mState.newSession(this, getLifecycle());
707         }
708         mUserId = UserHandle.myUserId();
709         mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
710         if (mAppEntry != null) {
711             // Get application info again to refresh changed properties of application
712             try {
713                 mPackageInfo = activity.getPackageManager().getPackageInfo(
714                         mAppEntry.info.packageName,
715                         PackageManager.MATCH_DISABLED_COMPONENTS |
716                                 PackageManager.MATCH_ANY_USER |
717                                 PackageManager.GET_SIGNATURES |
718                                 PackageManager.GET_PERMISSIONS);
719             } catch (NameNotFoundException e) {
720                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
721             }
722         } else {
723             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
724             mPackageInfo = null;
725         }
726     }
727 
728     private void setIntentAndFinish(boolean finish, boolean appChanged) {
729         if (localLOGV) Log.i(TAG, "appChanged=" + appChanged);
730         final Intent intent = new Intent();
731         intent.putExtra(ManageApplications.APP_CHG, appChanged);
732         final SettingsActivity sa = (SettingsActivity) getActivity();
733         sa.finishPreferencePanel(Activity.RESULT_OK, intent);
734         mFinishing = true;
735     }
736 
737     void showDialogInner(int id, int moveErrorCode) {
738         final DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
739         newFragment.setTargetFragment(this, 0);
740         newFragment.show(getFragmentManager(), "dialog " + id);
741     }
742 
743     @Override
744     public void onRunningStateChanged(boolean running) {
745         // No op.
746     }
747 
748     @Override
749     public void onRebuildComplete(ArrayList<AppEntry> apps) {
750         // No op.
751     }
752 
753     @Override
754     public void onPackageIconChanged() {
755         // No op.
756     }
757 
758     @Override
759     public void onAllSizesComputed() {
760         // No op.
761     }
762 
763     @Override
764     public void onLauncherInfoChanged() {
765         // No op.
766     }
767 
768     @Override
769     public void onLoadEntriesCompleted() {
770         // No op.
771     }
772 
773     @Override
774     public void onPackageListChanged() {
775         if (!refreshUi()) {
776             setIntentAndFinish(true, true);
777         }
778     }
779 
780     public static class MyAlertDialogFragment extends InstrumentedDialogFragment {
781 
782         private static final String ARG_ID = "id";
783 
784         @Override
785         public int getMetricsCategory() {
786             return MetricsEvent.DIALOG_APP_INFO_ACTION;
787         }
788 
789         @Override
790         public Dialog onCreateDialog(Bundle savedInstanceState) {
791             final int id = getArguments().getInt(ARG_ID);
792             final int errorCode = getArguments().getInt("moveError");
793             final Dialog dialog =
794                     ((AppInfoDashboardFragment) getTargetFragment()).createDialog(id, errorCode);
795             if (dialog == null) {
796                 throw new IllegalArgumentException("unknown id " + id);
797             }
798             return dialog;
799         }
800 
801         public static MyAlertDialogFragment newInstance(int id, int errorCode) {
802             final MyAlertDialogFragment dialogFragment = new MyAlertDialogFragment();
803             final Bundle args = new Bundle();
804             args.putInt(ARG_ID, id);
805             args.putInt("moveError", errorCode);
806             dialogFragment.setArguments(args);
807             return dialogFragment;
808         }
809     }
810 
811     @VisibleForTesting
812     void startListeningToPackageRemove() {
813         if (mListeningToPackageRemove) {
814             return;
815         }
816         mListeningToPackageRemove = true;
817         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
818         filter.addDataScheme("package");
819         getContext().registerReceiver(mPackageRemovedReceiver, filter);
820     }
821 
822     private void stopListeningToPackageRemove() {
823         if (!mListeningToPackageRemove) {
824             return;
825         }
826         mListeningToPackageRemove = false;
827         getContext().unregisterReceiver(mPackageRemovedReceiver);
828     }
829 
830     @VisibleForTesting
831     final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
832         @Override
833         public void onReceive(Context context, Intent intent) {
834             final String packageName = intent.getData().getSchemeSpecificPart();
835             if (!mFinishing && (mAppEntry == null || mAppEntry.info == null
836                     || TextUtils.equals(mAppEntry.info.packageName, packageName))) {
837                 onPackageRemoved();
838             }
839         }
840     };
841 
842 }
843