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