Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2007 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;
     18 
     19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
     20 
     21 import android.Manifest.permission;
     22 import android.app.Activity;
     23 import android.app.ActivityManager;
     24 import android.app.AlertDialog;
     25 import android.app.LoaderManager;
     26 import android.app.LoaderManager.LoaderCallbacks;
     27 import android.app.admin.DevicePolicyManager;
     28 import android.content.ActivityNotFoundException;
     29 import android.content.BroadcastReceiver;
     30 import android.content.ComponentName;
     31 import android.content.Context;
     32 import android.content.DialogInterface;
     33 import android.content.Intent;
     34 import android.content.Loader;
     35 import android.content.pm.ApplicationInfo;
     36 import android.content.pm.PackageInfo;
     37 import android.content.pm.PackageManager;
     38 import android.content.pm.PackageManager.NameNotFoundException;
     39 import android.content.pm.ResolveInfo;
     40 import android.content.pm.UserInfo;
     41 import android.content.res.Resources;
     42 import android.icu.text.ListFormatter;
     43 import android.net.INetworkStatsService;
     44 import android.net.INetworkStatsSession;
     45 import android.net.NetworkTemplate;
     46 import android.net.TrafficStats;
     47 import android.net.Uri;
     48 import android.os.AsyncTask;
     49 import android.os.BatteryStats;
     50 import android.os.Bundle;
     51 import android.os.RemoteException;
     52 import android.os.ServiceManager;
     53 import android.os.UserHandle;
     54 import android.os.UserManager;
     55 import android.support.annotation.VisibleForTesting;
     56 import android.support.v7.preference.Preference;
     57 import android.support.v7.preference.Preference.OnPreferenceClickListener;
     58 import android.support.v7.preference.PreferenceCategory;
     59 import android.support.v7.preference.PreferenceScreen;
     60 import android.text.BidiFormatter;
     61 import android.text.TextUtils;
     62 import android.text.format.DateUtils;
     63 import android.text.format.Formatter;
     64 import android.util.Log;
     65 import android.view.Menu;
     66 import android.view.MenuInflater;
     67 import android.view.MenuItem;
     68 import android.view.View;
     69 import android.webkit.IWebViewUpdateService;
     70 import android.widget.Button;
     71 
     72 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     73 import com.android.internal.os.BatterySipper;
     74 import com.android.internal.os.BatteryStatsHelper;
     75 import com.android.settings.DeviceAdminAdd;
     76 import com.android.settings.R;
     77 import com.android.settings.SettingsActivity;
     78 import com.android.settings.SettingsPreferenceFragment;
     79 import com.android.settings.Utils;
     80 import com.android.settings.applications.defaultapps.DefaultBrowserPreferenceController;
     81 import com.android.settings.applications.defaultapps.DefaultEmergencyPreferenceController;
     82 import com.android.settings.applications.defaultapps.DefaultHomePreferenceController;
     83 import com.android.settings.applications.defaultapps.DefaultPhonePreferenceController;
     84 import com.android.settings.applications.defaultapps.DefaultSmsPreferenceController;
     85 import com.android.settings.applications.instantapps.InstantAppButtonsController;
     86 import com.android.settings.datausage.AppDataUsage;
     87 import com.android.settings.datausage.DataUsageList;
     88 import com.android.settings.datausage.DataUsageUtils;
     89 import com.android.settings.development.DevelopmentSettingsEnabler;
     90 import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
     91 import com.android.settings.fuelgauge.BatteryEntry;
     92 import com.android.settings.fuelgauge.BatteryStatsHelperLoader;
     93 import com.android.settings.fuelgauge.BatteryUtils;
     94 import com.android.settings.notification.AppNotificationSettings;
     95 import com.android.settings.notification.NotificationBackend;
     96 import com.android.settings.notification.NotificationBackend.AppRow;
     97 import com.android.settings.widget.EntityHeaderController;
     98 import com.android.settingslib.AppItem;
     99 import com.android.settingslib.RestrictedLockUtils;
    100 import com.android.settingslib.applications.AppUtils;
    101 import com.android.settingslib.applications.ApplicationsState;
    102 import com.android.settingslib.applications.ApplicationsState.AppEntry;
    103 import com.android.settingslib.applications.PermissionsSummaryHelper;
    104 import com.android.settingslib.applications.PermissionsSummaryHelper.PermissionsResultCallback;
    105 import com.android.settingslib.applications.StorageStatsSource;
    106 import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
    107 import com.android.settingslib.net.ChartData;
    108 import com.android.settingslib.net.ChartDataLoader;
    109 
    110 import java.lang.ref.WeakReference;
    111 import java.util.ArrayList;
    112 import java.util.HashSet;
    113 import java.util.List;
    114 import java.util.Set;
    115 
    116 /**
    117  * Activity to display application information from Settings. This activity presents
    118  * extended information associated with a package like code, data, total size, permissions
    119  * used by the application and also the set of default launchable activities.
    120  * For system applications, an option to clear user data is displayed only if data size is > 0.
    121  * System applications that do not want clear user data do not have this option.
    122  * For non-system applications, there is no option to clear data. Instead there is an option to
    123  * uninstall the application.
    124  */
    125 public class InstalledAppDetails extends AppInfoBase
    126         implements View.OnClickListener, OnPreferenceClickListener,
    127         LoaderManager.LoaderCallbacks<AppStorageStats> {
    128 
    129     private static final String LOG_TAG = "InstalledAppDetails";
    130 
    131     // Menu identifiers
    132     public static final int UNINSTALL_ALL_USERS_MENU = 1;
    133     public static final int UNINSTALL_UPDATES = 2;
    134 
    135     // Result code identifiers
    136     public static final int REQUEST_UNINSTALL = 0;
    137     private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
    138 
    139     private static final int SUB_INFO_FRAGMENT = 1;
    140 
    141     private static final int LOADER_CHART_DATA = 2;
    142     private static final int LOADER_STORAGE = 3;
    143     @VisibleForTesting
    144     static final int LOADER_BATTERY = 4;
    145 
    146     private static final int DLG_FORCE_STOP = DLG_BASE + 1;
    147     private static final int DLG_DISABLE = DLG_BASE + 2;
    148     private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
    149     private static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
    150     private static final String KEY_HEADER = "header_view";
    151     private static final String KEY_INSTANT_APP_BUTTONS = "instant_app_buttons";
    152     private static final String KEY_ACTION_BUTTONS = "action_buttons";
    153     private static final String KEY_NOTIFICATION = "notification_settings";
    154     private static final String KEY_STORAGE = "storage_settings";
    155     private static final String KEY_PERMISSION = "permission_settings";
    156     private static final String KEY_DATA = "data_settings";
    157     private static final String KEY_LAUNCH = "preferred_settings";
    158     private static final String KEY_BATTERY = "battery";
    159     private static final String KEY_MEMORY = "memory";
    160     private static final String KEY_VERSION = "app_version";
    161     private static final String KEY_INSTANT_APP_SUPPORTED_LINKS =
    162             "instant_app_launch_supported_domain_urls";
    163 
    164     private final HashSet<String> mHomePackages = new HashSet<>();
    165 
    166     private boolean mInitialized;
    167     private boolean mShowUninstalled;
    168     private LayoutPreference mHeader;
    169     private LayoutPreference mActionButtons;
    170     private Button mUninstallButton;
    171     private boolean mUpdatedSysApp = false;
    172     private Button mForceStopButton;
    173     private Preference mNotificationPreference;
    174     private Preference mStoragePreference;
    175     private Preference mPermissionsPreference;
    176     private Preference mLaunchPreference;
    177     private Preference mDataPreference;
    178     private Preference mMemoryPreference;
    179     private Preference mVersionPreference;
    180     private AppDomainsPreference mInstantAppDomainsPreference;
    181     private DevelopmentSettingsEnabler mDevelopmentSettingsEnabler;
    182     private boolean mDisableAfterUninstall;
    183 
    184     // Used for updating notification preference.
    185     private final NotificationBackend mBackend = new NotificationBackend();
    186 
    187     private ChartData mChartData;
    188     private INetworkStatsSession mStatsSession;
    189 
    190     @VisibleForTesting
    191     Preference mBatteryPreference;
    192     @VisibleForTesting
    193     BatterySipper mSipper;
    194     @VisibleForTesting
    195     BatteryStatsHelper mBatteryHelper;
    196     @VisibleForTesting
    197     BatteryUtils mBatteryUtils;
    198 
    199     protected ProcStatsData mStatsManager;
    200     protected ProcStatsPackageEntry mStats;
    201 
    202     private InstantAppButtonsController mInstantAppButtonsController;
    203 
    204     private AppStorageStats mLastResult;
    205     private String mBatteryPercent;
    206 
    207     @VisibleForTesting
    208     final LoaderCallbacks<BatteryStatsHelper> mBatteryCallbacks =
    209             new LoaderCallbacks<BatteryStatsHelper>() {
    210 
    211                 @Override
    212                 public Loader<BatteryStatsHelper> onCreateLoader(int id, Bundle args) {
    213                     return new BatteryStatsHelperLoader(getContext());
    214                 }
    215 
    216                 @Override
    217                 public void onLoadFinished(Loader<BatteryStatsHelper> loader,
    218                         BatteryStatsHelper batteryHelper) {
    219                     mBatteryHelper = batteryHelper;
    220                     if (mPackageInfo != null) {
    221                         mSipper = findTargetSipper(batteryHelper, mPackageInfo.applicationInfo.uid);
    222                         if (getActivity() != null) {
    223                             updateBattery();
    224                         }
    225                     }
    226                 }
    227 
    228                 @Override
    229                 public void onLoaderReset(Loader<BatteryStatsHelper> loader) {
    230                 }
    231             };
    232 
    233     @VisibleForTesting
    234     boolean handleDisableable(Button button) {
    235         boolean disableable = false;
    236         // Try to prevent the user from bricking their phone
    237         // by not allowing disabling of apps signed with the
    238         // system cert and any launcher app in the system.
    239         if (mHomePackages.contains(mAppEntry.info.packageName)
    240                 || Utils.isSystemPackage(getContext().getResources(), mPm, mPackageInfo)) {
    241             // Disable button for core system applications.
    242             button.setText(R.string.disable_text);
    243         } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
    244             button.setText(R.string.disable_text);
    245             disableable = !mApplicationFeatureProvider.getKeepEnabledPackages()
    246                     .contains(mAppEntry.info.packageName);
    247         } else {
    248             button.setText(R.string.enable_text);
    249             disableable = true;
    250         }
    251 
    252         return disableable;
    253     }
    254 
    255     private boolean isDisabledUntilUsed() {
    256         return mAppEntry.info.enabledSetting
    257                 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
    258     }
    259 
    260     private void initUninstallButtons() {
    261         final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    262         boolean enabled = true;
    263         if (isBundled) {
    264             enabled = handleDisableable(mUninstallButton);
    265         } else {
    266             enabled = initUnintsallButtonForUserApp();
    267         }
    268         // If this is a device admin, it can't be uninstalled or disabled.
    269         // We do this here so the text of the button is still set correctly.
    270         if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    271             enabled = false;
    272         }
    273 
    274         // We don't allow uninstalling DO/PO on *any* users, because if it's a system app,
    275         // "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
    276         // will clear data on all users.
    277         if (Utils.isProfileOrDeviceOwner(mUserManager, mDpm, mPackageInfo.packageName)) {
    278             enabled = false;
    279         }
    280 
    281         // Don't allow uninstalling the device provisioning package.
    282         if (Utils.isDeviceProvisioningPackage(getResources(), mAppEntry.info.packageName)) {
    283             enabled = false;
    284         }
    285 
    286         // If the uninstall intent is already queued, disable the uninstall button
    287         if (mDpm.isUninstallInQueue(mPackageName)) {
    288             enabled = false;
    289         }
    290 
    291         // Home apps need special handling.  Bundled ones we don't risk downgrading
    292         // because that can interfere with home-key resolution.  Furthermore, we
    293         // can't allow uninstallation of the only home app, and we don't want to
    294         // allow uninstallation of an explicitly preferred one -- the user can go
    295         // to Home settings and pick a different one, after which we'll permit
    296         // uninstallation of the now-not-default one.
    297         if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
    298             if (isBundled) {
    299                 enabled = false;
    300             } else {
    301                 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
    302                 ComponentName currentDefaultHome  = mPm.getHomeActivities(homeActivities);
    303                 if (currentDefaultHome == null) {
    304                     // No preferred default, so permit uninstall only when
    305                     // there is more than one candidate
    306                     enabled = (mHomePackages.size() > 1);
    307                 } else {
    308                     // There is an explicit default home app -- forbid uninstall of
    309                     // that one, but permit it for installed-but-inactive ones.
    310                     enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
    311                 }
    312             }
    313         }
    314 
    315         if (mAppsControlDisallowedBySystem) {
    316             enabled = false;
    317         }
    318 
    319         try {
    320             IWebViewUpdateService webviewUpdateService =
    321                 IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
    322             if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) {
    323                 enabled = false;
    324             }
    325         } catch (RemoteException e) {
    326             throw new RuntimeException(e);
    327         }
    328 
    329         mUninstallButton.setEnabled(enabled);
    330         if (enabled) {
    331             // Register listener
    332             mUninstallButton.setOnClickListener(this);
    333         }
    334     }
    335 
    336     @VisibleForTesting
    337     boolean initUnintsallButtonForUserApp() {
    338         boolean enabled = true;
    339         if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
    340                 && mUserManager.getUsers().size() >= 2) {
    341             // When we have multiple users, there is a separate menu
    342             // to uninstall for all users.
    343             enabled = false;
    344         } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
    345             enabled = false;
    346             mUninstallButton.setVisibility(View.GONE);
    347         }
    348         mUninstallButton.setText(R.string.uninstall_text);
    349         return enabled;
    350     }
    351 
    352     /** Called when the activity is first created. */
    353     @Override
    354     public void onCreate(Bundle icicle) {
    355         super.onCreate(icicle);
    356         final Activity activity = getActivity();
    357 
    358         if (!ensurePackageInfoAvailable(activity)) {
    359             return;
    360         }
    361 
    362         setHasOptionsMenu(true);
    363         addPreferencesFromResource(R.xml.installed_app_details);
    364 
    365         addDynamicPrefs();
    366         if (Utils.isBandwidthControlEnabled()) {
    367             INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
    368                     ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
    369             try {
    370                 mStatsSession = statsService.openSession();
    371             } catch (RemoteException e) {
    372                 throw new RuntimeException(e);
    373             }
    374         } else {
    375             removePreference(KEY_DATA);
    376         }
    377         mBatteryUtils = BatteryUtils.getInstance(getContext());
    378         mDevelopmentSettingsEnabler = new DevelopmentSettingsEnabler(
    379                 activity, null /* lifecycle */);
    380     }
    381 
    382     @Override
    383     public int getMetricsCategory() {
    384         return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS;
    385     }
    386 
    387     @Override
    388     public void onResume() {
    389         super.onResume();
    390         if (mFinishing) {
    391             return;
    392         }
    393         AppItem app = new AppItem(mAppEntry.info.uid);
    394         app.addUid(mAppEntry.info.uid);
    395         if (mStatsSession != null) {
    396             LoaderManager loaderManager = getLoaderManager();
    397             loaderManager.restartLoader(LOADER_CHART_DATA,
    398                     ChartDataLoader.buildArgs(getTemplate(getContext()), app),
    399                     mDataCallbacks);
    400             loaderManager.restartLoader(LOADER_STORAGE, Bundle.EMPTY, this);
    401         }
    402         restartBatteryStatsLoader();
    403         if (mDevelopmentSettingsEnabler.getLastEnabledState()) {
    404             new MemoryUpdater().execute();
    405         }
    406         updateDynamicPrefs();
    407     }
    408 
    409     @VisibleForTesting
    410     public void restartBatteryStatsLoader() {
    411         getLoaderManager().restartLoader(LOADER_BATTERY, Bundle.EMPTY, mBatteryCallbacks);
    412     }
    413 
    414     @Override
    415     public void onPause() {
    416         getLoaderManager().destroyLoader(LOADER_CHART_DATA);
    417         super.onPause();
    418     }
    419 
    420     @Override
    421     public void onDestroy() {
    422         TrafficStats.closeQuietly(mStatsSession);
    423         super.onDestroy();
    424     }
    425 
    426     public void onActivityCreated(Bundle savedInstanceState) {
    427         super.onActivityCreated(savedInstanceState);
    428         if (mFinishing) {
    429             return;
    430         }
    431         final Activity activity = getActivity();
    432         mHeader = (LayoutPreference) findPreference(KEY_HEADER);
    433         mActionButtons = (LayoutPreference) findPreference(KEY_ACTION_BUTTONS);
    434         EntityHeaderController.newInstance(activity, this, mHeader.findViewById(R.id.entity_header))
    435                 .setRecyclerView(getListView(), getLifecycle())
    436                 .setPackageName(mPackageName)
    437                 .setHasAppInfoLink(false)
    438                 .setButtonActions(EntityHeaderController.ActionType.ACTION_APP_PREFERENCE,
    439                         EntityHeaderController.ActionType.ACTION_NONE)
    440                 .styleActionBar(activity)
    441                 .bindHeaderButtons();
    442         prepareUninstallAndStop();
    443 
    444         mNotificationPreference = findPreference(KEY_NOTIFICATION);
    445         mNotificationPreference.setOnPreferenceClickListener(this);
    446         mStoragePreference = findPreference(KEY_STORAGE);
    447         mStoragePreference.setOnPreferenceClickListener(this);
    448         mPermissionsPreference = findPreference(KEY_PERMISSION);
    449         mPermissionsPreference.setOnPreferenceClickListener(this);
    450         mDataPreference = findPreference(KEY_DATA);
    451         if (mDataPreference != null) {
    452             mDataPreference.setOnPreferenceClickListener(this);
    453         }
    454         mBatteryPreference = findPreference(KEY_BATTERY);
    455         mBatteryPreference.setEnabled(false);
    456         mBatteryPreference.setOnPreferenceClickListener(this);
    457         mMemoryPreference = findPreference(KEY_MEMORY);
    458         mMemoryPreference.setOnPreferenceClickListener(this);
    459         mMemoryPreference.setVisible(mDevelopmentSettingsEnabler.getLastEnabledState());
    460         mVersionPreference = findPreference(KEY_VERSION);
    461         mInstantAppDomainsPreference =
    462                 (AppDomainsPreference) findPreference(KEY_INSTANT_APP_SUPPORTED_LINKS);
    463         mLaunchPreference = findPreference(KEY_LAUNCH);
    464         if (mAppEntry != null && mAppEntry.info != null) {
    465             if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 ||
    466                     !mAppEntry.info.enabled) {
    467                 mLaunchPreference.setEnabled(false);
    468             } else {
    469                 mLaunchPreference.setOnPreferenceClickListener(this);
    470             }
    471         } else {
    472             mLaunchPreference.setEnabled(false);
    473         }
    474     }
    475 
    476     @Override
    477     public void onPackageSizeChanged(String packageName) {
    478         if (!TextUtils.equals(packageName, mPackageName)) {
    479             Log.d(LOG_TAG, "Package change irrelevant, skipping");
    480           return;
    481         }
    482         refreshUi();
    483     }
    484 
    485     /**
    486      * Ensures the {@link PackageInfo} is available to proceed. If it's not available, the fragment
    487      * will finish.
    488      *
    489      * @return true if packageInfo is available.
    490      */
    491     @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
    492     boolean ensurePackageInfoAvailable(Activity activity) {
    493         if (mPackageInfo == null) {
    494             mFinishing = true;
    495             Log.w(LOG_TAG, "Package info not available. Is this package already uninstalled?");
    496             activity.finishAndRemoveTask();
    497             return false;
    498         }
    499         return true;
    500     }
    501 
    502     private void prepareUninstallAndStop() {
    503         mForceStopButton = (Button) mActionButtons.findViewById(R.id.right_button);
    504         mForceStopButton.setText(R.string.force_stop);
    505         mUninstallButton = (Button) mActionButtons.findViewById(R.id.left_button);
    506         mForceStopButton.setEnabled(false);
    507     }
    508 
    509     @Override
    510     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    511         menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
    512                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    513         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
    514                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    515     }
    516 
    517     @Override
    518     public void onPrepareOptionsMenu(Menu menu) {
    519         if (mFinishing) {
    520             return;
    521         }
    522         menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(shouldShowUninstallForAll(mAppEntry));
    523         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
    524         MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
    525         uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem);
    526         if (uninstallUpdatesItem.isVisible()) {
    527             RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(),
    528                     uninstallUpdatesItem, mAppsControlDisallowedAdmin);
    529         }
    530     }
    531 
    532     @Override
    533     public boolean onOptionsItemSelected(MenuItem item) {
    534         switch (item.getItemId()) {
    535             case UNINSTALL_ALL_USERS_MENU:
    536                 uninstallPkg(mAppEntry.info.packageName, true, false);
    537                 return true;
    538             case UNINSTALL_UPDATES:
    539                 uninstallPkg(mAppEntry.info.packageName, false, false);
    540                 return true;
    541         }
    542         return false;
    543     }
    544 
    545     @Override
    546     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    547         super.onActivityResult(requestCode, resultCode, data);
    548         switch (requestCode) {
    549             case REQUEST_UNINSTALL:
    550                 // Refresh option menu
    551                 getActivity().invalidateOptionsMenu();
    552 
    553                 if (mDisableAfterUninstall) {
    554                     mDisableAfterUninstall = false;
    555                     new DisableChanger(this, mAppEntry.info,
    556                             PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
    557                             .execute((Object)null);
    558                 }
    559                 // continue with following operations
    560             case REQUEST_REMOVE_DEVICE_ADMIN:
    561                 if (!refreshUi()) {
    562                     setIntentAndFinish(true, true);
    563                 } else {
    564                     startListeningToPackageRemove();
    565                 }
    566                 break;
    567         }
    568     }
    569 
    570     @Override
    571     public Loader<AppStorageStats> onCreateLoader(int id, Bundle args) {
    572         Context context = getContext();
    573         return new FetchPackageStorageAsyncLoader(
    574                 context, new StorageStatsSource(context), mAppEntry.info, UserHandle.of(mUserId));
    575     }
    576 
    577     @Override
    578     public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
    579         mLastResult = result;
    580         refreshUi();
    581     }
    582 
    583     @Override
    584     public void onLoaderReset(Loader<AppStorageStats> loader) {
    585     }
    586 
    587     /**
    588      * Utility method to hide and show specific preferences based on whether the app being displayed
    589      * is an Instant App or an installed app.
    590      */
    591     @VisibleForTesting
    592     void prepareInstantAppPrefs() {
    593         final boolean isInstant = AppUtils.isInstant(mPackageInfo.applicationInfo);
    594         if (isInstant) {
    595             Set<String> handledDomainSet = Utils.getHandledDomains(mPm, mPackageInfo.packageName);
    596             String[] handledDomains = handledDomainSet.toArray(new String[handledDomainSet.size()]);
    597             mInstantAppDomainsPreference.setTitles(handledDomains);
    598             // Dummy values, unused in the implementation
    599             mInstantAppDomainsPreference.setValues(new int[handledDomains.length]);
    600             getPreferenceScreen().removePreference(mLaunchPreference);
    601         } else {
    602             getPreferenceScreen().removePreference(mInstantAppDomainsPreference);
    603         }
    604     }
    605 
    606     // Utility method to set application label and icon.
    607     private void setAppLabelAndIcon(PackageInfo pkgInfo) {
    608         final View appSnippet = mHeader.findViewById(R.id.entity_header);
    609         mState.ensureIcon(mAppEntry);
    610         final Activity activity = getActivity();
    611         final boolean isInstantApp = AppUtils.isInstant(mPackageInfo.applicationInfo);
    612         final CharSequence summary =
    613                 isInstantApp ? null : getString(Utils.getInstallationStatus(mAppEntry.info));
    614         EntityHeaderController.newInstance(activity, this, appSnippet)
    615                 .setLabel(mAppEntry)
    616                 .setIcon(mAppEntry)
    617                 .setSummary(summary)
    618                 .setIsInstantApp(isInstantApp)
    619                 .done(activity, false /* rebindActions */);
    620         mVersionPreference.setSummary(getString(R.string.version_text,
    621                 BidiFormatter.getInstance().unicodeWrap(pkgInfo.versionName)));
    622     }
    623 
    624     @VisibleForTesting
    625     boolean shouldShowUninstallForAll(ApplicationsState.AppEntry appEntry) {
    626         boolean showIt = true;
    627         if (mUpdatedSysApp) {
    628             showIt = false;
    629         } else if (appEntry == null) {
    630             showIt = false;
    631         } else if ((appEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    632             showIt = false;
    633         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    634             showIt = false;
    635         } else if (UserHandle.myUserId() != 0) {
    636             showIt = false;
    637         } else if (mUserManager.getUsers().size() < 2) {
    638             showIt = false;
    639         } else if (PackageUtil.countPackageInUsers(mPm, mUserManager, mPackageName) < 2
    640                 && (appEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
    641             showIt = false;
    642         } else if (AppUtils.isInstant(appEntry.info)) {
    643             showIt = false;
    644         }
    645         return showIt;
    646     }
    647 
    648     @VisibleForTesting
    649     BatterySipper findTargetSipper(BatteryStatsHelper batteryHelper, int uid) {
    650         List<BatterySipper> usageList = batteryHelper.getUsageList();
    651         for (int i = 0, size = usageList.size(); i < size; i++) {
    652             BatterySipper sipper = usageList.get(i);
    653             if (sipper.getUid() == uid) {
    654                 return sipper;
    655             }
    656         }
    657 
    658         return null;
    659     }
    660 
    661     private boolean signaturesMatch(String pkg1, String pkg2) {
    662         if (pkg1 != null && pkg2 != null) {
    663             try {
    664                 final int match = mPm.checkSignatures(pkg1, pkg2);
    665                 if (match >= PackageManager.SIGNATURE_MATCH) {
    666                     return true;
    667                 }
    668             } catch (Exception e) {
    669                 // e.g. named alternate package not found during lookup;
    670                 // this is an expected case sometimes
    671             }
    672         }
    673         return false;
    674     }
    675 
    676     @Override
    677     protected boolean refreshUi() {
    678         retrieveAppEntry();
    679         if (mAppEntry == null) {
    680             return false; // onCreate must have failed, make sure to exit
    681         }
    682 
    683         if (mPackageInfo == null) {
    684             return false; // onCreate must have failed, make sure to exit
    685         }
    686 
    687         // Get list of "home" apps and trace through any meta-data references
    688         List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
    689         mPm.getHomeActivities(homeActivities);
    690         mHomePackages.clear();
    691         for (int i = 0; i< homeActivities.size(); i++) {
    692             ResolveInfo ri = homeActivities.get(i);
    693             final String activityPkg = ri.activityInfo.packageName;
    694             mHomePackages.add(activityPkg);
    695 
    696             // Also make sure to include anything proxying for the home app
    697             final Bundle metadata = ri.activityInfo.metaData;
    698             if (metadata != null) {
    699                 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
    700                 if (signaturesMatch(metaPkg, activityPkg)) {
    701                     mHomePackages.add(metaPkg);
    702                 }
    703             }
    704         }
    705 
    706         checkForceStop();
    707         setAppLabelAndIcon(mPackageInfo);
    708         initUninstallButtons();
    709         prepareInstantAppPrefs();
    710 
    711         // Update the preference summaries.
    712         Activity context = getActivity();
    713         boolean isExternal = ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0);
    714         mStoragePreference.setSummary(getStorageSummary(context, mLastResult, isExternal));
    715 
    716         PermissionsSummaryHelper.getPermissionSummary(getContext(),
    717                 mPackageName, mPermissionCallback);
    718         mLaunchPreference.setSummary(AppUtils.getLaunchByDefaultSummary(mAppEntry, mUsbManager,
    719                 mPm, context));
    720         mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context,
    721                 mBackend));
    722         if (mDataPreference != null) {
    723             mDataPreference.setSummary(getDataSummary());
    724         }
    725 
    726         if (!mInitialized) {
    727             // First time init: are we displaying an uninstalled app?
    728             mInitialized = true;
    729             mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
    730         } else {
    731             // All other times: if the app no longer exists then we want
    732             // to go away.
    733             try {
    734                 ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo(
    735                         mAppEntry.info.packageName,
    736                         PackageManager.MATCH_DISABLED_COMPONENTS
    737                         | PackageManager.MATCH_ANY_USER);
    738                 if (!mShowUninstalled) {
    739                     // If we did not start out with the app uninstalled, then
    740                     // it transitioning to the uninstalled state for the current
    741                     // user means we should go away as well.
    742                     return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
    743                 }
    744             } catch (NameNotFoundException e) {
    745                 return false;
    746             }
    747         }
    748 
    749         return true;
    750     }
    751 
    752     @VisibleForTesting
    753     void updateBattery() {
    754         mBatteryPreference.setEnabled(true);
    755         if (isBatteryStatsAvailable()) {
    756             final int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
    757                     BatteryStats.STATS_SINCE_CHARGED);
    758 
    759             final List<BatterySipper> usageList = new ArrayList<>(mBatteryHelper.getUsageList());
    760             final double hiddenAmount = mBatteryUtils.removeHiddenBatterySippers(usageList);
    761             final int percentOfMax = (int) mBatteryUtils.calculateBatteryPercent(
    762                     mSipper.totalPowerMah, mBatteryHelper.getTotalPower(), hiddenAmount,
    763                     dischargeAmount);
    764             mBatteryPercent = Utils.formatPercentage(percentOfMax);
    765             mBatteryPreference.setSummary(getString(R.string.battery_summary, mBatteryPercent));
    766         } else {
    767             mBatteryPreference.setSummary(getString(R.string.no_battery_summary));
    768         }
    769     }
    770 
    771     private CharSequence getDataSummary() {
    772         if (mChartData != null) {
    773             long totalBytes = mChartData.detail.getTotalBytes();
    774             if (totalBytes == 0) {
    775                 return getString(R.string.no_data_usage);
    776             }
    777             Context context = getActivity();
    778             return getString(R.string.data_summary_format,
    779                     Formatter.formatFileSize(context, totalBytes),
    780                     DateUtils.formatDateTime(context, mChartData.detail.getStart(),
    781                             DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH));
    782         }
    783         return getString(R.string.computing_size);
    784     }
    785 
    786     @VisibleForTesting
    787     static CharSequence getStorageSummary(
    788             Context context, AppStorageStats stats, boolean isExternal) {
    789         if (stats == null) {
    790             return context.getText(R.string.computing_size);
    791         } else {
    792             CharSequence storageType = context.getString(isExternal
    793                     ? R.string.storage_type_external
    794                     : R.string.storage_type_internal);
    795             return context.getString(R.string.storage_summary_format,
    796                     getSize(context, stats), storageType.toString().toLowerCase());
    797         }
    798     }
    799 
    800     @VisibleForTesting
    801     boolean isBatteryStatsAvailable() {
    802         return mBatteryHelper != null && mSipper != null;
    803     }
    804 
    805     private static CharSequence getSize(Context context, AppStorageStats stats) {
    806         return Formatter.formatFileSize(context, stats.getTotalBytes());
    807     }
    808 
    809 
    810     @Override
    811     protected AlertDialog createDialog(int id, int errorCode) {
    812         switch (id) {
    813             case DLG_DISABLE:
    814                 return new AlertDialog.Builder(getActivity())
    815                         .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
    816                         .setPositiveButton(R.string.app_disable_dlg_positive,
    817                                 new DialogInterface.OnClickListener() {
    818                             public void onClick(DialogInterface dialog, int which) {
    819                                 // Disable the app
    820                                 mMetricsFeatureProvider.action(getContext(),
    821                                         MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
    822                                 new DisableChanger(InstalledAppDetails.this, mAppEntry.info,
    823                                         PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
    824                                 .execute((Object)null);
    825                             }
    826                         })
    827                         .setNegativeButton(R.string.dlg_cancel, null)
    828                         .create();
    829             case DLG_SPECIAL_DISABLE:
    830                 return new AlertDialog.Builder(getActivity())
    831                         .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
    832                         .setPositiveButton(R.string.app_disable_dlg_positive,
    833                                 new DialogInterface.OnClickListener() {
    834                             public void onClick(DialogInterface dialog, int which) {
    835                                 // Disable the app and ask for uninstall
    836                                 mMetricsFeatureProvider.action(getContext(),
    837                                         MetricsEvent.ACTION_SETTINGS_DISABLE_APP);
    838                                 uninstallPkg(mAppEntry.info.packageName,
    839                                         false, true);
    840                             }
    841                         })
    842                         .setNegativeButton(R.string.dlg_cancel, null)
    843                         .create();
    844             case DLG_FORCE_STOP:
    845                 return new AlertDialog.Builder(getActivity())
    846                         .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
    847                         .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
    848                         .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    849                             public void onClick(DialogInterface dialog, int which) {
    850                                 // Force stop
    851                                 forceStopPackage(mAppEntry.info.packageName);
    852                             }
    853                         })
    854                         .setNegativeButton(R.string.dlg_cancel, null)
    855                         .create();
    856         }
    857         if (mInstantAppButtonsController != null) {
    858             return mInstantAppButtonsController.createDialog(id);
    859         }
    860         return null;
    861     }
    862 
    863     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
    864         stopListeningToPackageRemove();
    865          // Create new intent to launch Uninstaller activity
    866         Uri packageURI = Uri.parse("package:"+packageName);
    867         Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
    868         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
    869         mMetricsFeatureProvider.action(
    870                 getContext(), MetricsEvent.ACTION_SETTINGS_UNINSTALL_APP);
    871         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
    872         mDisableAfterUninstall = andDisable;
    873     }
    874 
    875     private void forceStopPackage(String pkgName) {
    876         mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_APP_FORCE_STOP, pkgName);
    877         ActivityManager am = (ActivityManager) getActivity().getSystemService(
    878                 Context.ACTIVITY_SERVICE);
    879         Log.d(LOG_TAG, "Stopping package " + pkgName);
    880         am.forceStopPackage(pkgName);
    881         int userId = UserHandle.getUserId(mAppEntry.info.uid);
    882         mState.invalidatePackage(pkgName, userId);
    883         ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId);
    884         if (newEnt != null) {
    885             mAppEntry = newEnt;
    886         }
    887         checkForceStop();
    888     }
    889 
    890     private void updateForceStopButton(boolean enabled) {
    891         if (mAppsControlDisallowedBySystem) {
    892             mForceStopButton.setEnabled(false);
    893         } else {
    894             mForceStopButton.setEnabled(enabled);
    895             mForceStopButton.setOnClickListener(this);
    896         }
    897     }
    898 
    899     @VisibleForTesting
    900     void checkForceStop() {
    901         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    902             // User can't force stop device admin.
    903             Log.w(LOG_TAG, "User can't force stop device admin");
    904             updateForceStopButton(false);
    905         } else if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
    906             updateForceStopButton(false);
    907             mForceStopButton.setVisibility(View.GONE);
    908         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
    909             // If the app isn't explicitly stopped, then always show the
    910             // force stop button.
    911             Log.w(LOG_TAG, "App is not explicitly stopped");
    912             updateForceStopButton(true);
    913         } else {
    914             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
    915                     Uri.fromParts("package", mAppEntry.info.packageName, null));
    916             intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
    917             intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
    918             intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
    919             Log.d(LOG_TAG, "Sending broadcast to query restart status for "
    920                     + mAppEntry.info.packageName);
    921             getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
    922                     mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
    923         }
    924     }
    925 
    926     private void startManagePermissionsActivity() {
    927         // start new activity to manage app permissions
    928         Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
    929         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
    930         intent.putExtra(EXTRA_HIDE_INFO_BUTTON, true);
    931         try {
    932             getActivity().startActivityForResult(intent, SUB_INFO_FRAGMENT);
    933         } catch (ActivityNotFoundException e) {
    934             Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
    935         }
    936     }
    937 
    938     private void startAppInfoFragment(Class<?> fragment, CharSequence title) {
    939         startAppInfoFragment(fragment, title, this, mAppEntry);
    940     }
    941 
    942     public static void startAppInfoFragment(Class<?> fragment, CharSequence title,
    943             SettingsPreferenceFragment caller, AppEntry appEntry) {
    944         // start new fragment to display extended information
    945         Bundle args = new Bundle();
    946         args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
    947         args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
    948 
    949         SettingsActivity sa = (SettingsActivity) caller.getActivity();
    950         sa.startPreferencePanel(caller, fragment.getName(), args, -1, title, caller,
    951                 SUB_INFO_FRAGMENT);
    952     }
    953 
    954     /*
    955      * Method implementing functionality of buttons clicked
    956      * @see android.view.View.OnClickListener#onClick(android.view.View)
    957      */
    958     public void onClick(View v) {
    959         if (mAppEntry == null) {
    960             setIntentAndFinish(true, true);
    961             return;
    962         }
    963         String packageName = mAppEntry.info.packageName;
    964         if (v == mUninstallButton) {
    965             if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
    966                 stopListeningToPackageRemove();
    967                 Activity activity = getActivity();
    968                 Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class);
    969                 uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
    970                         mPackageName);
    971                 mMetricsFeatureProvider.action(
    972                         activity, MetricsEvent.ACTION_SETTINGS_UNINSTALL_DEVICE_ADMIN);
    973                 activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN);
    974                 return;
    975             }
    976             EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(),
    977                     packageName, mUserId);
    978             boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
    979                     RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId);
    980             if (admin != null && !uninstallBlockedBySystem) {
    981                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin);
    982             } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
    983                 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
    984                     // If the system app has an update and this is the only user on the device,
    985                     // then offer to downgrade the app, otherwise only offer to disable the
    986                     // app for this user.
    987                     if (mUpdatedSysApp && isSingleUser()) {
    988                         showDialogInner(DLG_SPECIAL_DISABLE, 0);
    989                     } else {
    990                         showDialogInner(DLG_DISABLE, 0);
    991                     }
    992                 } else {
    993                     mMetricsFeatureProvider.action(
    994                             getActivity(),
    995                             mAppEntry.info.enabled
    996                                     ? MetricsEvent.ACTION_SETTINGS_DISABLE_APP
    997                                     : MetricsEvent.ACTION_SETTINGS_ENABLE_APP);
    998                     new DisableChanger(this, mAppEntry.info,
    999                             PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
   1000                                     .execute((Object) null);
   1001                 }
   1002             } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
   1003                 uninstallPkg(packageName, true, false);
   1004             } else {
   1005                 uninstallPkg(packageName, false, false);
   1006             }
   1007         } else if (v == mForceStopButton) {
   1008             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
   1009                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
   1010                         getActivity(), mAppsControlDisallowedAdmin);
   1011             } else {
   1012                 showDialogInner(DLG_FORCE_STOP, 0);
   1013                 //forceStopPackage(mAppInfo.packageName);
   1014             }
   1015         }
   1016     }
   1017 
   1018     /** Returns whether there is only one user on this device, not including the system-only user */
   1019     private boolean isSingleUser() {
   1020         final int userCount = mUserManager.getUserCount();
   1021         return userCount == 1
   1022                 || (mUserManager.isSplitSystemUser() && userCount == 2);
   1023     }
   1024 
   1025     @Override
   1026     public boolean onPreferenceClick(Preference preference) {
   1027         if (preference == mStoragePreference) {
   1028             startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle());
   1029         } else if (preference == mNotificationPreference) {
   1030             startAppInfoFragment(AppNotificationSettings.class,
   1031                     getString(R.string.app_notifications_title));
   1032         } else if (preference == mPermissionsPreference) {
   1033             startManagePermissionsActivity();
   1034         } else if (preference == mLaunchPreference) {
   1035             startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle());
   1036         } else if (preference == mMemoryPreference) {
   1037             ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(),
   1038                     mStatsManager.getMemInfo(), mStats, false);
   1039         } else if (preference == mDataPreference) {
   1040             startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage));
   1041         } else if (preference == mBatteryPreference) {
   1042             if (isBatteryStatsAvailable()) {
   1043                 BatteryEntry entry = new BatteryEntry(getContext(), null, mUserManager, mSipper);
   1044                 entry.defaultPackageName = mPackageName;
   1045                 AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
   1046                         this, mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry,
   1047                         mBatteryPercent, null /* mAnomalies */);
   1048             } else {
   1049                 AdvancedPowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
   1050                         this, mPackageName);
   1051             }
   1052         } else {
   1053             return false;
   1054         }
   1055         return true;
   1056     }
   1057 
   1058     private void addDynamicPrefs() {
   1059         if (UserManager.get(getContext()).isManagedProfile()) {
   1060             return;
   1061         }
   1062         final PreferenceScreen screen = getPreferenceScreen();
   1063         final Context context = getContext();
   1064         if (DefaultHomePreferenceController.hasHomePreference(mPackageName, context)) {
   1065             screen.addPreference(new ShortcutPreference(getPrefContext(),
   1066                     DefaultAppSettings.class, "default_home", R.string.home_app,
   1067                     R.string.configure_apps));
   1068         }
   1069         if (DefaultBrowserPreferenceController.hasBrowserPreference(mPackageName, context)) {
   1070             screen.addPreference(new ShortcutPreference(getPrefContext(),
   1071                     DefaultAppSettings.class, "default_browser", R.string.default_browser_title,
   1072                     R.string.configure_apps));
   1073         }
   1074         if (DefaultPhonePreferenceController.hasPhonePreference(mPackageName, context)) {
   1075             screen.addPreference(new ShortcutPreference(getPrefContext(),
   1076                     DefaultAppSettings.class, "default_phone_app", R.string.default_phone_title,
   1077                     R.string.configure_apps));
   1078         }
   1079         if (DefaultEmergencyPreferenceController.hasEmergencyPreference(mPackageName, context)) {
   1080             screen.addPreference(new ShortcutPreference(getPrefContext(),
   1081                     DefaultAppSettings.class, "default_emergency_app",
   1082                     R.string.default_emergency_app, R.string.configure_apps));
   1083         }
   1084         if (DefaultSmsPreferenceController.hasSmsPreference(mPackageName, context)) {
   1085             screen.addPreference(new ShortcutPreference(getPrefContext(),
   1086                     DefaultAppSettings.class, "default_sms_app", R.string.sms_application_title,
   1087                     R.string.configure_apps));
   1088         }
   1089 
   1090         // Get the package info with the activities
   1091         PackageInfo packageInfoWithActivities = null;
   1092         try {
   1093             packageInfoWithActivities = mPm.getPackageInfoAsUser(mPackageName,
   1094                     PackageManager.GET_ACTIVITIES, UserHandle.myUserId());
   1095         } catch (NameNotFoundException e) {
   1096             Log.e(TAG, "Exception while retrieving the package info of " + mPackageName, e);
   1097         }
   1098 
   1099         boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW);
   1100         boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS);
   1101         boolean hasPictureInPictureActivities = (packageInfoWithActivities != null) &&
   1102                 PictureInPictureSettings.checkPackageHasPictureInPictureActivities(
   1103                         packageInfoWithActivities.packageName,
   1104                         packageInfoWithActivities.activities);
   1105         boolean isPotentialAppSource = isPotentialAppSource();
   1106         if (hasDrawOverOtherApps || hasWriteSettings || hasPictureInPictureActivities ||
   1107                 isPotentialAppSource) {
   1108             PreferenceCategory category = new PreferenceCategory(getPrefContext());
   1109             category.setTitle(R.string.advanced_apps);
   1110             screen.addPreference(category);
   1111 
   1112             if (hasDrawOverOtherApps) {
   1113                 Preference pref = new Preference(getPrefContext());
   1114                 pref.setTitle(R.string.draw_overlay);
   1115                 pref.setKey("system_alert_window");
   1116                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
   1117                     @Override
   1118                     public boolean onPreferenceClick(Preference preference) {
   1119                         startAppInfoFragment(DrawOverlayDetails.class,
   1120                                 getString(R.string.draw_overlay));
   1121                         return true;
   1122                     }
   1123                 });
   1124                 category.addPreference(pref);
   1125             }
   1126             if (hasWriteSettings) {
   1127                 Preference pref = new Preference(getPrefContext());
   1128                 pref.setTitle(R.string.write_settings);
   1129                 pref.setKey("write_settings_apps");
   1130                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
   1131                     @Override
   1132                     public boolean onPreferenceClick(Preference preference) {
   1133                         startAppInfoFragment(WriteSettingsDetails.class,
   1134                                 getString(R.string.write_settings));
   1135                         return true;
   1136                     }
   1137                 });
   1138                 category.addPreference(pref);
   1139             }
   1140             if (hasPictureInPictureActivities) {
   1141                 Preference pref = new Preference(getPrefContext());
   1142                 pref.setTitle(R.string.picture_in_picture_app_detail_title);
   1143                 pref.setKey("picture_in_picture");
   1144                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
   1145                     @Override
   1146                     public boolean onPreferenceClick(Preference preference) {
   1147                         AppInfoBase.startAppInfoFragment(PictureInPictureDetails.class,
   1148                                 R.string.picture_in_picture_app_detail_title, mPackageName,
   1149                                 mPackageInfo.applicationInfo.uid, InstalledAppDetails.this,
   1150                                 -1, getMetricsCategory());
   1151                         return true;
   1152                     }
   1153                 });
   1154                 category.addPreference(pref);
   1155             }
   1156             if (isPotentialAppSource) {
   1157                 Preference pref = new Preference(getPrefContext());
   1158                 pref.setTitle(R.string.install_other_apps);
   1159                 pref.setKey("install_other_apps");
   1160                 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
   1161                     @Override
   1162                     public boolean onPreferenceClick(Preference preference) {
   1163                         startAppInfoFragment(ExternalSourcesDetails.class,
   1164                                 getString(R.string.install_other_apps));
   1165                         return true;
   1166                     }
   1167                 });
   1168                 category.addPreference(pref);
   1169             }
   1170         }
   1171 
   1172         addAppInstallerInfoPref(screen);
   1173         maybeAddInstantAppButtons();
   1174     }
   1175 
   1176     private boolean isPotentialAppSource() {
   1177         AppStateInstallAppsBridge.InstallAppsState appState =
   1178                 new AppStateInstallAppsBridge(getContext(), null, null)
   1179                         .createInstallAppsStateFor(mPackageName, mPackageInfo.applicationInfo.uid);
   1180         return appState.isPotentialAppSource();
   1181     }
   1182 
   1183     private void addAppInstallerInfoPref(PreferenceScreen screen) {
   1184         String installerPackageName =
   1185                 AppStoreUtil.getInstallerPackageName(getContext(), mPackageName);
   1186 
   1187         final CharSequence installerLabel = Utils.getApplicationLabel(getContext(),
   1188                 installerPackageName);
   1189         if (installerLabel == null) {
   1190             return;
   1191         }
   1192         final int detailsStringId = AppUtils.isInstant(mPackageInfo.applicationInfo)
   1193                 ? R.string.instant_app_details_summary
   1194                 : R.string.app_install_details_summary;
   1195         PreferenceCategory category = new PreferenceCategory(getPrefContext());
   1196         category.setTitle(R.string.app_install_details_group_title);
   1197         screen.addPreference(category);
   1198         Preference pref = new Preference(getPrefContext());
   1199         pref.setTitle(R.string.app_install_details_title);
   1200         pref.setKey("app_info_store");
   1201         pref.setSummary(getString(detailsStringId, installerLabel));
   1202 
   1203         Intent intent =
   1204                 AppStoreUtil.getAppStoreLink(getContext(), installerPackageName, mPackageName);
   1205         if (intent != null) {
   1206             pref.setIntent(intent);
   1207         } else {
   1208             pref.setEnabled(false);
   1209         }
   1210         category.addPreference(pref);
   1211     }
   1212 
   1213     @VisibleForTesting
   1214     void maybeAddInstantAppButtons() {
   1215         if (AppUtils.isInstant(mPackageInfo.applicationInfo)) {
   1216             LayoutPreference buttons = (LayoutPreference) findPreference(KEY_INSTANT_APP_BUTTONS);
   1217             mInstantAppButtonsController = mApplicationFeatureProvider
   1218                     .newInstantAppButtonsController(this,
   1219                             buttons.findViewById(R.id.instant_app_button_container),
   1220                             id -> showDialogInner(id, 0))
   1221                     .setPackageName(mPackageName)
   1222                     .show();
   1223         }
   1224     }
   1225 
   1226     private boolean hasPermission(String permission) {
   1227         if (mPackageInfo == null || mPackageInfo.requestedPermissions == null) {
   1228             return false;
   1229         }
   1230         for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) {
   1231             if (mPackageInfo.requestedPermissions[i].equals(permission)) {
   1232                 return true;
   1233             }
   1234         }
   1235         return false;
   1236     }
   1237 
   1238     private void updateDynamicPrefs() {
   1239         final Context context = getContext();
   1240         Preference pref = findPreference("default_home");
   1241 
   1242         if (pref != null) {
   1243             pref.setSummary(DefaultHomePreferenceController.isHomeDefault(mPackageName,
   1244                     new PackageManagerWrapperImpl(context.getPackageManager()))
   1245                     ? R.string.yes : R.string.no);
   1246         }
   1247         pref = findPreference("default_browser");
   1248         if (pref != null) {
   1249             pref.setSummary(new DefaultBrowserPreferenceController(context)
   1250                     .isBrowserDefault(mPackageName, mUserId)
   1251                     ? R.string.yes : R.string.no);
   1252         }
   1253         pref = findPreference("default_phone_app");
   1254         if (pref != null) {
   1255             pref.setSummary(
   1256                     DefaultPhonePreferenceController.isPhoneDefault(mPackageName, context)
   1257                     ? R.string.yes : R.string.no);
   1258         }
   1259         pref = findPreference("default_emergency_app");
   1260         if (pref != null) {
   1261             pref.setSummary(DefaultEmergencyPreferenceController.isEmergencyDefault(mPackageName,
   1262                     getContext()) ? R.string.yes : R.string.no);
   1263         }
   1264         pref = findPreference("default_sms_app");
   1265         if (pref != null) {
   1266             pref.setSummary(DefaultSmsPreferenceController.isSmsDefault(mPackageName, context)
   1267                     ? R.string.yes : R.string.no);
   1268         }
   1269         pref = findPreference("system_alert_window");
   1270         if (pref != null) {
   1271             pref.setSummary(DrawOverlayDetails.getSummary(getContext(), mAppEntry));
   1272         }
   1273         pref = findPreference("picture_in_picture");
   1274         if (pref != null) {
   1275             pref.setSummary(PictureInPictureDetails.getPreferenceSummary(getContext(),
   1276                     mPackageInfo.applicationInfo.uid, mPackageName));
   1277         }
   1278         pref = findPreference("write_settings_apps");
   1279         if (pref != null) {
   1280             pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry));
   1281         }
   1282         pref = findPreference("install_other_apps");
   1283         if (pref != null) {
   1284             pref.setSummary(ExternalSourcesDetails.getPreferenceSummary(getContext(), mAppEntry));
   1285         }
   1286     }
   1287 
   1288     public static NetworkTemplate getTemplate(Context context) {
   1289         if (DataUsageList.hasReadyMobileRadio(context)) {
   1290             return NetworkTemplate.buildTemplateMobileWildcard();
   1291         }
   1292         if (DataUsageUtils.hasWifiRadio(context)) {
   1293             return NetworkTemplate.buildTemplateWifiWildcard();
   1294         }
   1295         return NetworkTemplate.buildTemplateEthernet();
   1296     }
   1297 
   1298     public static CharSequence getNotificationSummary(AppEntry appEntry, Context context,
   1299             NotificationBackend backend) {
   1300         AppRow appRow = backend.loadAppRow(context, context.getPackageManager(), appEntry.info);
   1301         return getNotificationSummary(appRow, context);
   1302     }
   1303 
   1304     public static CharSequence getNotificationSummary(AppRow appRow, Context context) {
   1305         // TODO: implement summary when it is known what it should say
   1306         return "";
   1307     }
   1308 
   1309     @Override
   1310     protected void onPackageRemoved() {
   1311         getActivity().finishActivity(SUB_INFO_FRAGMENT);
   1312         super.onPackageRemoved();
   1313     }
   1314 
   1315     private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> {
   1316 
   1317         @Override
   1318         protected ProcStatsPackageEntry doInBackground(Void... params) {
   1319             if (getActivity() == null) {
   1320                 return null;
   1321             }
   1322             if (mPackageInfo == null) {
   1323                 return null;
   1324             }
   1325             if (mStatsManager == null) {
   1326                 mStatsManager = new ProcStatsData(getActivity(), false);
   1327                 mStatsManager.setDuration(ProcessStatsBase.sDurations[0]);
   1328             }
   1329             mStatsManager.refreshStats(true);
   1330             for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) {
   1331                 for (ProcStatsEntry entry : pkgEntry.mEntries) {
   1332                     if (entry.mUid == mPackageInfo.applicationInfo.uid) {
   1333                         pkgEntry.updateMetrics();
   1334                         return pkgEntry;
   1335                     }
   1336                 }
   1337             }
   1338             return null;
   1339         }
   1340 
   1341         @Override
   1342         protected void onPostExecute(ProcStatsPackageEntry entry) {
   1343             if (getActivity() == null) {
   1344                 return;
   1345             }
   1346             if (entry != null) {
   1347                 mStats = entry;
   1348                 mMemoryPreference.setEnabled(true);
   1349                 double amount = Math.max(entry.mRunWeight, entry.mBgWeight)
   1350                         * mStatsManager.getMemInfo().weightToRam;
   1351                 mMemoryPreference.setSummary(getString(R.string.memory_use_summary,
   1352                         Formatter.formatShortFileSize(getContext(), (long) amount)));
   1353             } else {
   1354                 mMemoryPreference.setEnabled(false);
   1355                 mMemoryPreference.setSummary(getString(R.string.no_memory_use_summary));
   1356             }
   1357         }
   1358 
   1359     }
   1360 
   1361     /**
   1362      * Elicit this class for testing. Test cannot be done in robolectric because it
   1363      * invokes the new API.
   1364      */
   1365     @VisibleForTesting
   1366     public static class PackageUtil {
   1367         /**
   1368          * Count how many users in device have installed package {@paramref packageName}
   1369          */
   1370         public static int countPackageInUsers(PackageManager packageManager, UserManager
   1371                 userManager, String packageName) {
   1372             final List<UserInfo> userInfos = userManager.getUsers(true);
   1373             int count = 0;
   1374 
   1375             for (final UserInfo userInfo : userInfos) {
   1376                 try {
   1377                     // Use this API to check whether user has this package
   1378                     final ApplicationInfo info = packageManager.getApplicationInfoAsUser(
   1379                             packageName, PackageManager.GET_META_DATA, userInfo.id);
   1380                     if ((info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
   1381                         count++;
   1382                     }
   1383                 } catch(NameNotFoundException e) {
   1384                     Log.e(TAG, "Package: " + packageName + " not found for user: " + userInfo.id);
   1385                 }
   1386             }
   1387 
   1388             return count;
   1389         }
   1390     }
   1391 
   1392     private static class DisableChanger extends AsyncTask<Object, Object, Object> {
   1393         final PackageManager mPm;
   1394         final WeakReference<InstalledAppDetails> mActivity;
   1395         final ApplicationInfo mInfo;
   1396         final int mState;
   1397 
   1398         DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
   1399             mPm = activity.mPm;
   1400             mActivity = new WeakReference<InstalledAppDetails>(activity);
   1401             mInfo = info;
   1402             mState = state;
   1403         }
   1404 
   1405         @Override
   1406         protected Object doInBackground(Object... params) {
   1407             mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
   1408             return null;
   1409         }
   1410     }
   1411 
   1412     private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() {
   1413 
   1414         @Override
   1415         public Loader<ChartData> onCreateLoader(int id, Bundle args) {
   1416             return new ChartDataLoader(getActivity(), mStatsSession, args);
   1417         }
   1418 
   1419         @Override
   1420         public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
   1421             mChartData = data;
   1422             mDataPreference.setSummary(getDataSummary());
   1423         }
   1424 
   1425         @Override
   1426         public void onLoaderReset(Loader<ChartData> loader) {
   1427             // Leave last result.
   1428         }
   1429     };
   1430 
   1431     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
   1432         @Override
   1433         public void onReceive(Context context, Intent intent) {
   1434             final boolean enabled = getResultCode() != Activity.RESULT_CANCELED;
   1435             Log.d(LOG_TAG, "Got broadcast response: Restart status for "
   1436                     + mAppEntry.info.packageName + " " + enabled);
   1437             updateForceStopButton(enabled);
   1438         }
   1439     };
   1440 
   1441     private final PermissionsResultCallback mPermissionCallback
   1442             = new PermissionsResultCallback() {
   1443         @Override
   1444         public void onPermissionSummaryResult(int standardGrantedPermissionCount,
   1445                 int requestedPermissionCount, int additionalGrantedPermissionCount,
   1446                 List<CharSequence> grantedGroupLabels) {
   1447             if (getActivity() == null) {
   1448                 return;
   1449             }
   1450             final Resources res = getResources();
   1451             CharSequence summary = null;
   1452 
   1453             if (requestedPermissionCount == 0) {
   1454                 summary = res.getString(
   1455                         R.string.runtime_permissions_summary_no_permissions_requested);
   1456                 mPermissionsPreference.setOnPreferenceClickListener(null);
   1457                 mPermissionsPreference.setEnabled(false);
   1458             } else {
   1459                 final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
   1460                 if (additionalGrantedPermissionCount > 0) {
   1461                     // N additional permissions.
   1462                     list.add(res.getQuantityString(
   1463                             R.plurals.runtime_permissions_additional_count,
   1464                             additionalGrantedPermissionCount, additionalGrantedPermissionCount));
   1465                 }
   1466                 if (list.size() == 0) {
   1467                     summary = res.getString(
   1468                             R.string.runtime_permissions_summary_no_permissions_granted);
   1469                 } else {
   1470                     summary = ListFormatter.getInstance().format(list);
   1471                 }
   1472                 mPermissionsPreference.setOnPreferenceClickListener(InstalledAppDetails.this);
   1473                 mPermissionsPreference.setEnabled(true);
   1474             }
   1475             mPermissionsPreference.setSummary(summary);
   1476         }
   1477     };
   1478 }
   1479