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