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