• 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 android.app.admin.DevicePolicyResources.Strings.Settings.CONNECTED_WORK_AND_PERSONAL_APPS_TITLE;
20 
21 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
22 
23 import android.app.Activity;
24 import android.app.AppOpsManager;
25 import android.app.KeyguardManager;
26 import android.app.admin.DevicePolicyManager;
27 import android.app.settings.SettingsEnums;
28 import android.content.BroadcastReceiver;
29 import android.content.Context;
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.hardware.biometrics.BiometricManager;
38 import android.hardware.biometrics.BiometricPrompt;
39 import android.net.Uri;
40 import android.os.Bundle;
41 import android.os.CancellationSignal;
42 import android.os.Handler;
43 import android.os.Looper;
44 import android.os.UserHandle;
45 import android.os.UserManager;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.view.Menu;
49 import android.view.MenuInflater;
50 import android.view.MenuItem;
51 import android.widget.Toast;
52 
53 import androidx.annotation.VisibleForTesting;
54 
55 import com.android.settings.R;
56 import com.android.settings.SettingsActivity;
57 import com.android.settings.SettingsPreferenceFragment;
58 import com.android.settings.applications.manageapplications.ManageApplications;
59 import com.android.settings.applications.specialaccess.interactacrossprofiles.InteractAcrossProfilesDetailsPreferenceController;
60 import com.android.settings.applications.specialaccess.pictureinpicture.PictureInPictureDetailPreferenceController;
61 import com.android.settings.core.SubSettingLauncher;
62 import com.android.settings.dashboard.DashboardFragment;
63 import com.android.settingslib.RestrictedLockUtilsInternal;
64 import com.android.settingslib.applications.AppUtils;
65 import com.android.settingslib.applications.ApplicationsState;
66 import com.android.settingslib.applications.ApplicationsState.AppEntry;
67 import com.android.settingslib.core.AbstractPreferenceController;
68 import com.android.settingslib.core.lifecycle.Lifecycle;
69 
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.List;
73 
74 /**
75  * Dashboard fragment to display application information from Settings. This activity presents
76  * extended information associated with a package like code, data, total size, permissions
77  * used by the application and also the set of default launchable activities.
78  * For system applications, an option to clear user data is displayed only if data size is > 0.
79  * System applications that do not want clear user data do not have this option.
80  * For non-system applications, there is no option to clear data. Instead there is an option to
81  * uninstall the application.
82  */
83 public class AppInfoDashboardFragment extends DashboardFragment
84         implements ApplicationsState.Callbacks,
85         ButtonActionDialogFragment.AppButtonsDialogListener {
86 
87     private static final String TAG = "AppInfoDashboard";
88 
89     // Menu identifiers
90     @VisibleForTesting
91     static final int UNINSTALL_ALL_USERS_MENU = 1;
92     @VisibleForTesting
93     static final int UNINSTALL_UPDATES = 2;
94     static final int INSTALL_INSTANT_APP_MENU = 3;
95     static final int ACCESS_RESTRICTED_SETTINGS = 4;
96 
97     // Result code identifiers
98     @VisibleForTesting
99     static final int REQUEST_UNINSTALL = 0;
100     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 5;
101 
102     static final int SUB_INFO_FRAGMENT = 1;
103 
104     static final int LOADER_CHART_DATA = 2;
105     static final int LOADER_STORAGE = 3;
106     static final int LOADER_BATTERY = 4;
107     static final int LOADER_BATTERY_USAGE_STATS = 5;
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     private int mUid;
124 
125     private DevicePolicyManager mDpm;
126     private UserManager mUserManager;
127     private PackageManager mPm;
128 
129     @VisibleForTesting
130     boolean mFinishing;
131     private boolean mListeningToPackageRemove;
132 
133 
134     private boolean mInitialized;
135     private boolean mShowUninstalled;
136     private boolean mUpdatedSysApp = false;
137 
138     private List<Callback> mCallbacks = new ArrayList<>();
139 
140     private InstantAppButtonsPreferenceController mInstantAppButtonPreferenceController;
141     private AppButtonsPreferenceController mAppButtonsPreferenceController;
142 
143     /**
144      * Callback to invoke when app info has been changed.
145      */
146     public interface Callback {
refreshUi()147         void refreshUi();
148     }
149 
150     @Override
onAttach(Context context)151     public void onAttach(Context context) {
152         super.onAttach(context);
153         final String packageName = getPackageName();
154         final TimeSpentInAppPreferenceController timeSpentInAppPreferenceController = use(
155                 TimeSpentInAppPreferenceController.class);
156         timeSpentInAppPreferenceController.setPackageName(packageName);
157         timeSpentInAppPreferenceController.setParentFragment(this);
158         timeSpentInAppPreferenceController.initLifeCycleOwner(this);
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 
168         use(AppOpenByDefaultPreferenceController.class)
169                 .setPackageName(packageName)
170                 .setParentFragment(this);
171 
172         use(AppPermissionPreferenceController.class).setParentFragment(this);
173         use(AppPermissionPreferenceController.class).setPackageName(packageName);
174         use(AppSettingPreferenceController.class)
175                 .setPackageName(packageName)
176                 .setParentFragment(this);
177         use(AppAllServicesPreferenceController.class).setParentFragment(this);
178         use(AppAllServicesPreferenceController.class).setPackageName(packageName);
179         use(AppStoragePreferenceController.class).setParentFragment(this);
180         use(AppVersionPreferenceController.class).setParentFragment(this);
181         use(InstantAppDomainsPreferenceController.class).setParentFragment(this);
182 
183         final HibernationSwitchPreferenceController appHibernationSettings =
184                 use(HibernationSwitchPreferenceController.class);
185         appHibernationSettings.setParentFragment(this);
186         appHibernationSettings.setPackage(packageName);
187         use(AppHibernationPreferenceCategoryController.class).setChildren(
188                 Arrays.asList(appHibernationSettings));
189 
190         final WriteSystemSettingsPreferenceController writeSystemSettings =
191                 use(WriteSystemSettingsPreferenceController.class);
192         writeSystemSettings.setParentFragment(this);
193 
194         final DrawOverlayDetailPreferenceController drawOverlay =
195                 use(DrawOverlayDetailPreferenceController.class);
196         drawOverlay.setParentFragment(this);
197 
198         final PictureInPictureDetailPreferenceController pip =
199                 use(PictureInPictureDetailPreferenceController.class);
200         pip.setPackageName(packageName);
201         pip.setParentFragment(this);
202 
203         final ExternalSourceDetailPreferenceController externalSource =
204                 use(ExternalSourceDetailPreferenceController.class);
205         externalSource.setPackageName(packageName);
206         externalSource.setParentFragment(this);
207 
208         final InteractAcrossProfilesDetailsPreferenceController acrossProfiles =
209                 use(InteractAcrossProfilesDetailsPreferenceController.class);
210         acrossProfiles.setPackageName(packageName);
211         acrossProfiles.setParentFragment(this);
212 
213         final AlarmsAndRemindersDetailPreferenceController alarmsAndReminders =
214                 use(AlarmsAndRemindersDetailPreferenceController.class);
215         alarmsAndReminders.setPackageName(packageName);
216         alarmsAndReminders.setParentFragment(this);
217 
218         final LongBackgroundTasksDetailsPreferenceController longBackgroundTasks =
219                 use(LongBackgroundTasksDetailsPreferenceController.class);
220         longBackgroundTasks.setPackageName(packageName);
221         longBackgroundTasks.setParentFragment(this);
222 
223         final AdvancedAppInfoPreferenceCategoryController advancedAppInfo =
224                 use(AdvancedAppInfoPreferenceCategoryController.class);
225         advancedAppInfo.setChildren(Arrays.asList(writeSystemSettings, drawOverlay, pip,
226                 externalSource, acrossProfiles, alarmsAndReminders, longBackgroundTasks));
227         advancedAppInfo.setAppEntry(mAppEntry);
228 
229         final AppLocalePreferenceController appLocale =
230                 use(AppLocalePreferenceController.class);
231         appLocale.setParentFragment(this);
232     }
233 
234     @Override
onCreate(Bundle icicle)235     public void onCreate(Bundle icicle) {
236         super.onCreate(icicle);
237         mFinishing = false;
238         final Activity activity = getActivity();
239         mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
240         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
241         mPm = activity.getPackageManager();
242         if (!ensurePackageInfoAvailable(activity)) {
243             return;
244         }
245         if (!ensureDisplayableModule(activity)) {
246             return;
247         }
248         startListeningToPackageRemove();
249 
250         setHasOptionsMenu(true);
251         replaceEnterpriseStringTitle("interact_across_profiles",
252                 CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, R.string.interact_across_profiles_title);
253     }
254 
255     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)256     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
257         if (!ensurePackageInfoAvailable(getActivity())) {
258             return;
259         }
260         super.onCreatePreferences(savedInstanceState, rootKey);
261     }
262 
263     @Override
onDestroy()264     public void onDestroy() {
265         stopListeningToPackageRemove();
266         super.onDestroy();
267     }
268 
269     @Override
getMetricsCategory()270     public int getMetricsCategory() {
271         return SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS;
272     }
273 
274     @Override
onResume()275     public void onResume() {
276         super.onResume();
277         final Activity activity = getActivity();
278         mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
279                 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
280         mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
281                 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
282 
283         if (!refreshUi()) {
284             setIntentAndFinish(true, true);
285         }
286         getActivity().invalidateOptionsMenu();
287     }
288 
289     @Override
getPreferenceScreenResId()290     protected int getPreferenceScreenResId() {
291         return R.xml.app_info_settings;
292     }
293 
294     @Override
getLogTag()295     protected String getLogTag() {
296         return TAG;
297     }
298 
299     @Override
createPreferenceControllers(Context context)300     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
301         retrieveAppEntry();
302         if (mPackageInfo == null) {
303             return null;
304         }
305         final String packageName = getPackageName();
306         final List<AbstractPreferenceController> controllers = new ArrayList<>();
307         final Lifecycle lifecycle = getSettingsLifecycle();
308 
309         // The following are controllers for preferences that needs to refresh the preference state
310         // when app state changes.
311         controllers.add(
312                 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle));
313 
314         for (AbstractPreferenceController controller : controllers) {
315             mCallbacks.add((Callback) controller);
316         }
317 
318         // The following are controllers for preferences that don't need to refresh the preference
319         // state when app state changes.
320         mInstantAppButtonPreferenceController =
321                 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle);
322         controllers.add(mInstantAppButtonPreferenceController);
323         mAppButtonsPreferenceController = new AppButtonsPreferenceController(
324                 (SettingsActivity) getActivity(), this, lifecycle, packageName, mState,
325                 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
326         controllers.add(mAppButtonsPreferenceController);
327         controllers.add(new AppBatteryPreferenceController(
328                 context, this, packageName, getUid(), lifecycle));
329         controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
330         controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
331         controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
332         controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
333         controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
334         controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
335 
336         return controllers;
337     }
338 
addToCallbackList(Callback callback)339     void addToCallbackList(Callback callback) {
340         if (callback != null) {
341             mCallbacks.add(callback);
342         }
343     }
344 
getAppEntry()345     ApplicationsState.AppEntry getAppEntry() {
346         return mAppEntry;
347     }
348 
setAppEntry(ApplicationsState.AppEntry appEntry)349     void setAppEntry(ApplicationsState.AppEntry appEntry) {
350         mAppEntry = appEntry;
351     }
352 
getPackageInfo()353     public PackageInfo getPackageInfo() {
354         return mPackageInfo;
355     }
356 
357     @Override
onPackageSizeChanged(String packageName)358     public void onPackageSizeChanged(String packageName) {
359         if (!TextUtils.equals(packageName, mPackageName)) {
360             Log.d(TAG, "Package change irrelevant, skipping");
361             return;
362         }
363         refreshUi();
364     }
365 
366     /**
367      * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment
368      * will finish.
369      *
370      * @return true if packageInfo is available.
371      */
372     @VisibleForTesting
ensurePackageInfoAvailable(Activity activity)373     boolean ensurePackageInfoAvailable(Activity activity) {
374         if (mPackageInfo == null) {
375             mFinishing = true;
376             Log.w(TAG, "Package info not available. Is this package already uninstalled?");
377             activity.finishAndRemoveTask();
378             return false;
379         }
380         return true;
381     }
382 
383     /**
384      * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}.
385      * If it's not, the fragment will finish.
386      *
387      * @return true if package is displayable.
388      */
389     @VisibleForTesting
ensureDisplayableModule(Activity activity)390     boolean ensureDisplayableModule(Activity activity) {
391         if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) {
392             mFinishing = true;
393             Log.w(TAG, "Package is hidden module, exiting: " + mPackageName);
394             activity.finishAndRemoveTask();
395             return false;
396         }
397         return true;
398     }
399 
400     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)401     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
402         super.onCreateOptionsMenu(menu, inflater);
403         menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
404                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
405         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
406                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
407         menu.add(0, ACCESS_RESTRICTED_SETTINGS, 0,
408                 R.string.app_restricted_settings_lockscreen_title)
409                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
410     }
411 
412     @Override
onPrepareOptionsMenu(Menu menu)413     public void onPrepareOptionsMenu(Menu menu) {
414         if (mFinishing) {
415             return;
416         }
417         super.onPrepareOptionsMenu(menu);
418         final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU);
419         uninstallAllUsersItem.setVisible(
420                 shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem);
421         if (uninstallAllUsersItem.isVisible()) {
422             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(),
423                     uninstallAllUsersItem, mAppsControlDisallowedAdmin);
424         }
425         menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings());
426         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
427         final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
428         final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean(
429                 R.bool.config_disable_uninstall_update);
430         uninstallUpdatesItem.setVisible(mUserManager.isAdminUser()
431                 && mUpdatedSysApp
432                 && !mAppsControlDisallowedBySystem
433                 && !uninstallUpdateDisabled);
434         if (uninstallUpdatesItem.isVisible()) {
435             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(),
436                     uninstallUpdatesItem, mAppsControlDisallowedAdmin);
437         }
438     }
439 
440     /** Shows the lock screen if the keyguard is secured. */
showLockScreen(Context context, Runnable successRunnable)441     public static void showLockScreen(Context context, Runnable successRunnable) {
442         final KeyguardManager keyguardManager = context.getSystemService(
443                 KeyguardManager.class);
444 
445         if (keyguardManager.isKeyguardSecure()) {
446             final BiometricPrompt.AuthenticationCallback authenticationCallback =
447                     new BiometricPrompt.AuthenticationCallback() {
448                         @Override
449                         public void onAuthenticationSucceeded(
450                                 BiometricPrompt.AuthenticationResult result) {
451                             successRunnable.run();
452                         }
453 
454                         @Override
455                         public void onAuthenticationError(int errorCode, CharSequence errString) {
456                             //Do nothing
457                         }
458                     };
459 
460             final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
461                     .setUseDefaultSubtitle() // use default subtitle if subtitle is null/empty
462                     .setUseDefaultTitle(); // use default title if title is null/empty
463 
464             final BiometricManager bm = context.getSystemService(BiometricManager.class);
465             final int authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
466                     | BiometricManager.Authenticators.BIOMETRIC_WEAK;
467             if (bm.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS) {
468                 builder.setAllowedAuthenticators(authenticators);
469                 builder.setSubtitle(bm.getStrings(authenticators).getPromptMessage());
470             }
471 
472             final BiometricPrompt bp = builder.build();
473             final Handler handler = new Handler(Looper.getMainLooper());
474             bp.authenticate(new CancellationSignal(),
475                     runnable -> handler.post(runnable),
476                     authenticationCallback);
477         } else {
478             successRunnable.run();
479         }
480     }
481 
482     @Override
onOptionsItemSelected(MenuItem item)483     public boolean onOptionsItemSelected(MenuItem item) {
484         switch (item.getItemId()) {
485             case UNINSTALL_ALL_USERS_MENU:
486                 uninstallPkg(mAppEntry.info.packageName, true, false);
487                 return true;
488             case UNINSTALL_UPDATES:
489                 uninstallPkg(mAppEntry.info.packageName, false, false);
490                 return true;
491             case ACCESS_RESTRICTED_SETTINGS:
492                 showLockScreen(getContext(), () -> {
493                     final AppOpsManager appOpsManager = getContext().getSystemService(
494                             AppOpsManager.class);
495                     appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
496                             getUid(),
497                             getPackageName(),
498                             AppOpsManager.MODE_ALLOWED);
499                     getActivity().invalidateOptionsMenu();
500                     final String toastString = getContext().getString(
501                             R.string.toast_allows_restricted_settings_successfully,
502                             mAppEntry.label);
503                     Toast.makeText(getContext(), toastString, Toast.LENGTH_LONG).show();
504                 });
505                 return true;
506         }
507         return super.onOptionsItemSelected(item);
508     }
509 
510     @Override
onActivityResult(int requestCode, int resultCode, Intent data)511     public void onActivityResult(int requestCode, int resultCode, Intent data) {
512         super.onActivityResult(requestCode, resultCode, data);
513         if (requestCode == REQUEST_UNINSTALL) {
514             // Refresh option menu
515             getActivity().invalidateOptionsMenu();
516         }
517         if (mAppButtonsPreferenceController != null) {
518             mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
519         }
520     }
521 
522     @Override
handleDialogClick(int id)523     public void handleDialogClick(int id) {
524         if (mAppButtonsPreferenceController != null) {
525             mAppButtonsPreferenceController.handleDialogClick(id);
526         }
527     }
528 
shouldShowAccessRestrictedSettings()529     private boolean shouldShowAccessRestrictedSettings() {
530         try {
531             final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
532                     AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
533                     getPackageName());
534             return mode == AppOpsManager.MODE_IGNORED;
535         } catch (Exception e) {
536             // Fallback in case if app ops is not available in testing.
537             return false;
538         }
539     }
540 
541     @VisibleForTesting
shouldShowUninstallForAll(AppEntry appEntry)542     boolean shouldShowUninstallForAll(AppEntry appEntry) {
543         boolean showIt = true;
544         if (mUpdatedSysApp) {
545             showIt = false;
546         } else if (appEntry == null) {
547             showIt = false;
548         } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
549             showIt = false;
550         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
551             showIt = false;
552         } else if (UserHandle.myUserId() != 0) {
553             showIt = false;
554         } else if (mUserManager.getUsers().size() < 2) {
555             showIt = false;
556         } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2
557                 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
558             showIt = false;
559         } else if (AppUtils.isInstant(appEntry.info)) {
560             showIt = false;
561         }
562         return showIt;
563     }
564 
565     @VisibleForTesting
refreshUi()566     boolean refreshUi() {
567         retrieveAppEntry();
568         if (mAppEntry == null) {
569             return false; // onCreate must have failed, make sure to exit
570         }
571 
572         if (mPackageInfo == null) {
573             return false; // onCreate must have failed, make sure to exit
574         }
575 
576         mState.ensureIcon(mAppEntry);
577 
578         // Update the preference summaries.
579         for (Callback callback : mCallbacks) {
580             callback.refreshUi();
581         }
582         if (mAppButtonsPreferenceController.isAvailable()) {
583             mAppButtonsPreferenceController.refreshUi();
584         }
585 
586         if (!mInitialized) {
587             // First time init: are we displaying an uninstalled app?
588             mInitialized = true;
589             mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0;
590         } else {
591             // All other times: if the app no longer exists then we want
592             // to go away.
593             try {
594                 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
595                         mAppEntry.info.packageName,
596                         PackageManager.MATCH_DISABLED_COMPONENTS
597                                 | PackageManager.MATCH_ANY_USER);
598                 if (!mShowUninstalled) {
599                     // If we did not start out with the app uninstalled, then
600                     // it transitioning to the uninstalled state for the current
601                     // user means we should go away as well.
602                     return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
603                 }
604             } catch (NameNotFoundException e) {
605                 return false;
606             }
607         }
608 
609         return true;
610     }
611 
612     @Override
shouldSkipForInitialSUW()613     protected boolean shouldSkipForInitialSUW() {
614         return true;
615     }
616 
uninstallPkg(String packageName, boolean allUsers, boolean andDisable)617     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
618         stopListeningToPackageRemove();
619         // Create new intent to launch Uninstaller activity
620         final Uri packageURI = Uri.parse("package:" + packageName);
621         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
622         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
623         mMetricsFeatureProvider.action(
624                 getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
625         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
626     }
627 
startAppInfoFragment(Class<?> fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry)628     public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args,
629             SettingsPreferenceFragment caller, AppEntry appEntry) {
630         // start new fragment to display extended information
631         if (args == null) {
632             args = new Bundle();
633         }
634         args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
635         args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
636         new SubSettingLauncher(caller.getContext())
637                 .setDestination(fragment.getName())
638                 .setArguments(args)
639                 .setTitleRes(title)
640                 .setResultListener(caller, SUB_INFO_FRAGMENT)
641                 .setSourceMetricsCategory(caller.getMetricsCategory())
642                 .launch();
643     }
644 
645     /** Starts app info fragment from SPA pages. */
startAppInfoFragment( Class<?> destination, ApplicationInfo app, Context context, int sourceMetricsCategory)646     public static void startAppInfoFragment(
647             Class<?> destination, ApplicationInfo app, Context context, int sourceMetricsCategory) {
648         // start new fragment to display extended information
649         Bundle args = new Bundle();
650         args.putString(ARG_PACKAGE_NAME, app.packageName);
651         args.putInt(ARG_PACKAGE_UID, app.uid);
652         new SubSettingLauncher(context)
653                 .setDestination(destination.getName())
654                 .setArguments(args)
655                 .setUserHandle(UserHandle.getUserHandleForUid(app.uid))
656                 .setSourceMetricsCategory(sourceMetricsCategory)
657                 .launch();
658     }
659 
onPackageRemoved()660     private void onPackageRemoved() {
661         getActivity().finishActivity(SUB_INFO_FRAGMENT);
662         getActivity().finishAndRemoveTask();
663     }
664 
665     @VisibleForTesting
getNumberOfUserWithPackageInstalled(String packageName)666     int getNumberOfUserWithPackageInstalled(String packageName) {
667         final List<UserInfo> userInfos = mUserManager.getAliveUsers();
668         int count = 0;
669 
670         for (final UserInfo userInfo : userInfos) {
671             try {
672                 // Use this API to check whether user has this package
673                 final ApplicationInfo info = mPm.getApplicationInfoAsUser(
674                         packageName, PackageManager.GET_META_DATA, userInfo.id);
675                 if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
676                     count++;
677                 }
678             } catch (NameNotFoundException e) {
679                 Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id);
680             }
681         }
682 
683         return count;
684     }
685 
getPackageName()686     private String getPackageName() {
687         if (mPackageName != null) {
688             return mPackageName;
689         }
690         final Bundle args = getArguments();
691         mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
692         if (mPackageName == null) {
693             final Intent intent = args == null ?
694                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
695             if (intent != null) {
696                 mPackageName = intent.getData().getSchemeSpecificPart();
697             }
698         }
699         return mPackageName;
700     }
701 
getUid()702     private int getUid() {
703         if (mUid > 0) {
704             return mUid;
705         }
706         final Bundle args = getArguments();
707         mUid = (args != null) ? args.getInt(ARG_PACKAGE_UID) : -1;
708         if (mUid <= 0) {
709             final Intent intent = args == null
710                     ? getActivity().getIntent() : (Intent) args.getParcelable("intent");
711             mUid = intent != null && intent.getExtras() != null
712                     ? mUid = intent.getIntExtra("uId", -1) : -1;
713         }
714         return mUid;
715     }
716 
717     @VisibleForTesting
retrieveAppEntry()718     void retrieveAppEntry() {
719         final Activity activity = getActivity();
720         if (activity == null || mFinishing) {
721             return;
722         }
723         if (mState == null) {
724             mState = ApplicationsState.getInstance(activity.getApplication());
725             mSession = mState.newSession(this, getSettingsLifecycle());
726         }
727         mUserId = UserHandle.myUserId();
728         mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
729         if (mAppEntry != null) {
730             // Get application info again to refresh changed properties of application
731             try {
732                 mPackageInfo = activity.getPackageManager().getPackageInfo(
733                         mAppEntry.info.packageName,
734                         PackageManager.MATCH_DISABLED_COMPONENTS |
735                                 PackageManager.MATCH_ANY_USER |
736                                 PackageManager.GET_SIGNATURES |
737                                 PackageManager.GET_PERMISSIONS);
738             } catch (NameNotFoundException e) {
739                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
740             }
741         } else {
742             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
743             mPackageInfo = null;
744         }
745     }
746 
setIntentAndFinish(boolean finish, boolean appChanged)747     private void setIntentAndFinish(boolean finish, boolean appChanged) {
748         if (localLOGV) Log.i(TAG, "appChanged=" + appChanged);
749         final Intent intent = new Intent();
750         intent.putExtra(ManageApplications.APP_CHG, appChanged);
751         final SettingsActivity sa = (SettingsActivity) getActivity();
752         sa.finishPreferencePanel(Activity.RESULT_OK, intent);
753         mFinishing = true;
754     }
755 
756     @Override
onRunningStateChanged(boolean running)757     public void onRunningStateChanged(boolean running) {
758         // No op.
759     }
760 
761     @Override
onRebuildComplete(ArrayList<AppEntry> apps)762     public void onRebuildComplete(ArrayList<AppEntry> apps) {
763         // No op.
764     }
765 
766     @Override
onPackageIconChanged()767     public void onPackageIconChanged() {
768         // No op.
769     }
770 
771     @Override
onAllSizesComputed()772     public void onAllSizesComputed() {
773         // No op.
774     }
775 
776     @Override
onLauncherInfoChanged()777     public void onLauncherInfoChanged() {
778         // No op.
779     }
780 
781     @Override
onLoadEntriesCompleted()782     public void onLoadEntriesCompleted() {
783         // No op.
784     }
785 
786     @Override
onPackageListChanged()787     public void onPackageListChanged() {
788         if (!refreshUi()) {
789             setIntentAndFinish(true, true);
790         }
791     }
792 
793     @VisibleForTesting
startListeningToPackageRemove()794     void startListeningToPackageRemove() {
795         if (mListeningToPackageRemove) {
796             return;
797         }
798         mListeningToPackageRemove = true;
799         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
800         filter.addDataScheme("package");
801         getContext().registerReceiver(mPackageRemovedReceiver, filter);
802     }
803 
stopListeningToPackageRemove()804     private void stopListeningToPackageRemove() {
805         if (!mListeningToPackageRemove) {
806             return;
807         }
808         mListeningToPackageRemove = false;
809         getContext().unregisterReceiver(mPackageRemovedReceiver);
810     }
811 
812     @VisibleForTesting
813     final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
814         @Override
815         public void onReceive(Context context, Intent intent) {
816             if (mFinishing) {
817                 return;
818             }
819 
820             final String packageName = intent.getData().getSchemeSpecificPart();
821             if (mAppEntry == null
822                     || mAppEntry.info == null
823                     || TextUtils.equals(mAppEntry.info.packageName, packageName)) {
824                 onPackageRemoved();
825             } else if (mAppEntry.info.isResourceOverlay()
826                     && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) {
827                 refreshUi();
828             }
829         }
830     };
831 
832 }
833