Home | History | Annotate | Download | only in applications
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy 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,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.settings.applications;
     18 
     19 import static android.content.pm.ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
     20 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
     21 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.AUTHORITY;
     22 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.COL_GRANTED;
     23 import static android.os.storage.StorageVolume.ScopedAccessProviderContract.TABLE_PERMISSIONS;
     24 
     25 import static com.android.settings.applications.AppStateDirectoryAccessBridge.DEBUG;
     26 
     27 import android.app.ActivityManager;
     28 import android.app.AlertDialog;
     29 import android.app.AppGlobals;
     30 import android.app.GrantedUriPermission;
     31 import android.app.LoaderManager;
     32 import android.content.ContentResolver;
     33 import android.content.ContentValues;
     34 import android.content.Context;
     35 import android.content.DialogInterface;
     36 import android.content.Intent;
     37 import android.content.Loader;
     38 import android.content.pm.ApplicationInfo;
     39 import android.content.pm.IPackageDataObserver;
     40 import android.content.pm.PackageManager;
     41 import android.content.pm.ProviderInfo;
     42 import android.net.Uri;
     43 import android.os.Bundle;
     44 import android.os.Handler;
     45 import android.os.Message;
     46 import android.os.RemoteException;
     47 import android.os.UserHandle;
     48 import android.os.storage.StorageManager;
     49 import android.os.storage.VolumeInfo;
     50 import android.support.annotation.VisibleForTesting;
     51 import android.support.v7.preference.Preference;
     52 import android.support.v7.preference.PreferenceCategory;
     53 import android.util.Log;
     54 import android.util.MutableInt;
     55 import android.view.View;
     56 import android.view.View.OnClickListener;
     57 import android.widget.Button;
     58 
     59 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     60 import com.android.settings.R;
     61 import com.android.settings.Utils;
     62 import com.android.settings.deviceinfo.StorageWizardMoveConfirm;
     63 import com.android.settings.widget.ActionButtonPreference;
     64 import com.android.settingslib.RestrictedLockUtils;
     65 import com.android.settingslib.applications.ApplicationsState.Callbacks;
     66 import com.android.settingslib.applications.StorageStatsSource;
     67 import com.android.settingslib.applications.StorageStatsSource.AppStorageStats;
     68 
     69 import java.util.Collections;
     70 import java.util.List;
     71 import java.util.Map;
     72 import java.util.Objects;
     73 import java.util.TreeMap;
     74 
     75 public class AppStorageSettings extends AppInfoWithHeader
     76         implements OnClickListener, Callbacks, DialogInterface.OnClickListener,
     77         LoaderManager.LoaderCallbacks<AppStorageStats> {
     78     private static final String TAG = AppStorageSettings.class.getSimpleName();
     79 
     80     //internal constants used in Handler
     81     private static final int OP_SUCCESSFUL = 1;
     82     private static final int OP_FAILED = 2;
     83     private static final int MSG_CLEAR_USER_DATA = 1;
     84     private static final int MSG_CLEAR_CACHE = 3;
     85 
     86     // invalid size value used initially and also when size retrieval through PackageManager
     87     // fails for whatever reason
     88     private static final int SIZE_INVALID = -1;
     89 
     90     // Result code identifiers
     91     public static final int REQUEST_MANAGE_SPACE = 2;
     92 
     93     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
     94     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 2;
     95 
     96     private static final String KEY_STORAGE_USED = "storage_used";
     97     private static final String KEY_CHANGE_STORAGE = "change_storage_button";
     98     private static final String KEY_STORAGE_SPACE = "storage_space";
     99     private static final String KEY_STORAGE_CATEGORY = "storage_category";
    100 
    101     private static final String KEY_TOTAL_SIZE = "total_size";
    102     private static final String KEY_APP_SIZE = "app_size";
    103     private static final String KEY_DATA_SIZE = "data_size";
    104     private static final String KEY_CACHE_SIZE = "cache_size";
    105 
    106     private static final String KEY_HEADER_BUTTONS = "header_view";
    107 
    108     private static final String KEY_URI_CATEGORY = "uri_category";
    109     private static final String KEY_CLEAR_URI = "clear_uri_button";
    110 
    111     private static final String KEY_CACHE_CLEARED = "cache_cleared";
    112     private static final String KEY_DATA_CLEARED = "data_cleared";
    113 
    114     // Views related to cache info
    115     @VisibleForTesting
    116     ActionButtonPreference mButtonsPref;
    117 
    118     private Preference mStorageUsed;
    119     private Button mChangeStorageButton;
    120 
    121     // Views related to URI permissions
    122     private Button mClearUriButton;
    123     private LayoutPreference mClearUri;
    124     private PreferenceCategory mUri;
    125 
    126     private boolean mCanClearData = true;
    127     private boolean mCacheCleared;
    128     private boolean mDataCleared;
    129 
    130     @VisibleForTesting
    131     AppStorageSizesController mSizeController;
    132 
    133     private ClearCacheObserver mClearCacheObserver;
    134     private ClearUserDataObserver mClearDataObserver;
    135 
    136     private VolumeInfo[] mCandidates;
    137     private AlertDialog.Builder mDialogBuilder;
    138     private ApplicationInfo mInfo;
    139 
    140     @Override
    141     public void onCreate(Bundle savedInstanceState) {
    142         super.onCreate(savedInstanceState);
    143         if (savedInstanceState != null) {
    144             mCacheCleared = savedInstanceState.getBoolean(KEY_CACHE_CLEARED, false);
    145             mDataCleared = savedInstanceState.getBoolean(KEY_DATA_CLEARED, false);
    146             mCacheCleared = mCacheCleared || mDataCleared;
    147         }
    148 
    149         addPreferencesFromResource(R.xml.app_storage_settings);
    150         setupViews();
    151         initMoveDialog();
    152     }
    153 
    154     @Override
    155     public void onResume() {
    156         super.onResume();
    157         updateSize();
    158     }
    159 
    160     @Override
    161     public void onSaveInstanceState(Bundle outState) {
    162         super.onSaveInstanceState(outState);
    163         outState.putBoolean(KEY_CACHE_CLEARED, mCacheCleared);
    164         outState.putBoolean(KEY_DATA_CLEARED, mDataCleared);
    165     }
    166 
    167     private void setupViews() {
    168         // Set default values on sizes
    169         mSizeController = new AppStorageSizesController.Builder()
    170                 .setTotalSizePreference(findPreference(KEY_TOTAL_SIZE))
    171                 .setAppSizePreference(findPreference(KEY_APP_SIZE))
    172                 .setDataSizePreference(findPreference(KEY_DATA_SIZE))
    173                 .setCacheSizePreference(findPreference(KEY_CACHE_SIZE))
    174                 .setComputingString(R.string.computing_size)
    175                 .setErrorString(R.string.invalid_size_value)
    176                 .build();
    177         mButtonsPref = ((ActionButtonPreference) findPreference(KEY_HEADER_BUTTONS))
    178                 .setButton1Positive(false)
    179                 .setButton2Positive(false);
    180 
    181         mStorageUsed = findPreference(KEY_STORAGE_USED);
    182         mChangeStorageButton = (Button) ((LayoutPreference) findPreference(KEY_CHANGE_STORAGE))
    183                 .findViewById(R.id.button);
    184         mChangeStorageButton.setText(R.string.change);
    185         mChangeStorageButton.setOnClickListener(this);
    186 
    187         // Cache section
    188         mButtonsPref.setButton2Text(R.string.clear_cache_btn_text);
    189 
    190         // URI permissions section
    191         mUri = (PreferenceCategory) findPreference(KEY_URI_CATEGORY);
    192         mClearUri = (LayoutPreference) mUri.findPreference(KEY_CLEAR_URI);
    193         mClearUriButton = (Button) mClearUri.findViewById(R.id.button);
    194         mClearUriButton.setText(R.string.clear_uri_btn_text);
    195         mClearUriButton.setOnClickListener(this);
    196     }
    197 
    198     @VisibleForTesting
    199     void handleClearCacheClick() {
    200         if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    201             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    202                     getActivity(), mAppsControlDisallowedAdmin);
    203             return;
    204         } else if (mClearCacheObserver == null) { // Lazy initialization of observer
    205             mClearCacheObserver = new ClearCacheObserver();
    206         }
    207         mMetricsFeatureProvider.action(getContext(),
    208                 MetricsEvent.ACTION_SETTINGS_CLEAR_APP_CACHE);
    209         mPm.deleteApplicationCacheFiles(mPackageName, mClearCacheObserver);
    210     }
    211 
    212     @VisibleForTesting
    213     void handleClearDataClick() {
    214         if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    215             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    216                     getActivity(), mAppsControlDisallowedAdmin);
    217         } else if (mAppEntry.info.manageSpaceActivityName != null) {
    218             if (!Utils.isMonkeyRunning()) {
    219                 Intent intent = new Intent(Intent.ACTION_DEFAULT);
    220                 intent.setClassName(mAppEntry.info.packageName,
    221                         mAppEntry.info.manageSpaceActivityName);
    222                 startActivityForResult(intent, REQUEST_MANAGE_SPACE);
    223             }
    224         } else {
    225             showDialogInner(DLG_CLEAR_DATA, 0);
    226         }
    227     }
    228 
    229     @Override
    230     public void onClick(View v) {
    231         if (v == mChangeStorageButton && mDialogBuilder != null && !isMoveInProgress()) {
    232             mDialogBuilder.show();
    233         } else if (v == mClearUriButton) {
    234             if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
    235                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
    236                         getActivity(), mAppsControlDisallowedAdmin);
    237             } else {
    238                 clearUriPermissions();
    239             }
    240         }
    241     }
    242 
    243     private boolean isMoveInProgress() {
    244         try {
    245             // TODO: define a cleaner API for this
    246             AppGlobals.getPackageManager().checkPackageStartable(mPackageName,
    247                     UserHandle.myUserId());
    248             return false;
    249         } catch (RemoteException | SecurityException e) {
    250             return true;
    251         }
    252     }
    253 
    254     @Override
    255     public void onClick(DialogInterface dialog, int which) {
    256         final Context context = getActivity();
    257 
    258         // If not current volume, kick off move wizard
    259         final VolumeInfo targetVol = mCandidates[which];
    260         final VolumeInfo currentVol = context.getPackageManager().getPackageCurrentVolume(
    261                 mAppEntry.info);
    262         if (!Objects.equals(targetVol, currentVol)) {
    263             final Intent intent = new Intent(context, StorageWizardMoveConfirm.class);
    264             intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, targetVol.getId());
    265             intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
    266             startActivity(intent);
    267         }
    268         dialog.dismiss();
    269     }
    270 
    271     @Override
    272     protected boolean refreshUi() {
    273         retrieveAppEntry();
    274         if (mAppEntry == null) {
    275             return false;
    276         }
    277         updateUiWithSize(mSizeController.getLastResult());
    278         refreshGrantedUriPermissions();
    279 
    280         final VolumeInfo currentVol = getActivity().getPackageManager()
    281                 .getPackageCurrentVolume(mAppEntry.info);
    282         final StorageManager storage = getContext().getSystemService(StorageManager.class);
    283         mStorageUsed.setSummary(storage.getBestVolumeDescription(currentVol));
    284 
    285         refreshButtons();
    286 
    287         return true;
    288     }
    289 
    290     private void refreshButtons() {
    291         initMoveDialog();
    292         initDataButtons();
    293     }
    294 
    295     private void initDataButtons() {
    296         final boolean appHasSpaceManagementUI = mAppEntry.info.manageSpaceActivityName != null;
    297         final boolean appHasActiveAdmins = mDpm.packageHasActiveAdmins(mPackageName);
    298         // Check that SYSTEM_APP flag is set, and ALLOW_CLEAR_USER_DATA is not set.
    299         final boolean isNonClearableSystemApp =
    300                 (mAppEntry.info.flags & (FLAG_SYSTEM | FLAG_ALLOW_CLEAR_USER_DATA)) == FLAG_SYSTEM;
    301         final boolean appRestrictsClearingData = isNonClearableSystemApp || appHasActiveAdmins;
    302 
    303         final Intent intent = new Intent(Intent.ACTION_DEFAULT);
    304         if (appHasSpaceManagementUI) {
    305             intent.setClassName(mAppEntry.info.packageName, mAppEntry.info.manageSpaceActivityName);
    306         }
    307         final boolean isManageSpaceActivityAvailable =
    308                 getPackageManager().resolveActivity(intent, 0) != null;
    309 
    310         if ((!appHasSpaceManagementUI && appRestrictsClearingData)
    311                 || !isManageSpaceActivityAvailable) {
    312             mButtonsPref
    313                     .setButton1Text(R.string.clear_user_data_text)
    314                     .setButton1Enabled(false);
    315             mCanClearData = false;
    316         } else {
    317             if (appHasSpaceManagementUI) {
    318                 mButtonsPref.setButton1Text(R.string.manage_space_text);
    319             } else {
    320                 mButtonsPref.setButton1Text(R.string.clear_user_data_text);
    321             }
    322             mButtonsPref
    323                     .setButton1Text(R.string.clear_user_data_text)
    324                     .setButton1OnClickListener(v -> handleClearDataClick());
    325         }
    326 
    327         if (mAppsControlDisallowedBySystem) {
    328             mButtonsPref.setButton1Enabled(false);
    329         }
    330     }
    331 
    332     private void initMoveDialog() {
    333         final Context context = getActivity();
    334         final StorageManager storage = context.getSystemService(StorageManager.class);
    335 
    336         final List<VolumeInfo> candidates = context.getPackageManager()
    337                 .getPackageCandidateVolumes(mAppEntry.info);
    338         if (candidates.size() > 1) {
    339             Collections.sort(candidates, VolumeInfo.getDescriptionComparator());
    340 
    341             CharSequence[] labels = new CharSequence[candidates.size()];
    342             int current = -1;
    343             for (int i = 0; i < candidates.size(); i++) {
    344                 final String volDescrip = storage.getBestVolumeDescription(candidates.get(i));
    345                 if (Objects.equals(volDescrip, mStorageUsed.getSummary())) {
    346                     current = i;
    347                 }
    348                 labels[i] = volDescrip;
    349             }
    350             mCandidates = candidates.toArray(new VolumeInfo[candidates.size()]);
    351             mDialogBuilder = new AlertDialog.Builder(getContext())
    352                     .setTitle(R.string.change_storage)
    353                     .setSingleChoiceItems(labels, current, this)
    354                     .setNegativeButton(R.string.cancel, null);
    355         } else {
    356             removePreference(KEY_STORAGE_USED);
    357             removePreference(KEY_CHANGE_STORAGE);
    358             removePreference(KEY_STORAGE_SPACE);
    359         }
    360     }
    361 
    362     /*
    363      * Private method to initiate clearing user data when the user clicks the clear data
    364      * button for a system package
    365      */
    366     private void initiateClearUserData() {
    367         mMetricsFeatureProvider.action(getContext(), MetricsEvent.ACTION_SETTINGS_CLEAR_APP_DATA);
    368         mButtonsPref.setButton1Enabled(false);
    369         // Invoke uninstall or clear user data based on sysPackage
    370         String packageName = mAppEntry.info.packageName;
    371         Log.i(TAG, "Clearing user data for package : " + packageName);
    372         if (mClearDataObserver == null) {
    373             mClearDataObserver = new ClearUserDataObserver();
    374         }
    375         ActivityManager am = (ActivityManager)
    376                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
    377         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
    378         if (!res) {
    379             // Clearing data failed for some obscure reason. Just log error for now
    380             Log.i(TAG, "Couldn't clear application user data for package:" + packageName);
    381             showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
    382         } else {
    383             mButtonsPref.setButton1Text(R.string.recompute_size);
    384         }
    385     }
    386 
    387     /*
    388      * Private method to handle clear message notification from observer when
    389      * the async operation from PackageManager is complete
    390      */
    391     private void processClearMsg(Message msg) {
    392         int result = msg.arg1;
    393         String packageName = mAppEntry.info.packageName;
    394         mButtonsPref.setButton1Text(R.string.clear_user_data_text);
    395         if (result == OP_SUCCESSFUL) {
    396             Log.i(TAG, "Cleared user data for package : " + packageName);
    397             updateSize();
    398         } else {
    399             mButtonsPref.setButton1Enabled(true);
    400         }
    401     }
    402 
    403     private void refreshGrantedUriPermissions() {
    404         // Clear UI first (in case the activity has been resumed)
    405         removeUriPermissionsFromUi();
    406 
    407         // Gets all URI permissions from am.
    408         ActivityManager am = (ActivityManager) getActivity().getSystemService(
    409                 Context.ACTIVITY_SERVICE);
    410         List<GrantedUriPermission> perms =
    411                 am.getGrantedUriPermissions(mAppEntry.info.packageName).getList();
    412 
    413         if (perms.isEmpty()) {
    414             mClearUriButton.setVisibility(View.GONE);
    415             return;
    416         }
    417 
    418         PackageManager pm = getActivity().getPackageManager();
    419 
    420         // Group number of URIs by app.
    421         Map<CharSequence, MutableInt> uriCounters = new TreeMap<>();
    422         for (GrantedUriPermission perm : perms) {
    423             String authority = perm.uri.getAuthority();
    424             ProviderInfo provider = pm.resolveContentProvider(authority, 0);
    425             CharSequence app = provider.applicationInfo.loadLabel(pm);
    426             MutableInt count = uriCounters.get(app);
    427             if (count == null) {
    428                 uriCounters.put(app, new MutableInt(1));
    429             } else {
    430                 count.value++;
    431             }
    432         }
    433 
    434         // Dynamically add the preferences, one per app.
    435         int order = 0;
    436         for (Map.Entry<CharSequence, MutableInt> entry : uriCounters.entrySet()) {
    437             int numberResources = entry.getValue().value;
    438             Preference pref = new Preference(getPrefContext());
    439             pref.setTitle(entry.getKey());
    440             pref.setSummary(getPrefContext().getResources()
    441                     .getQuantityString(R.plurals.uri_permissions_text, numberResources,
    442                             numberResources));
    443             pref.setSelectable(false);
    444             pref.setLayoutResource(R.layout.horizontal_preference);
    445             pref.setOrder(order);
    446             Log.v(TAG, "Adding preference '" + pref + "' at order " + order);
    447             mUri.addPreference(pref);
    448         }
    449 
    450         if (mAppsControlDisallowedBySystem) {
    451             mClearUriButton.setEnabled(false);
    452         }
    453 
    454         mClearUri.setOrder(order);
    455         mClearUriButton.setVisibility(View.VISIBLE);
    456 
    457     }
    458 
    459     private void clearUriPermissions() {
    460         final Context context = getActivity();
    461         final String packageName = mAppEntry.info.packageName;
    462         // Synchronously revoke the permissions.
    463         final ActivityManager am = (ActivityManager) context.getSystemService(
    464                 Context.ACTIVITY_SERVICE);
    465         am.clearGrantedUriPermissions(packageName);
    466 
    467 
    468         // Also update the Scoped Directory Access UI permissions
    469         final Uri providerUri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT)
    470                 .authority(AUTHORITY).appendPath(TABLE_PERMISSIONS).appendPath("*")
    471                 .build();
    472         Log.v(TAG, "Asking " + providerUri + " to delete permissions for " + packageName);
    473         final int deleted = context.getContentResolver().delete(providerUri, null, new String[] {
    474                 packageName
    475         });
    476         Log.d(TAG, "Deleted " + deleted + " entries for package " + packageName);
    477 
    478         // Update UI
    479         refreshGrantedUriPermissions();
    480     }
    481 
    482     private void removeUriPermissionsFromUi() {
    483         // Remove all preferences but the clear button.
    484         int count = mUri.getPreferenceCount();
    485         for (int i = count - 1; i >= 0; i--) {
    486             Preference pref = mUri.getPreference(i);
    487             if (pref != mClearUri) {
    488                 mUri.removePreference(pref);
    489             }
    490         }
    491     }
    492 
    493     @Override
    494     protected AlertDialog createDialog(int id, int errorCode) {
    495         switch (id) {
    496             case DLG_CLEAR_DATA:
    497                 return new AlertDialog.Builder(getActivity())
    498                         .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
    499                         .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
    500                         .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    501                             public void onClick(DialogInterface dialog, int which) {
    502                                 // Clear user data here
    503                                 initiateClearUserData();
    504                             }
    505                         })
    506                         .setNegativeButton(R.string.dlg_cancel, null)
    507                         .create();
    508             case DLG_CANNOT_CLEAR_DATA:
    509                 return new AlertDialog.Builder(getActivity())
    510                         .setTitle(getActivity().getText(R.string.clear_user_data_text))
    511                         .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
    512                         .setNeutralButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
    513                             public void onClick(DialogInterface dialog, int which) {
    514                                 mButtonsPref.setButton1Enabled(false);
    515                                 //force to recompute changed value
    516                                 setIntentAndFinish(false, false);
    517                             }
    518                         })
    519                         .create();
    520         }
    521         return null;
    522     }
    523 
    524     @Override
    525     public void onPackageSizeChanged(String packageName) {
    526     }
    527 
    528     @Override
    529     public Loader<AppStorageStats> onCreateLoader(int id, Bundle args) {
    530         Context context = getContext();
    531         return new FetchPackageStorageAsyncLoader(
    532                 context, new StorageStatsSource(context), mInfo, UserHandle.of(mUserId));
    533     }
    534 
    535     @Override
    536     public void onLoadFinished(Loader<AppStorageStats> loader, AppStorageStats result) {
    537         mSizeController.setResult(result);
    538         updateUiWithSize(result);
    539     }
    540 
    541     @Override
    542     public void onLoaderReset(Loader<AppStorageStats> loader) {
    543     }
    544 
    545     private void updateSize() {
    546         PackageManager packageManager = getPackageManager();
    547         try {
    548             mInfo = packageManager.getApplicationInfo(mPackageName, 0);
    549         } catch (PackageManager.NameNotFoundException e) {
    550             Log.e(TAG, "Could not find package", e);
    551         }
    552 
    553         if (mInfo == null) {
    554             return;
    555         }
    556 
    557         getLoaderManager().restartLoader(1, Bundle.EMPTY, this);
    558     }
    559 
    560     @VisibleForTesting
    561     void updateUiWithSize(AppStorageStats result) {
    562         if (mCacheCleared) {
    563             mSizeController.setCacheCleared(true);
    564         }
    565         if (mDataCleared) {
    566             mSizeController.setDataCleared(true);
    567         }
    568 
    569         mSizeController.updateUi(getContext());
    570 
    571         if (result == null) {
    572             mButtonsPref.setButton1Enabled(false).setButton2Enabled(false);
    573         } else {
    574             long cacheSize = result.getCacheBytes();
    575             long dataSize = result.getDataBytes() - cacheSize;
    576 
    577             if (dataSize <= 0 || !mCanClearData || mDataCleared) {
    578                 mButtonsPref.setButton1Enabled(false);
    579             } else {
    580                 mButtonsPref.setButton1Enabled(true)
    581                         .setButton1OnClickListener(v -> handleClearDataClick());
    582             }
    583             if (cacheSize <= 0 || mCacheCleared) {
    584                 mButtonsPref.setButton2Enabled(false);
    585             } else {
    586                 mButtonsPref.setButton2Enabled(true)
    587                         .setButton2OnClickListener(v -> handleClearCacheClick());
    588             }
    589         }
    590         if (mAppsControlDisallowedBySystem) {
    591             mButtonsPref.setButton1Enabled(false).setButton2Enabled(false);
    592         }
    593     }
    594 
    595     private final Handler mHandler = new Handler() {
    596         public void handleMessage(Message msg) {
    597             if (getView() == null) {
    598                 return;
    599             }
    600             switch (msg.what) {
    601                 case MSG_CLEAR_USER_DATA:
    602                     mDataCleared = true;
    603                     mCacheCleared = true;
    604                     processClearMsg(msg);
    605                     break;
    606                 case MSG_CLEAR_CACHE:
    607                     mCacheCleared = true;
    608                     // Refresh size info
    609                     updateSize();
    610                     break;
    611             }
    612         }
    613     };
    614 
    615     @Override
    616     public int getMetricsCategory() {
    617         return MetricsEvent.APPLICATIONS_APP_STORAGE;
    618     }
    619 
    620     class ClearCacheObserver extends IPackageDataObserver.Stub {
    621         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
    622             final Message msg = mHandler.obtainMessage(MSG_CLEAR_CACHE);
    623             msg.arg1 = succeeded ? OP_SUCCESSFUL : OP_FAILED;
    624             mHandler.sendMessage(msg);
    625         }
    626     }
    627 
    628     class ClearUserDataObserver extends IPackageDataObserver.Stub {
    629         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
    630             final Message msg = mHandler.obtainMessage(MSG_CLEAR_USER_DATA);
    631             msg.arg1 = succeeded ? OP_SUCCESSFUL : OP_FAILED;
    632             mHandler.sendMessage(msg);
    633         }
    634     }
    635 }
    636