• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 
3 /**
4  * Copyright (C) 2007 The Android Open Source Project
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
7  * use this file except in compliance with the License. You may obtain a copy
8  * of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15  * License for the specific language governing permissions and limitations
16  * under the License.
17  */
18 
19 package com.android.settings;
20 
21 import com.android.settings.R;
22 import android.app.Activity;
23 import android.app.ActivityManager;
24 import android.app.AlertDialog;
25 import android.app.Dialog;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.pm.ApplicationInfo;
31 import android.content.pm.IPackageDataObserver;
32 import android.content.pm.IPackageDeleteObserver;
33 import android.content.pm.IPackageStatsObserver;
34 import android.content.pm.PackageInfo;
35 import android.content.pm.PackageManager;
36 import android.content.pm.PackageStats;
37 import android.content.pm.PackageManager.NameNotFoundException;
38 import android.net.Uri;
39 import android.os.Bundle;
40 import android.os.Handler;
41 import android.os.Message;
42 import android.os.RemoteException;
43 import android.text.format.Formatter;
44 import android.util.Config;
45 import android.util.Log;
46 import java.util.ArrayList;
47 import java.util.List;
48 import android.content.ComponentName;
49 import android.view.View;
50 import android.widget.AppSecurityPermissions;
51 import android.widget.Button;
52 import android.widget.ImageView;
53 import android.widget.LinearLayout;
54 import android.widget.TextView;
55 
56 /**
57  * Activity to display application information from Settings. This activity presents
58  * extended information associated with a package like code, data, total size, permissions
59  * used by the application and also the set of default launchable activities.
60  * For system applications, an option to clear user data is displayed only if data size is > 0.
61  * System applications that do not want clear user data do not have this option.
62  * For non-system applications, there is no option to clear data. Instead there is an option to
63  * uninstall the application.
64  */
65 public class InstalledAppDetails extends Activity implements View.OnClickListener, DialogInterface.OnClickListener  {
66     private static final String TAG="InstalledAppDetails";
67     private static final int _UNKNOWN_APP=R.string.unknown;
68     private ApplicationInfo mAppInfo;
69     private Button mAppButton;
70     private Button mActivitiesButton;
71     private boolean localLOGV = false;
72     private TextView mAppVersion;
73     private TextView mTotalSize;
74     private TextView mAppSize;
75     private TextView mDataSize;
76     private PkgSizeObserver mSizeObserver;
77     private ClearUserDataObserver mClearDataObserver;
78     // Views related to cache info
79     private View mCachePanel;
80     private TextView mCacheSize;
81     private Button mClearCacheButton;
82     private ClearCacheObserver mClearCacheObserver;
83     private Button mForceStopButton;
84 
85     PackageStats mSizeInfo;
86     private Button mManageSpaceButton;
87     private PackageManager mPm;
88 
89     //internal constants used in Handler
90     private static final int OP_SUCCESSFUL = 1;
91     private static final int OP_FAILED = 2;
92     private static final int CLEAR_USER_DATA = 1;
93     private static final int GET_PKG_SIZE = 2;
94     private static final int CLEAR_CACHE = 3;
95     private static final String ATTR_PACKAGE_STATS="PackageStats";
96 
97     // invalid size value used initially and also when size retrieval through PackageManager
98     // fails for whatever reason
99     private static final int SIZE_INVALID = -1;
100 
101     // Resource strings
102     private CharSequence mInvalidSizeStr;
103     private CharSequence mComputingStr;
104     private CharSequence mAppButtonText;
105 
106     // Dialog identifiers used in showDialog
107     private static final int DLG_BASE = 0;
108     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
109     private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
110     private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
111     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
112 
113     // Possible btn states
114     private enum AppButtonStates {
115         CLEAR_DATA,
116         UNINSTALL,
117         FACTORY_RESET,
118         NONE
119     }
120     private AppButtonStates mAppButtonState;
121 
122     private Handler mHandler = new Handler() {
123         public void handleMessage(Message msg) {
124             switch (msg.what) {
125                 case CLEAR_USER_DATA:
126                     processClearMsg(msg);
127                     break;
128                 case GET_PKG_SIZE:
129                     refreshSizeInfo(msg);
130                     break;
131                 case CLEAR_CACHE:
132                     // Refresh size info
133                     mPm.getPackageSizeInfo(mAppInfo.packageName, mSizeObserver);
134                     break;
135                 default:
136                     break;
137             }
138         }
139     };
140 
141     class ClearUserDataObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)142        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
143            final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
144            msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
145            mHandler.sendMessage(msg);
146         }
147     }
148 
149     class PkgSizeObserver extends IPackageStatsObserver.Stub {
150         public int idx;
onGetStatsCompleted(PackageStats pStats, boolean succeeded)151         public void onGetStatsCompleted(PackageStats pStats, boolean succeeded) {
152              Message msg = mHandler.obtainMessage(GET_PKG_SIZE);
153              Bundle data = new Bundle();
154              data.putParcelable(ATTR_PACKAGE_STATS, pStats);
155              msg.setData(data);
156              mHandler.sendMessage(msg);
157 
158          }
159      }
160 
161     class ClearCacheObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)162         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
163             final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
164             msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
165             mHandler.sendMessage(msg);
166          }
167      }
168 
getSizeStr(long size)169     private String getSizeStr(long size) {
170         if (size == SIZE_INVALID) {
171             return mInvalidSizeStr.toString();
172         }
173         return Formatter.formatFileSize(this, size);
174     }
175 
176     /** Called when the activity is first created. */
177     @Override
onCreate(Bundle icicle)178     protected void onCreate(Bundle icicle) {
179         super.onCreate(icicle);
180         // Get package manager
181         mPm = getPackageManager();
182         // Get application's name from intent
183         Intent intent = getIntent();
184         final String packageName = intent.getStringExtra(ManageApplications.APP_PKG_NAME);
185         mComputingStr = getText(R.string.computing_size);
186         // Try retrieving package stats again
187         CharSequence totalSizeStr, appSizeStr, dataSizeStr;
188         totalSizeStr = appSizeStr = dataSizeStr = mComputingStr;
189         if(localLOGV) Log.i(TAG, "Have to compute package sizes");
190         mSizeObserver = new PkgSizeObserver();
191         try {
192             mAppInfo = mPm.getApplicationInfo(packageName,
193                     PackageManager.GET_UNINSTALLED_PACKAGES);
194         } catch (NameNotFoundException e) {
195             Log.e(TAG, "Exception when retrieving package:"+packageName, e);
196             showDialogInner(DLG_APP_NOT_FOUND);
197             return;
198         }
199         setContentView(R.layout.installed_app_details);
200         //TODO download str and download url
201         // Set default values on sizes
202         mTotalSize = (TextView)findViewById(R.id.total_size_text);
203         mTotalSize.setText(totalSizeStr);
204         mAppSize = (TextView)findViewById(R.id.application_size_text);
205         mAppSize.setText(appSizeStr);
206         mDataSize = (TextView)findViewById(R.id.data_size_text);
207         mDataSize.setText(dataSizeStr);
208          // Get AppButton
209          mAppButton = ((Button)findViewById(R.id.uninstall_button));
210          // Get ManageSpaceButton
211          mManageSpaceButton = (Button)findViewById(R.id.manage_space_button);
212          if(mAppInfo.manageSpaceActivityName != null) {
213              mManageSpaceButton.setVisibility(View.VISIBLE);
214              mManageSpaceButton.setOnClickListener(this);
215          }
216          // Cache section
217          mCachePanel = findViewById(R.id.cache_panel);
218          mCacheSize = (TextView) findViewById(R.id.cache_size_text);
219          mCacheSize.setText(mComputingStr);
220          mClearCacheButton = (Button) findViewById(R.id.clear_cache_button);
221          mForceStopButton = (Button) findViewById(R.id.force_stop_button);
222          mForceStopButton.setOnClickListener(this);
223          // Get list of preferred activities
224          mActivitiesButton = (Button)findViewById(R.id.clear_activities_button);
225          List<ComponentName> prefActList = new ArrayList<ComponentName>();
226          // Intent list cannot be null. so pass empty list
227          List<IntentFilter> intentList = new ArrayList<IntentFilter>();
228          mPm.getPreferredActivities(intentList,  prefActList, packageName);
229          if(localLOGV) Log.i(TAG, "Have "+prefActList.size()+" number of activities in prefered list");
230          TextView autoLaunchView = (TextView)findViewById(R.id.auto_launch);
231          if(prefActList.size() <= 0) {
232              // Disable clear activities button
233              autoLaunchView.setText(R.string.auto_launch_disable_text);
234              mActivitiesButton.setEnabled(false);
235          } else {
236              autoLaunchView.setText(R.string.auto_launch_enable_text);
237              mActivitiesButton.setOnClickListener(this);
238          }
239 
240          // Security permissions section
241          LinearLayout permsView = (LinearLayout) findViewById(R.id.permissions_section);
242          AppSecurityPermissions asp = new AppSecurityPermissions(this, packageName);
243          if(asp.getPermissionCount() > 0) {
244              permsView.setVisibility(View.VISIBLE);
245              // Make the security sections header visible
246              LinearLayout securityList = (LinearLayout) permsView.findViewById(
247                      R.id.security_settings_list);
248              securityList.addView(asp.getPermissionsView());
249          } else {
250              permsView.setVisibility(View.GONE);
251          }
252     }
253 
refreshAppAttributes(PackageInfo pkgInfo)254     private void refreshAppAttributes(PackageInfo pkgInfo) {
255         setAppLabelAndIcon();
256         // Version number of application
257         setAppVersion(pkgInfo);
258         setAppBtnState();
259         // Refresh size info
260         if (mAppInfo != null && mAppInfo.packageName != null) {
261             mPm.getPackageSizeInfo(mAppInfo.packageName, mSizeObserver);
262         }
263     }
264 
265     // Utility method to set applicaiton label and icon.
setAppLabelAndIcon()266     private void setAppLabelAndIcon() {
267         ((ImageView)findViewById(R.id.app_icon)).setImageDrawable(mAppInfo.loadIcon(mPm));
268         //set application name TODO version
269         CharSequence appName = mAppInfo.loadLabel(mPm);
270         if(appName == null) {
271             appName = getString(_UNKNOWN_APP);
272         }
273         ((TextView)findViewById(R.id.app_name)).setText(appName);
274     }
275 
276     // Utility method to set application version
setAppVersion(PackageInfo pkgInfo)277     private void setAppVersion(PackageInfo pkgInfo) {
278         // Version number of application
279         mAppVersion = ((TextView)findViewById(R.id.app_version));
280         if (pkgInfo != null) {
281             mAppVersion.setVisibility(View.VISIBLE);
282             mAppVersion.setText(getString(R.string.version_text,
283                     String.valueOf(pkgInfo.versionCode)));
284         } else {
285             mAppVersion.setVisibility(View.GONE);
286         }
287     }
288 
289     // Utility method to set button state
setAppBtnState()290     private void setAppBtnState() {
291         boolean visible = true;
292         if  ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
293             if ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
294                 mAppButtonState = AppButtonStates.FACTORY_RESET;
295                 mAppButtonText = getText(R.string.app_factory_reset);
296             } else {
297                 if ((mAppInfo.flags & ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA) == 0) {
298                     // Hide button if diableClearUserData is set
299                     mAppButtonState = AppButtonStates.NONE;
300                     visible = false;
301                 } else {
302                     mAppButtonState = AppButtonStates.CLEAR_DATA;
303                     mAppButtonText = getText(R.string.clear_user_data_text);
304                 }
305             }
306         } else {
307             mAppButtonState = AppButtonStates.UNINSTALL;
308             mAppButtonText = getText(R.string.uninstall_text);
309         }
310         if(visible) {
311             mAppButton.setText(mAppButtonText);
312             mAppButton.setVisibility(View.VISIBLE);
313         } else {
314             mAppButton.setVisibility(View.GONE);
315         }
316     }
317 
318     @Override
onStart()319     public void onStart() {
320         super.onStart();
321         PackageInfo pkgInfo;
322         // Get application info again to refresh changed properties of application
323         try {
324             mAppInfo = mPm.getApplicationInfo(mAppInfo.packageName,
325                     PackageManager.GET_UNINSTALLED_PACKAGES);
326             pkgInfo = mPm.getPackageInfo(mAppInfo.packageName, 0);
327         } catch (NameNotFoundException e) {
328             Log.e(TAG, "Exception when retrieving package:" + mAppInfo.packageName, e);
329             showDialogInner(DLG_APP_NOT_FOUND);
330             return;
331         }
332         refreshAppAttributes(pkgInfo);
333     }
334 
setIntentAndFinish(boolean finish, boolean appChanged)335     private void setIntentAndFinish(boolean finish, boolean appChanged) {
336         if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
337         Intent intent = new Intent();
338         intent.putExtra(ManageApplications.APP_CHG, appChanged);
339         setResult(ManageApplications.RESULT_OK, intent);
340         mAppButton.setEnabled(false);
341         if(finish) {
342             finish();
343         }
344     }
345 
346     /*
347      * Private method to handle get size info notification from observer when
348      * the async operation from PackageManager is complete. The current user data
349      * info has to be refreshed in the manage applications screen as well as the current screen.
350      */
refreshSizeInfo(Message msg)351     private void refreshSizeInfo(Message msg) {
352         boolean changed = false;
353         PackageStats newPs = msg.getData().getParcelable(ATTR_PACKAGE_STATS);
354         long newTot = newPs.cacheSize+newPs.codeSize+newPs.dataSize;
355         if(mSizeInfo == null) {
356             mSizeInfo = newPs;
357             String str = getSizeStr(newTot);
358             mTotalSize.setText(str);
359             mAppSize.setText(getSizeStr(newPs.codeSize));
360             mDataSize.setText(getSizeStr(newPs.dataSize));
361             mCacheSize.setText(getSizeStr(newPs.cacheSize));
362         } else {
363             long oldTot = mSizeInfo.cacheSize+mSizeInfo.codeSize+mSizeInfo.dataSize;
364             if(newTot != oldTot) {
365                 String str = getSizeStr(newTot);
366                 mTotalSize.setText(str);
367                 changed = true;
368             }
369             if(newPs.codeSize != mSizeInfo.codeSize) {
370                 mAppSize.setText(getSizeStr(newPs.codeSize));
371                 changed = true;
372             }
373             if(newPs.dataSize != mSizeInfo.dataSize) {
374                 mDataSize.setText(getSizeStr(newPs.dataSize));
375                 changed = true;
376             }
377             if(newPs.cacheSize != mSizeInfo.cacheSize) {
378                 mCacheSize.setText(getSizeStr(newPs.cacheSize));
379                 changed = true;
380             }
381             if(changed) {
382                 mSizeInfo = newPs;
383             }
384         }
385 
386         long data = mSizeInfo.dataSize;
387         // Disable button if data is 0
388         if(mAppButtonState != AppButtonStates.NONE){
389             mAppButton.setText(mAppButtonText);
390             if((mAppButtonState == AppButtonStates.CLEAR_DATA) && (data == 0)) {
391                 mAppButton.setEnabled(false);
392             } else {
393                 mAppButton.setEnabled(true);
394                 mAppButton.setOnClickListener(this);
395             }
396         }
397         refreshCacheInfo(newPs.cacheSize);
398     }
399 
refreshCacheInfo(long cacheSize)400     private void refreshCacheInfo(long cacheSize) {
401         // Set cache info
402         mCacheSize.setText(getSizeStr(cacheSize));
403         if (cacheSize <= 0) {
404             mClearCacheButton.setEnabled(false);
405         } else {
406             mClearCacheButton.setOnClickListener(this);
407         }
408     }
409 
410     /*
411      * Private method to handle clear message notification from observer when
412      * the async operation from PackageManager is complete
413      */
processClearMsg(Message msg)414     private void processClearMsg(Message msg) {
415         int result = msg.arg1;
416         String packageName = mAppInfo.packageName;
417         if(result == OP_SUCCESSFUL) {
418             Log.i(TAG, "Cleared user data for system package:"+packageName);
419             mPm.getPackageSizeInfo(packageName, mSizeObserver);
420         } else {
421             mAppButton.setText(R.string.clear_user_data_text);
422             mAppButton.setEnabled(true);
423         }
424     }
425 
426     /*
427      * Private method to initiate clearing user data when the user clicks the clear data
428      * button for a system package
429      */
initiateClearUserDataForSysPkg()430     private  void initiateClearUserDataForSysPkg() {
431         mAppButton.setEnabled(false);
432         //invoke uninstall or clear user data based on sysPackage
433         String packageName = mAppInfo.packageName;
434         Log.i(TAG, "Clearing user data for system package");
435         if(mClearDataObserver == null) {
436             mClearDataObserver = new ClearUserDataObserver();
437         }
438         ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
439         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
440         if(!res) {
441             // Clearing data failed for some obscure reason. Just log error for now
442             Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
443             showDialogInner(DLG_CANNOT_CLEAR_DATA);
444         } else {
445                 mAppButton.setText(R.string.recompute_size);
446         }
447     }
448 
showDialogInner(int id)449     private void showDialogInner(int id) {
450         //removeDialog(id);
451         showDialog(id);
452     }
453 
454     @Override
onCreateDialog(int id)455     public Dialog onCreateDialog(int id) {
456         switch (id) {
457         case DLG_CLEAR_DATA:
458             return new AlertDialog.Builder(this)
459             .setTitle(getString(R.string.clear_data_dlg_title))
460             .setIcon(android.R.drawable.ic_dialog_alert)
461             .setMessage(getString(R.string.clear_data_dlg_text))
462             .setPositiveButton(R.string.dlg_ok, this)
463             .setNegativeButton(R.string.dlg_cancel, this)
464             .create();
465         case DLG_FACTORY_RESET:
466             return new AlertDialog.Builder(this)
467             .setTitle(getString(R.string.app_factory_reset_dlg_title))
468             .setIcon(android.R.drawable.ic_dialog_alert)
469             .setMessage(getString(R.string.app_factory_reset_dlg_text))
470             .setPositiveButton(R.string.dlg_ok, this)
471             .setNegativeButton(R.string.dlg_cancel, this)
472             .create();
473         case DLG_APP_NOT_FOUND:
474             return new AlertDialog.Builder(this)
475             .setTitle(getString(R.string.app_not_found_dlg_title))
476             .setIcon(android.R.drawable.ic_dialog_alert)
477             .setMessage(getString(R.string.app_not_found_dlg_title))
478             .setNeutralButton(getString(R.string.dlg_ok),
479                     new DialogInterface.OnClickListener() {
480                 public void onClick(DialogInterface dialog, int which) {
481                     //force to recompute changed value
482                     setIntentAndFinish(true, true);
483                 }
484             })
485             .create();
486         case DLG_CANNOT_CLEAR_DATA:
487             return new AlertDialog.Builder(this)
488             .setTitle(getString(R.string.clear_failed_dlg_title))
489             .setIcon(android.R.drawable.ic_dialog_alert)
490             .setMessage(getString(R.string.clear_failed_dlg_text))
491             .setNeutralButton(R.string.dlg_ok,
492                     new DialogInterface.OnClickListener() {
493                 public void onClick(DialogInterface dialog, int which) {
494                     //force to recompute changed value
495                     setIntentAndFinish(false, false);
496                 }
497             })
498             .create();
499         }
500         return null;
501     }
502 
503     private void uninstallPkg(String packageName) {
504          // Create new intent to launch Uninstaller activity
505         Uri packageURI = Uri.parse("package:"+packageName);
506         Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
507         startActivity(uninstallIntent);
508         setIntentAndFinish(true, true);
509     }
510 
511     /*
512      * Method implementing functionality of buttons clicked
513      * @see android.view.View.OnClickListener#onClick(android.view.View)
514      */
515     public void onClick(View v) {
516         String packageName = mAppInfo.packageName;
517         if(v == mAppButton) {
518             if (mAppButtonState == AppButtonStates.CLEAR_DATA) {
519                 showDialogInner(DLG_CLEAR_DATA);
520             } else if (mAppButtonState == AppButtonStates.FACTORY_RESET) {
521                 showDialogInner(DLG_FACTORY_RESET);
522             } else if (mAppButtonState == AppButtonStates.UNINSTALL) {
523                 uninstallPkg(packageName);
524             }
525         } else if(v == mActivitiesButton) {
526             mPm.clearPackagePreferredActivities(packageName);
527             mActivitiesButton.setEnabled(false);
528         } else if(v == mManageSpaceButton) {
529             Intent intent = new Intent(Intent.ACTION_DEFAULT);
530             intent.setClassName(mAppInfo.packageName, mAppInfo.manageSpaceActivityName);
531             startActivityForResult(intent, -1);
532         } else if (v == mClearCacheButton) {
533             // Lazy initialization of observer
534             if (mClearCacheObserver == null) {
535                 mClearCacheObserver = new ClearCacheObserver();
536             }
537             mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
538         } else if (v == mForceStopButton) {
539             ActivityManager am = (ActivityManager)getSystemService(
540                     Context.ACTIVITY_SERVICE);
541             am.restartPackage(packageName);
542         }
543     }
544 
545     public void onClick(DialogInterface dialog, int which) {
546         if(which == AlertDialog.BUTTON_POSITIVE) {
547             if (mAppButtonState == AppButtonStates.CLEAR_DATA) {
548                 // Invoke uninstall or clear user data based on sysPackage
549                 initiateClearUserDataForSysPkg();
550             } else if (mAppButtonState == AppButtonStates.FACTORY_RESET) {
551                 // Initiate package installer to delete package
552                 uninstallPkg(mAppInfo.packageName);
553             }
554         } else {
555             //cancel do nothing just retain existing screen
556         }
557     }
558 }
559 
560