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