• 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         use(AdvancedAppInfoPreferenceCategoryController.class).setChildren(Arrays.asList(
219                 writeSystemSettings, drawOverlay, pip, externalSource, acrossProfiles,
220                 alarmsAndReminders));
221 
222         final AppLocalePreferenceController appLocale =
223                 use(AppLocalePreferenceController.class);
224         appLocale.setParentFragment(this);
225     }
226 
227     @Override
onCreate(Bundle icicle)228     public void onCreate(Bundle icicle) {
229         super.onCreate(icicle);
230         mFinishing = false;
231         final Activity activity = getActivity();
232         mDpm = (DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE);
233         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
234         mPm = activity.getPackageManager();
235         if (!ensurePackageInfoAvailable(activity)) {
236             return;
237         }
238         if (!ensureDisplayableModule(activity)) {
239             return;
240         }
241         startListeningToPackageRemove();
242 
243         setHasOptionsMenu(true);
244         replaceEnterpriseStringTitle("interact_across_profiles",
245                 CONNECTED_WORK_AND_PERSONAL_APPS_TITLE, R.string.interact_across_profiles_title);
246     }
247 
248     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)249     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
250         if (!ensurePackageInfoAvailable(getActivity())) {
251             return;
252         }
253         super.onCreatePreferences(savedInstanceState, rootKey);
254     }
255 
256     @Override
onDestroy()257     public void onDestroy() {
258         stopListeningToPackageRemove();
259         super.onDestroy();
260     }
261 
262     @Override
getMetricsCategory()263     public int getMetricsCategory() {
264         return SettingsEnums.APPLICATIONS_INSTALLED_APP_DETAILS;
265     }
266 
267     @Override
onResume()268     public void onResume() {
269         super.onResume();
270         final Activity activity = getActivity();
271         mAppsControlDisallowedAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
272                 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
273         mAppsControlDisallowedBySystem = RestrictedLockUtilsInternal.hasBaseUserRestriction(
274                 activity, UserManager.DISALLOW_APPS_CONTROL, mUserId);
275 
276         if (!refreshUi()) {
277             setIntentAndFinish(true, true);
278         }
279         getActivity().invalidateOptionsMenu();
280     }
281 
282     @Override
getPreferenceScreenResId()283     protected int getPreferenceScreenResId() {
284         return R.xml.app_info_settings;
285     }
286 
287     @Override
getLogTag()288     protected String getLogTag() {
289         return TAG;
290     }
291 
292     @Override
createPreferenceControllers(Context context)293     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
294         retrieveAppEntry();
295         if (mPackageInfo == null) {
296             return null;
297         }
298         final String packageName = getPackageName();
299         final List<AbstractPreferenceController> controllers = new ArrayList<>();
300         final Lifecycle lifecycle = getSettingsLifecycle();
301 
302         // The following are controllers for preferences that needs to refresh the preference state
303         // when app state changes.
304         controllers.add(
305                 new AppHeaderViewPreferenceController(context, this, packageName, lifecycle));
306 
307         for (AbstractPreferenceController controller : controllers) {
308             mCallbacks.add((Callback) controller);
309         }
310 
311         // The following are controllers for preferences that don't need to refresh the preference
312         // state when app state changes.
313         mInstantAppButtonPreferenceController =
314                 new InstantAppButtonsPreferenceController(context, this, packageName, lifecycle);
315         controllers.add(mInstantAppButtonPreferenceController);
316         mAppButtonsPreferenceController = new AppButtonsPreferenceController(
317                 (SettingsActivity) getActivity(), this, lifecycle, packageName, mState,
318                 REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
319         controllers.add(mAppButtonsPreferenceController);
320         controllers.add(new AppBatteryPreferenceController(
321                 context, this, packageName, getUid(), lifecycle));
322         controllers.add(new AppMemoryPreferenceController(context, this, lifecycle));
323         controllers.add(new DefaultHomeShortcutPreferenceController(context, packageName));
324         controllers.add(new DefaultBrowserShortcutPreferenceController(context, packageName));
325         controllers.add(new DefaultPhoneShortcutPreferenceController(context, packageName));
326         controllers.add(new DefaultEmergencyShortcutPreferenceController(context, packageName));
327         controllers.add(new DefaultSmsShortcutPreferenceController(context, packageName));
328 
329         return controllers;
330     }
331 
addToCallbackList(Callback callback)332     void addToCallbackList(Callback callback) {
333         if (callback != null) {
334             mCallbacks.add(callback);
335         }
336     }
337 
getAppEntry()338     ApplicationsState.AppEntry getAppEntry() {
339         return mAppEntry;
340     }
341 
setAppEntry(ApplicationsState.AppEntry appEntry)342     void setAppEntry(ApplicationsState.AppEntry appEntry) {
343         mAppEntry = appEntry;
344     }
345 
getPackageInfo()346     public PackageInfo getPackageInfo() {
347         return mPackageInfo;
348     }
349 
350     @Override
onPackageSizeChanged(String packageName)351     public void onPackageSizeChanged(String packageName) {
352         if (!TextUtils.equals(packageName, mPackageName)) {
353             Log.d(TAG, "Package change irrelevant, skipping");
354             return;
355         }
356         refreshUi();
357     }
358 
359     /**
360      * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment
361      * will finish.
362      *
363      * @return true if packageInfo is available.
364      */
365     @VisibleForTesting
ensurePackageInfoAvailable(Activity activity)366     boolean ensurePackageInfoAvailable(Activity activity) {
367         if (mPackageInfo == null) {
368             mFinishing = true;
369             Log.w(TAG, "Package info not available. Is this package already uninstalled?");
370             activity.finishAndRemoveTask();
371             return false;
372         }
373         return true;
374     }
375 
376     /**
377      * Ensures the package is displayable as directed by {@link AppUtils#isHiddenSystemModule}.
378      * If it's not, the fragment will finish.
379      *
380      * @return true if package is displayable.
381      */
382     @VisibleForTesting
ensureDisplayableModule(Activity activity)383     boolean ensureDisplayableModule(Activity activity) {
384         if (AppUtils.isHiddenSystemModule(activity.getApplicationContext(), mPackageName)) {
385             mFinishing = true;
386             Log.w(TAG, "Package is hidden module, exiting: " + mPackageName);
387             activity.finishAndRemoveTask();
388             return false;
389         }
390         return true;
391     }
392 
393     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)394     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
395         super.onCreateOptionsMenu(menu, inflater);
396         menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
397                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
398         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
399                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
400         menu.add(0, ACCESS_RESTRICTED_SETTINGS, 0,
401                 R.string.app_restricted_settings_lockscreen_title)
402                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
403     }
404 
405     @Override
onPrepareOptionsMenu(Menu menu)406     public void onPrepareOptionsMenu(Menu menu) {
407         if (mFinishing) {
408             return;
409         }
410         super.onPrepareOptionsMenu(menu);
411         final MenuItem uninstallAllUsersItem = menu.findItem(UNINSTALL_ALL_USERS_MENU);
412         uninstallAllUsersItem.setVisible(
413                 shouldShowUninstallForAll(mAppEntry) && !mAppsControlDisallowedBySystem);
414         if (uninstallAllUsersItem.isVisible()) {
415             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(),
416                     uninstallAllUsersItem, mAppsControlDisallowedAdmin);
417         }
418         menu.findItem(ACCESS_RESTRICTED_SETTINGS).setVisible(shouldShowAccessRestrictedSettings());
419         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
420         final MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
421         final boolean uninstallUpdateDisabled = getContext().getResources().getBoolean(
422                 R.bool.config_disable_uninstall_update);
423         uninstallUpdatesItem.setVisible(mUserManager.isAdminUser()
424                 && mUpdatedSysApp
425                 && !mAppsControlDisallowedBySystem
426                 && !uninstallUpdateDisabled);
427         if (uninstallUpdatesItem.isVisible()) {
428             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getActivity(),
429                     uninstallUpdatesItem, mAppsControlDisallowedAdmin);
430         }
431     }
432 
showLockScreen(Context context, Runnable successRunnable)433     private static void showLockScreen(Context context, Runnable successRunnable) {
434         final KeyguardManager keyguardManager = context.getSystemService(
435                 KeyguardManager.class);
436 
437         if (keyguardManager.isKeyguardSecure()) {
438             final BiometricPrompt.AuthenticationCallback authenticationCallback =
439                     new BiometricPrompt.AuthenticationCallback() {
440                         @Override
441                         public void onAuthenticationSucceeded(
442                                 BiometricPrompt.AuthenticationResult result) {
443                             successRunnable.run();
444                         }
445 
446                         @Override
447                         public void onAuthenticationError(int errorCode, CharSequence errString) {
448                             //Do nothing
449                         }
450                     };
451 
452             final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
453                     .setUseDefaultTitle(); // use default title if title is null/empty
454 
455             final BiometricManager bm = context.getSystemService(BiometricManager.class);
456             final int authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
457                     | BiometricManager.Authenticators.BIOMETRIC_WEAK;
458             if (bm.canAuthenticate(authenticators) == BiometricManager.BIOMETRIC_SUCCESS) {
459                 builder.setAllowedAuthenticators(authenticators);
460                 builder.setSubtitle(bm.getStrings(authenticators).getPromptMessage());
461             }
462 
463             final BiometricPrompt bp = builder.build();
464             final Handler handler = new Handler(Looper.getMainLooper());
465             bp.authenticate(new CancellationSignal(),
466                     runnable -> handler.post(runnable),
467                     authenticationCallback);
468         } else {
469             successRunnable.run();
470         }
471     }
472 
473     @Override
onOptionsItemSelected(MenuItem item)474     public boolean onOptionsItemSelected(MenuItem item) {
475         switch (item.getItemId()) {
476             case UNINSTALL_ALL_USERS_MENU:
477                 uninstallPkg(mAppEntry.info.packageName, true, false);
478                 return true;
479             case UNINSTALL_UPDATES:
480                 uninstallPkg(mAppEntry.info.packageName, false, false);
481                 return true;
482             case ACCESS_RESTRICTED_SETTINGS:
483                 showLockScreen(getContext(), () -> {
484                     final AppOpsManager appOpsManager = getContext().getSystemService(
485                             AppOpsManager.class);
486                     appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,
487                             getUid(),
488                             getPackageName(),
489                             AppOpsManager.MODE_ALLOWED);
490                     getActivity().invalidateOptionsMenu();
491                     final String toastString = getContext().getString(
492                             R.string.toast_allows_restricted_settings_successfully,
493                             mAppEntry.label);
494                     Toast.makeText(getContext(), toastString, Toast.LENGTH_LONG).show();
495                 });
496                 return true;
497         }
498         return super.onOptionsItemSelected(item);
499     }
500 
501     @Override
onActivityResult(int requestCode, int resultCode, Intent data)502     public void onActivityResult(int requestCode, int resultCode, Intent data) {
503         super.onActivityResult(requestCode, resultCode, data);
504         if (requestCode == REQUEST_UNINSTALL) {
505             // Refresh option menu
506             getActivity().invalidateOptionsMenu();
507         }
508         if (mAppButtonsPreferenceController != null) {
509             mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
510         }
511     }
512 
513     @Override
handleDialogClick(int id)514     public void handleDialogClick(int id) {
515         if (mAppButtonsPreferenceController != null) {
516             mAppButtonsPreferenceController.handleDialogClick(id);
517         }
518     }
519 
shouldShowAccessRestrictedSettings()520     private boolean shouldShowAccessRestrictedSettings() {
521         try {
522             final int mode = getSystemService(AppOpsManager.class).noteOpNoThrow(
523                     AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, getUid(),
524                     getPackageName());
525             return mode == AppOpsManager.MODE_IGNORED;
526         } catch (Exception e) {
527             // Fallback in case if app ops is not available in testing.
528             return false;
529         }
530     }
531 
532     @VisibleForTesting
shouldShowUninstallForAll(AppEntry appEntry)533     boolean shouldShowUninstallForAll(AppEntry appEntry) {
534         boolean showIt = true;
535         if (mUpdatedSysApp) {
536             showIt = false;
537         } else if (appEntry == null) {
538             showIt = false;
539         } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
540             showIt = false;
541         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
542             showIt = false;
543         } else if (UserHandle.myUserId() != 0) {
544             showIt = false;
545         } else if (mUserManager.getUsers().size() < 2) {
546             showIt = false;
547         } else if (getNumberOfUserWithPackageInstalled(mPackageName) < 2
548                 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
549             showIt = false;
550         } else if (AppUtils.isInstant(appEntry.info)) {
551             showIt = false;
552         }
553         return showIt;
554     }
555 
556     @VisibleForTesting
refreshUi()557     boolean refreshUi() {
558         retrieveAppEntry();
559         if (mAppEntry == null) {
560             return false; // onCreate must have failed, make sure to exit
561         }
562 
563         if (mPackageInfo == null) {
564             return false; // onCreate must have failed, make sure to exit
565         }
566 
567         mState.ensureIcon(mAppEntry);
568 
569         // Update the preference summaries.
570         for (Callback callback : mCallbacks) {
571             callback.refreshUi();
572         }
573         if (mAppButtonsPreferenceController.isAvailable()) {
574             mAppButtonsPreferenceController.refreshUi();
575         }
576 
577         if (!mInitialized) {
578             // First time init: are we displaying an uninstalled app?
579             mInitialized = true;
580             mShowUninstalled = (mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0;
581         } else {
582             // All other times: if the app no longer exists then we want
583             // to go away.
584             try {
585                 final ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
586                         mAppEntry.info.packageName,
587                         PackageManager.MATCH_DISABLED_COMPONENTS
588                                 | PackageManager.MATCH_ANY_USER);
589                 if (!mShowUninstalled) {
590                     // If we did not start out with the app uninstalled, then
591                     // it transitioning to the uninstalled state for the current
592                     // user means we should go away as well.
593                     return (ainfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0;
594                 }
595             } catch (NameNotFoundException e) {
596                 return false;
597             }
598         }
599 
600         return true;
601     }
602 
603     @Override
shouldSkipForInitialSUW()604     protected boolean shouldSkipForInitialSUW() {
605         return true;
606     }
607 
uninstallPkg(String packageName, boolean allUsers, boolean andDisable)608     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
609         stopListeningToPackageRemove();
610         // Create new intent to launch Uninstaller activity
611         final Uri packageURI = Uri.parse("package:" + packageName);
612         final Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
613         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
614         mMetricsFeatureProvider.action(
615                 getContext(), SettingsEnums.ACTION_SETTINGS_UNINSTALL_APP);
616         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
617     }
618 
startAppInfoFragment(Class<?> fragment, int title, Bundle args, SettingsPreferenceFragment caller, AppEntry appEntry)619     public static void startAppInfoFragment(Class<?> fragment, int title, Bundle args,
620             SettingsPreferenceFragment caller, AppEntry appEntry) {
621         // start new fragment to display extended information
622         if (args == null) {
623             args = new Bundle();
624         }
625         args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
626         args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
627         new SubSettingLauncher(caller.getContext())
628                 .setDestination(fragment.getName())
629                 .setArguments(args)
630                 .setTitleRes(title)
631                 .setResultListener(caller, SUB_INFO_FRAGMENT)
632                 .setSourceMetricsCategory(caller.getMetricsCategory())
633                 .launch();
634     }
635 
onPackageRemoved()636     private void onPackageRemoved() {
637         getActivity().finishActivity(SUB_INFO_FRAGMENT);
638         getActivity().finishAndRemoveTask();
639     }
640 
641     @VisibleForTesting
getNumberOfUserWithPackageInstalled(String packageName)642     int getNumberOfUserWithPackageInstalled(String packageName) {
643         final List<UserInfo> userInfos = mUserManager.getAliveUsers();
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 
getPackageName()662     private String getPackageName() {
663         if (mPackageName != null) {
664             return mPackageName;
665         }
666         final Bundle args = getArguments();
667         mPackageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
668         if (mPackageName == null) {
669             final Intent intent = args == null ?
670                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
671             if (intent != null) {
672                 mPackageName = intent.getData().getSchemeSpecificPart();
673             }
674         }
675         return mPackageName;
676     }
677 
getUid()678     private int getUid() {
679         if (mUid > 0) {
680             return mUid;
681         }
682         final Bundle args = getArguments();
683         mUid = (args != null) ? args.getInt(ARG_PACKAGE_UID) : -1;
684         if (mUid <= 0) {
685             final Intent intent = args == null
686                     ? getActivity().getIntent() : (Intent) args.getParcelable("intent");
687             mUid = intent != null && intent.getExtras() != null
688                     ? mUid = intent.getIntExtra("uId", -1) : -1;
689         }
690         return mUid;
691     }
692 
693     @VisibleForTesting
retrieveAppEntry()694     void retrieveAppEntry() {
695         final Activity activity = getActivity();
696         if (activity == null || mFinishing) {
697             return;
698         }
699         if (mState == null) {
700             mState = ApplicationsState.getInstance(activity.getApplication());
701             mSession = mState.newSession(this, getSettingsLifecycle());
702         }
703         mUserId = UserHandle.myUserId();
704         mAppEntry = mState.getEntry(getPackageName(), UserHandle.myUserId());
705         if (mAppEntry != null) {
706             // Get application info again to refresh changed properties of application
707             try {
708                 mPackageInfo = activity.getPackageManager().getPackageInfo(
709                         mAppEntry.info.packageName,
710                         PackageManager.MATCH_DISABLED_COMPONENTS |
711                                 PackageManager.MATCH_ANY_USER |
712                                 PackageManager.GET_SIGNATURES |
713                                 PackageManager.GET_PERMISSIONS);
714             } catch (NameNotFoundException e) {
715                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
716             }
717         } else {
718             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
719             mPackageInfo = null;
720         }
721     }
722 
setIntentAndFinish(boolean finish, boolean appChanged)723     private void setIntentAndFinish(boolean finish, boolean appChanged) {
724         if (localLOGV) Log.i(TAG, "appChanged=" + appChanged);
725         final Intent intent = new Intent();
726         intent.putExtra(ManageApplications.APP_CHG, appChanged);
727         final SettingsActivity sa = (SettingsActivity) getActivity();
728         sa.finishPreferencePanel(Activity.RESULT_OK, intent);
729         mFinishing = true;
730     }
731 
732     @Override
onRunningStateChanged(boolean running)733     public void onRunningStateChanged(boolean running) {
734         // No op.
735     }
736 
737     @Override
onRebuildComplete(ArrayList<AppEntry> apps)738     public void onRebuildComplete(ArrayList<AppEntry> apps) {
739         // No op.
740     }
741 
742     @Override
onPackageIconChanged()743     public void onPackageIconChanged() {
744         // No op.
745     }
746 
747     @Override
onAllSizesComputed()748     public void onAllSizesComputed() {
749         // No op.
750     }
751 
752     @Override
onLauncherInfoChanged()753     public void onLauncherInfoChanged() {
754         // No op.
755     }
756 
757     @Override
onLoadEntriesCompleted()758     public void onLoadEntriesCompleted() {
759         // No op.
760     }
761 
762     @Override
onPackageListChanged()763     public void onPackageListChanged() {
764         if (!refreshUi()) {
765             setIntentAndFinish(true, true);
766         }
767     }
768 
769     @VisibleForTesting
startListeningToPackageRemove()770     void startListeningToPackageRemove() {
771         if (mListeningToPackageRemove) {
772             return;
773         }
774         mListeningToPackageRemove = true;
775         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
776         filter.addDataScheme("package");
777         getContext().registerReceiver(mPackageRemovedReceiver, filter);
778     }
779 
stopListeningToPackageRemove()780     private void stopListeningToPackageRemove() {
781         if (!mListeningToPackageRemove) {
782             return;
783         }
784         mListeningToPackageRemove = false;
785         getContext().unregisterReceiver(mPackageRemovedReceiver);
786     }
787 
788     @VisibleForTesting
789     final BroadcastReceiver mPackageRemovedReceiver = new BroadcastReceiver() {
790         @Override
791         public void onReceive(Context context, Intent intent) {
792             if (mFinishing) {
793                 return;
794             }
795 
796             final String packageName = intent.getData().getSchemeSpecificPart();
797             if (mAppEntry == null
798                     || mAppEntry.info == null
799                     || TextUtils.equals(mAppEntry.info.packageName, packageName)) {
800                 onPackageRemoved();
801             } else if (mAppEntry.info.isResourceOverlay()
802                     && TextUtils.equals(mPackageInfo.overlayTarget, packageName)) {
803                 refreshUi();
804             }
805         }
806     };
807 
808 }
809