• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2007 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import com.android.settings.R;
20 import com.android.settings.Utils;
21 import com.android.settings.applications.ApplicationsState.AppEntry;
22 
23 import android.app.Activity;
24 import android.app.ActivityManager;
25 import android.app.AlertDialog;
26 import android.app.Dialog;
27 import android.app.DialogFragment;
28 import android.app.Fragment;
29 import android.app.INotificationManager;
30 import android.app.admin.DevicePolicyManager;
31 import android.appwidget.AppWidgetManager;
32 import android.content.BroadcastReceiver;
33 import android.content.ComponentName;
34 import android.content.Context;
35 import android.content.DialogInterface;
36 import android.content.Intent;
37 import android.content.IntentFilter;
38 import android.content.pm.ApplicationInfo;
39 import android.content.pm.IPackageDataObserver;
40 import android.content.pm.IPackageMoveObserver;
41 import android.content.pm.PackageInfo;
42 import android.content.pm.PackageManager;
43 import android.content.pm.ResolveInfo;
44 import android.content.pm.PackageManager.NameNotFoundException;
45 import android.content.res.Resources;
46 import android.hardware.usb.IUsbManager;
47 import android.net.Uri;
48 import android.os.AsyncTask;
49 import android.os.Bundle;
50 import android.os.Environment;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Message;
54 import android.os.RemoteException;
55 import android.os.ServiceManager;
56 import android.preference.PreferenceActivity;
57 import android.text.SpannableString;
58 import android.text.TextUtils;
59 import android.text.format.Formatter;
60 import android.text.style.BulletSpan;
61 import android.util.Log;
62 
63 import java.lang.ref.WeakReference;
64 import java.util.ArrayList;
65 import java.util.List;
66 import android.view.LayoutInflater;
67 import android.view.View;
68 import android.view.ViewGroup;
69 import android.widget.AppSecurityPermissions;
70 import android.widget.Button;
71 import android.widget.CheckBox;
72 import android.widget.CompoundButton;
73 import android.widget.ImageView;
74 import android.widget.LinearLayout;
75 import android.widget.TextView;
76 
77 /**
78  * Activity to display application information from Settings. This activity presents
79  * extended information associated with a package like code, data, total size, permissions
80  * used by the application and also the set of default launchable activities.
81  * For system applications, an option to clear user data is displayed only if data size is > 0.
82  * System applications that do not want clear user data do not have this option.
83  * For non-system applications, there is no option to clear data. Instead there is an option to
84  * uninstall the application.
85  */
86 public class InstalledAppDetails extends Fragment
87         implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
88         ApplicationsState.Callbacks {
89     private static final String TAG="InstalledAppDetails";
90     static final boolean SUPPORT_DISABLE_APPS = true;
91     private static final boolean localLOGV = false;
92 
93     public static final String ARG_PACKAGE_NAME = "package";
94 
95     private PackageManager mPm;
96     private IUsbManager mUsbManager;
97     private AppWidgetManager mAppWidgetManager;
98     private DevicePolicyManager mDpm;
99     private ApplicationsState mState;
100     private ApplicationsState.Session mSession;
101     private ApplicationsState.AppEntry mAppEntry;
102     private PackageInfo mPackageInfo;
103     private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
104     private View mRootView;
105     private Button mUninstallButton;
106     private boolean mMoveInProgress = false;
107     private boolean mUpdatedSysApp = false;
108     private Button mActivitiesButton;
109     private View mScreenCompatSection;
110     private CheckBox mAskCompatibilityCB;
111     private CheckBox mEnableCompatibilityCB;
112     private boolean mCanClearData = true;
113     private TextView mAppVersion;
114     private TextView mTotalSize;
115     private TextView mAppSize;
116     private TextView mDataSize;
117     private TextView mExternalCodeSize;
118     private TextView mExternalDataSize;
119     private ClearUserDataObserver mClearDataObserver;
120     // Views related to cache info
121     private TextView mCacheSize;
122     private Button mClearCacheButton;
123     private ClearCacheObserver mClearCacheObserver;
124     private Button mForceStopButton;
125     private Button mClearDataButton;
126     private Button mMoveAppButton;
127     private CompoundButton mNotificationSwitch;
128 
129     private PackageMoveObserver mPackageMoveObserver;
130 
131     private boolean mHaveSizes = false;
132     private long mLastCodeSize = -1;
133     private long mLastDataSize = -1;
134     private long mLastExternalCodeSize = -1;
135     private long mLastExternalDataSize = -1;
136     private long mLastCacheSize = -1;
137     private long mLastTotalSize = -1;
138 
139     //internal constants used in Handler
140     private static final int OP_SUCCESSFUL = 1;
141     private static final int OP_FAILED = 2;
142     private static final int CLEAR_USER_DATA = 1;
143     private static final int CLEAR_CACHE = 3;
144     private static final int PACKAGE_MOVE = 4;
145 
146     // invalid size value used initially and also when size retrieval through PackageManager
147     // fails for whatever reason
148     private static final int SIZE_INVALID = -1;
149 
150     // Resource strings
151     private CharSequence mInvalidSizeStr;
152     private CharSequence mComputingStr;
153 
154     // Dialog identifiers used in showDialog
155     private static final int DLG_BASE = 0;
156     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
157     private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
158     private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
159     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
160     private static final int DLG_FORCE_STOP = DLG_BASE + 5;
161     private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
162     private static final int DLG_DISABLE = DLG_BASE + 7;
163     private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
164 
165     private Handler mHandler = new Handler() {
166         public void handleMessage(Message msg) {
167             // If the fragment is gone, don't process any more messages.
168             if (getView() == null) {
169                 return;
170             }
171             switch (msg.what) {
172                 case CLEAR_USER_DATA:
173                     processClearMsg(msg);
174                     break;
175                 case CLEAR_CACHE:
176                     // Refresh size info
177                     mState.requestSize(mAppEntry.info.packageName);
178                     break;
179                 case PACKAGE_MOVE:
180                     processMoveMsg(msg);
181                     break;
182                 default:
183                     break;
184             }
185         }
186     };
187 
188     class ClearUserDataObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)189        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
190            final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
191            msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
192            mHandler.sendMessage(msg);
193         }
194     }
195 
196     class ClearCacheObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)197         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
198             final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
199             msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
200             mHandler.sendMessage(msg);
201          }
202      }
203 
204     class PackageMoveObserver extends IPackageMoveObserver.Stub {
packageMoved(String packageName, int returnCode)205         public void packageMoved(String packageName, int returnCode) throws RemoteException {
206             final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
207             msg.arg1 = returnCode;
208             mHandler.sendMessage(msg);
209         }
210     }
211 
getSizeStr(long size)212     private String getSizeStr(long size) {
213         if (size == SIZE_INVALID) {
214             return mInvalidSizeStr.toString();
215         }
216         return Formatter.formatFileSize(getActivity(), size);
217     }
218 
initDataButtons()219     private void initDataButtons() {
220         if ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
221                 | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
222                 == ApplicationInfo.FLAG_SYSTEM
223                 || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
224             mClearDataButton.setText(R.string.clear_user_data_text);
225             mClearDataButton.setEnabled(false);
226             mCanClearData = false;
227         } else {
228             if (mAppEntry.info.manageSpaceActivityName != null) {
229                 mClearDataButton.setText(R.string.manage_space_text);
230             } else {
231                 mClearDataButton.setText(R.string.clear_user_data_text);
232             }
233             mClearDataButton.setOnClickListener(this);
234         }
235     }
236 
getMoveErrMsg(int errCode)237     private CharSequence getMoveErrMsg(int errCode) {
238         switch (errCode) {
239             case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
240                 return getActivity().getString(R.string.insufficient_storage);
241             case PackageManager.MOVE_FAILED_DOESNT_EXIST:
242                 return getActivity().getString(R.string.does_not_exist);
243             case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
244                 return getActivity().getString(R.string.app_forward_locked);
245             case PackageManager.MOVE_FAILED_INVALID_LOCATION:
246                 return getActivity().getString(R.string.invalid_location);
247             case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
248                 return getActivity().getString(R.string.system_package);
249             case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
250                 return "";
251         }
252         return "";
253     }
254 
initMoveButton()255     private void initMoveButton() {
256         if (Environment.isExternalStorageEmulated()) {
257             mMoveAppButton.setVisibility(View.INVISIBLE);
258             return;
259         }
260         boolean dataOnly = false;
261         dataOnly = (mPackageInfo == null) && (mAppEntry != null);
262         boolean moveDisable = true;
263         if (dataOnly) {
264             mMoveAppButton.setText(R.string.move_app);
265         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
266             mMoveAppButton.setText(R.string.move_app_to_internal);
267             // Always let apps move to internal storage from sdcard.
268             moveDisable = false;
269         } else {
270             mMoveAppButton.setText(R.string.move_app_to_sdcard);
271             mCanBeOnSdCardChecker.init();
272             moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
273         }
274         if (moveDisable) {
275             mMoveAppButton.setEnabled(false);
276         } else {
277             mMoveAppButton.setOnClickListener(this);
278             mMoveAppButton.setEnabled(true);
279         }
280     }
281 
isThisASystemPackage()282     private boolean isThisASystemPackage() {
283         try {
284             PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
285             return (mPackageInfo != null && mPackageInfo.signatures != null &&
286                     sys.signatures[0].equals(mPackageInfo.signatures[0]));
287         } catch (PackageManager.NameNotFoundException e) {
288             return false;
289         }
290     }
291 
initUninstallButtons()292     private void initUninstallButtons() {
293         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
294         boolean enabled = true;
295         if (mUpdatedSysApp) {
296             mUninstallButton.setText(R.string.app_factory_reset);
297         } else {
298             if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
299                 enabled = false;
300                 if (SUPPORT_DISABLE_APPS) {
301                     try {
302                         // Try to prevent the user from bricking their phone
303                         // by not allowing disabling of apps signed with the
304                         // system cert and any launcher app in the system.
305                         PackageInfo sys = mPm.getPackageInfo("android",
306                                 PackageManager.GET_SIGNATURES);
307                         Intent intent = new Intent(Intent.ACTION_MAIN);
308                         intent.addCategory(Intent.CATEGORY_HOME);
309                         intent.setPackage(mAppEntry.info.packageName);
310                         List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
311                         if ((homes != null && homes.size() > 0) || isThisASystemPackage()) {
312                             // Disable button for core system applications.
313                             mUninstallButton.setText(R.string.disable_text);
314                         } else if (mAppEntry.info.enabled) {
315                             mUninstallButton.setText(R.string.disable_text);
316                             enabled = true;
317                         } else {
318                             mUninstallButton.setText(R.string.enable_text);
319                             enabled = true;
320                         }
321                     } catch (PackageManager.NameNotFoundException e) {
322                         Log.w(TAG, "Unable to get package info", e);
323                     }
324                 }
325             } else {
326                 mUninstallButton.setText(R.string.uninstall_text);
327             }
328         }
329         // If this is a device admin, it can't be uninstall or disabled.
330         // We do this here so the text of the button is still set correctly.
331         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
332             enabled = false;
333         }
334         mUninstallButton.setEnabled(enabled);
335         if (enabled) {
336             // Register listener
337             mUninstallButton.setOnClickListener(this);
338         }
339     }
340 
initNotificationButton()341     private void initNotificationButton() {
342         INotificationManager nm = INotificationManager.Stub.asInterface(
343                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
344         boolean enabled = true; // default on
345         try {
346             enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName);
347         } catch (android.os.RemoteException ex) {
348             // this does not bode well
349         }
350         mNotificationSwitch.setChecked(enabled);
351         if (isThisASystemPackage()) {
352             mNotificationSwitch.setEnabled(false);
353         } else {
354             mNotificationSwitch.setEnabled(true);
355             mNotificationSwitch.setOnCheckedChangeListener(this);
356         }
357     }
358 
359     /** Called when the activity is first created. */
360     @Override
onCreate(Bundle icicle)361     public void onCreate(Bundle icicle) {
362         super.onCreate(icicle);
363 
364         mState = ApplicationsState.getInstance(getActivity().getApplication());
365         mSession = mState.newSession(this);
366         mPm = getActivity().getPackageManager();
367         IBinder b = ServiceManager.getService(Context.USB_SERVICE);
368         mUsbManager = IUsbManager.Stub.asInterface(b);
369         mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
370         mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
371 
372         mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
373     }
374 
375     @Override
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)376     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
377         View view = mRootView = inflater.inflate(R.layout.installed_app_details, null);
378 
379         mComputingStr = getActivity().getText(R.string.computing_size);
380 
381         // Set default values on sizes
382         mTotalSize = (TextView)view.findViewById(R.id.total_size_text);
383         mAppSize = (TextView)view.findViewById(R.id.application_size_text);
384         mDataSize = (TextView)view.findViewById(R.id.data_size_text);
385         mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text);
386         mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text);
387 
388         // Get Control button panel
389         View btnPanel = view.findViewById(R.id.control_buttons_panel);
390         mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
391         mForceStopButton.setText(R.string.force_stop);
392         mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
393         mForceStopButton.setEnabled(false);
394 
395         // Initialize clear data and move install location buttons
396         View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
397         mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
398         mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
399 
400         // Cache section
401         mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
402         mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
403 
404         mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button);
405 
406         // Screen compatibility control
407         mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
408         mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
409         mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
410 
411         mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
412 
413         return view;
414     }
415 
416     // Utility method to set applicaiton label and icon.
setAppLabelAndIcon(PackageInfo pkgInfo)417     private void setAppLabelAndIcon(PackageInfo pkgInfo) {
418         View appSnippet = mRootView.findViewById(R.id.app_snippet);
419         ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
420         mState.ensureIcon(mAppEntry);
421         icon.setImageDrawable(mAppEntry.icon);
422         // Set application name.
423         TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
424         label.setText(mAppEntry.label);
425         // Version number of application
426         mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
427 
428         if (pkgInfo != null && pkgInfo.versionName != null) {
429             mAppVersion.setVisibility(View.VISIBLE);
430             mAppVersion.setText(getActivity().getString(R.string.version_text,
431                     String.valueOf(pkgInfo.versionName)));
432         } else {
433             mAppVersion.setVisibility(View.INVISIBLE);
434         }
435     }
436 
437     @Override
onResume()438     public void onResume() {
439         super.onResume();
440 
441         mSession.resume();
442         if (!refreshUi()) {
443             setIntentAndFinish(true, true);
444         }
445     }
446 
447     @Override
onPause()448     public void onPause() {
449         super.onPause();
450         mSession.pause();
451     }
452 
453     @Override
onAllSizesComputed()454     public void onAllSizesComputed() {
455     }
456 
457     @Override
onPackageIconChanged()458     public void onPackageIconChanged() {
459     }
460 
461     @Override
onPackageListChanged()462     public void onPackageListChanged() {
463         refreshUi();
464     }
465 
466     @Override
onRebuildComplete(ArrayList<AppEntry> apps)467     public void onRebuildComplete(ArrayList<AppEntry> apps) {
468     }
469 
470     @Override
onPackageSizeChanged(String packageName)471     public void onPackageSizeChanged(String packageName) {
472         if (packageName.equals(mAppEntry.info.packageName)) {
473             refreshSizeInfo();
474         }
475     }
476 
477     @Override
onRunningStateChanged(boolean running)478     public void onRunningStateChanged(boolean running) {
479     }
480 
refreshUi()481     private boolean refreshUi() {
482         if (mMoveInProgress) {
483             return true;
484         }
485         final Bundle args = getArguments();
486         String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
487         if (packageName == null) {
488             Intent intent = (args == null) ?
489                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
490             if (intent != null) {
491                 packageName = intent.getData().getSchemeSpecificPart();
492             }
493         }
494         mAppEntry = mState.getEntry(packageName);
495 
496         if (mAppEntry == null) {
497             return false; // onCreate must have failed, make sure to exit
498         }
499 
500         // Get application info again to refresh changed properties of application
501         try {
502             mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
503                     PackageManager.GET_DISABLED_COMPONENTS |
504                     PackageManager.GET_UNINSTALLED_PACKAGES |
505                     PackageManager.GET_SIGNATURES);
506         } catch (NameNotFoundException e) {
507             Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
508             return false; // onCreate must have failed, make sure to exit
509         }
510 
511         // Get list of preferred activities
512         List<ComponentName> prefActList = new ArrayList<ComponentName>();
513 
514         // Intent list cannot be null. so pass empty list
515         List<IntentFilter> intentList = new ArrayList<IntentFilter>();
516         mPm.getPreferredActivities(intentList, prefActList, packageName);
517         if (localLOGV)
518             Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
519         boolean hasUsbDefaults = false;
520         try {
521             hasUsbDefaults = mUsbManager.hasDefaults(packageName);
522         } catch (RemoteException e) {
523             Log.e(TAG, "mUsbManager.hasDefaults", e);
524         }
525         boolean hasBindAppWidgetPermission =
526                 mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
527 
528         TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
529         TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
530         boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
531         if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
532             resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
533         } else {
534             boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
535 
536             if (hasBindAppWidgetPermission) {
537                 autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
538             } else {
539                 autoLaunchTitleView.setText(R.string.auto_launch_label);
540             }
541 
542             CharSequence text = null;
543             int bulletIndent = getResources()
544                     .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
545             if (autoLaunchEnabled) {
546                 CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
547                 SpannableString s = new SpannableString(autoLaunchEnableText);
548                 if (useBullets) {
549                     s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
550                 }
551                 text = (text == null) ?
552                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
553             }
554             if (hasBindAppWidgetPermission) {
555                 CharSequence alwaysAllowBindAppWidgetsText =
556                         getText(R.string.always_allow_bind_appwidgets_text);
557                 SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
558                 if (useBullets) {
559                     s.setSpan(new BulletSpan(bulletIndent),
560                             0, alwaysAllowBindAppWidgetsText.length(), 0);
561                 }
562                 text = (text == null) ?
563                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
564             }
565             autoLaunchView.setText(text);
566             mActivitiesButton.setEnabled(true);
567             mActivitiesButton.setOnClickListener(this);
568         }
569 
570         // Screen compatibility section.
571         ActivityManager am = (ActivityManager)
572                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
573         int compatMode = am.getPackageScreenCompatMode(packageName);
574         // For now these are always off; this is the old UI model which we
575         // are no longer using.
576         if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
577                 || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
578             mScreenCompatSection.setVisibility(View.VISIBLE);
579             mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
580             mAskCompatibilityCB.setOnCheckedChangeListener(this);
581             mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED);
582             mEnableCompatibilityCB.setOnCheckedChangeListener(this);
583         } else {
584             mScreenCompatSection.setVisibility(View.GONE);
585         }
586 
587         // Security permissions section
588         LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
589         AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
590         if (asp.getPermissionCount() > 0) {
591             permsView.setVisibility(View.VISIBLE);
592             // Make the security sections header visible
593             LinearLayout securityList = (LinearLayout) permsView.findViewById(
594                     R.id.security_settings_list);
595             securityList.removeAllViews();
596             securityList.addView(asp.getPermissionsView());
597             // If this app is running under a shared user ID with other apps,
598             // update the description to explain this.
599             String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid);
600             if (packages != null && packages.length > 1) {
601                 ArrayList<CharSequence> pnames = new ArrayList<CharSequence>();
602                 for (int i=0; i<packages.length; i++) {
603                     String pkg = packages[i];
604                     if (mPackageInfo.packageName.equals(pkg)) {
605                         continue;
606                     }
607                     try {
608                         ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0);
609                         pnames.add(ainfo.loadLabel(mPm));
610                     } catch (PackageManager.NameNotFoundException e) {
611                     }
612                 }
613                 final int N = pnames.size();
614                 if (N > 0) {
615                     final Resources res = getActivity().getResources();
616                     String appListStr;
617                     if (N == 1) {
618                         appListStr = pnames.get(0).toString();
619                     } else if (N == 2) {
620                         appListStr = res.getString(R.string.join_two_items, pnames.get(0),
621                                 pnames.get(1));
622                     } else {
623                         appListStr = pnames.get(N-2).toString();
624                         for (int i=N-3; i>=0; i--) {
625                             appListStr = res.getString(i == 0 ? R.string.join_many_items_first
626                                     : R.string.join_many_items_middle, pnames.get(i), appListStr);
627                         }
628                         appListStr = res.getString(R.string.join_many_items_last,
629                                 appListStr, pnames.get(N-1));
630                     }
631                     TextView descr = (TextView) mRootView.findViewById(
632                             R.id.security_settings_desc);
633                     descr.setText(res.getString(R.string.security_settings_desc_multi,
634                             mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
635                 }
636             }
637         } else {
638             permsView.setVisibility(View.GONE);
639         }
640 
641         checkForceStop();
642         setAppLabelAndIcon(mPackageInfo);
643         refreshButtons();
644         refreshSizeInfo();
645         return true;
646     }
647 
resetLaunchDefaultsUi(TextView title, TextView autoLaunchView)648     private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
649         title.setText(R.string.auto_launch_label);
650         autoLaunchView.setText(R.string.auto_launch_disable_text);
651         // Disable clear activities button
652         mActivitiesButton.setEnabled(false);
653     }
654 
setIntentAndFinish(boolean finish, boolean appChanged)655     private void setIntentAndFinish(boolean finish, boolean appChanged) {
656         if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
657         Intent intent = new Intent();
658         intent.putExtra(ManageApplications.APP_CHG, appChanged);
659         PreferenceActivity pa = (PreferenceActivity)getActivity();
660         pa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
661     }
662 
refreshSizeInfo()663     private void refreshSizeInfo() {
664         if (mAppEntry.size == ApplicationsState.SIZE_INVALID
665                 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
666             mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
667             if (!mHaveSizes) {
668                 mAppSize.setText(mComputingStr);
669                 mDataSize.setText(mComputingStr);
670                 mCacheSize.setText(mComputingStr);
671                 mTotalSize.setText(mComputingStr);
672             }
673             mClearDataButton.setEnabled(false);
674             mClearCacheButton.setEnabled(false);
675 
676         } else {
677             mHaveSizes = true;
678             if (mLastCodeSize != mAppEntry.codeSize) {
679                 mLastCodeSize = mAppEntry.codeSize;
680                 mAppSize.setText(getSizeStr(mAppEntry.codeSize));
681             }
682             if (mLastDataSize != mAppEntry.dataSize) {
683                 mLastDataSize = mAppEntry.dataSize;
684                 mDataSize.setText(getSizeStr(mAppEntry.dataSize));
685             }
686             if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
687                 mLastExternalCodeSize = mAppEntry.externalCodeSize;
688                 mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize));
689             }
690             long nonCacheExtDataSize = mAppEntry.externalDataSize - mAppEntry.externalCacheSize;
691             if (mLastExternalDataSize != nonCacheExtDataSize) {
692                 mLastExternalDataSize = nonCacheExtDataSize;
693                 mExternalDataSize.setText(getSizeStr(nonCacheExtDataSize));
694             }
695             long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
696             if (mLastCacheSize != cacheSize) {
697                 mLastCacheSize = cacheSize;
698                 mCacheSize.setText(getSizeStr(cacheSize));
699             }
700             if (mLastTotalSize != mAppEntry.size) {
701                 mLastTotalSize = mAppEntry.size;
702                 mTotalSize.setText(getSizeStr(mAppEntry.size));
703             }
704 
705             if ((mAppEntry.dataSize+nonCacheExtDataSize) <= 0 || !mCanClearData) {
706                 mClearDataButton.setEnabled(false);
707             } else {
708                 mClearDataButton.setEnabled(true);
709                 mClearDataButton.setOnClickListener(this);
710             }
711             if (cacheSize <= 0) {
712                 mClearCacheButton.setEnabled(false);
713             } else {
714                 mClearCacheButton.setEnabled(true);
715                 mClearCacheButton.setOnClickListener(this);
716             }
717         }
718     }
719 
720     /*
721      * Private method to handle clear message notification from observer when
722      * the async operation from PackageManager is complete
723      */
processClearMsg(Message msg)724     private void processClearMsg(Message msg) {
725         int result = msg.arg1;
726         String packageName = mAppEntry.info.packageName;
727         mClearDataButton.setText(R.string.clear_user_data_text);
728         if(result == OP_SUCCESSFUL) {
729             Log.i(TAG, "Cleared user data for package : "+packageName);
730             mState.requestSize(mAppEntry.info.packageName);
731         } else {
732             mClearDataButton.setEnabled(true);
733         }
734         checkForceStop();
735     }
736 
refreshButtons()737     private void refreshButtons() {
738         if (!mMoveInProgress) {
739             initUninstallButtons();
740             initDataButtons();
741             initMoveButton();
742             initNotificationButton();
743         } else {
744             mMoveAppButton.setText(R.string.moving);
745             mMoveAppButton.setEnabled(false);
746             mUninstallButton.setEnabled(false);
747         }
748     }
749 
processMoveMsg(Message msg)750     private void processMoveMsg(Message msg) {
751         int result = msg.arg1;
752         String packageName = mAppEntry.info.packageName;
753         // Refresh the button attributes.
754         mMoveInProgress = false;
755         if (result == PackageManager.MOVE_SUCCEEDED) {
756             Log.i(TAG, "Moved resources for " + packageName);
757             // Refresh size information again.
758             mState.requestSize(mAppEntry.info.packageName);
759         } else {
760             showDialogInner(DLG_MOVE_FAILED, result);
761         }
762         refreshUi();
763     }
764 
765     /*
766      * Private method to initiate clearing user data when the user clicks the clear data
767      * button for a system package
768      */
initiateClearUserData()769     private  void initiateClearUserData() {
770         mClearDataButton.setEnabled(false);
771         // Invoke uninstall or clear user data based on sysPackage
772         String packageName = mAppEntry.info.packageName;
773         Log.i(TAG, "Clearing user data for package : " + packageName);
774         if (mClearDataObserver == null) {
775             mClearDataObserver = new ClearUserDataObserver();
776         }
777         ActivityManager am = (ActivityManager)
778                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
779         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
780         if (!res) {
781             // Clearing data failed for some obscure reason. Just log error for now
782             Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
783             showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
784         } else {
785             mClearDataButton.setText(R.string.recompute_size);
786         }
787     }
788 
showDialogInner(int id, int moveErrorCode)789     private void showDialogInner(int id, int moveErrorCode) {
790         DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
791         newFragment.setTargetFragment(this, 0);
792         newFragment.show(getFragmentManager(), "dialog " + id);
793     }
794 
795     public static class MyAlertDialogFragment extends DialogFragment {
796 
newInstance(int id, int moveErrorCode)797         public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
798             MyAlertDialogFragment frag = new MyAlertDialogFragment();
799             Bundle args = new Bundle();
800             args.putInt("id", id);
801             args.putInt("moveError", moveErrorCode);
802             frag.setArguments(args);
803             return frag;
804         }
805 
getOwner()806         InstalledAppDetails getOwner() {
807             return (InstalledAppDetails)getTargetFragment();
808         }
809 
810         @Override
onCreateDialog(Bundle savedInstanceState)811         public Dialog onCreateDialog(Bundle savedInstanceState) {
812             int id = getArguments().getInt("id");
813             int moveErrorCode = getArguments().getInt("moveError");
814             switch (id) {
815                 case DLG_CLEAR_DATA:
816                     return new AlertDialog.Builder(getActivity())
817                     .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
818                     .setIcon(android.R.drawable.ic_dialog_alert)
819                     .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
820                     .setPositiveButton(R.string.dlg_ok,
821                             new DialogInterface.OnClickListener() {
822                         public void onClick(DialogInterface dialog, int which) {
823                             // Clear user data here
824                             getOwner().initiateClearUserData();
825                         }
826                     })
827                     .setNegativeButton(R.string.dlg_cancel, null)
828                     .create();
829                 case DLG_FACTORY_RESET:
830                     return new AlertDialog.Builder(getActivity())
831                     .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
832                     .setIcon(android.R.drawable.ic_dialog_alert)
833                     .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
834                     .setPositiveButton(R.string.dlg_ok,
835                             new DialogInterface.OnClickListener() {
836                         public void onClick(DialogInterface dialog, int which) {
837                             // Clear user data here
838                             getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName);
839                         }
840                     })
841                     .setNegativeButton(R.string.dlg_cancel, null)
842                     .create();
843                 case DLG_APP_NOT_FOUND:
844                     return new AlertDialog.Builder(getActivity())
845                     .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
846                     .setIcon(android.R.drawable.ic_dialog_alert)
847                     .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
848                     .setNeutralButton(getActivity().getText(R.string.dlg_ok),
849                             new DialogInterface.OnClickListener() {
850                         public void onClick(DialogInterface dialog, int which) {
851                             //force to recompute changed value
852                             getOwner().setIntentAndFinish(true, true);
853                         }
854                     })
855                     .create();
856                 case DLG_CANNOT_CLEAR_DATA:
857                     return new AlertDialog.Builder(getActivity())
858                     .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
859                     .setIcon(android.R.drawable.ic_dialog_alert)
860                     .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
861                     .setNeutralButton(R.string.dlg_ok,
862                             new DialogInterface.OnClickListener() {
863                         public void onClick(DialogInterface dialog, int which) {
864                             getOwner().mClearDataButton.setEnabled(false);
865                             //force to recompute changed value
866                             getOwner().setIntentAndFinish(false, false);
867                         }
868                     })
869                     .create();
870                 case DLG_FORCE_STOP:
871                     return new AlertDialog.Builder(getActivity())
872                     .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
873                     .setIcon(android.R.drawable.ic_dialog_alert)
874                     .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
875                     .setPositiveButton(R.string.dlg_ok,
876                         new DialogInterface.OnClickListener() {
877                         public void onClick(DialogInterface dialog, int which) {
878                             // Force stop
879                             getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
880                         }
881                     })
882                     .setNegativeButton(R.string.dlg_cancel, null)
883                     .create();
884                 case DLG_MOVE_FAILED:
885                     CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
886                             getOwner().getMoveErrMsg(moveErrorCode));
887                     return new AlertDialog.Builder(getActivity())
888                     .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
889                     .setIcon(android.R.drawable.ic_dialog_alert)
890                     .setMessage(msg)
891                     .setNeutralButton(R.string.dlg_ok, null)
892                     .create();
893                 case DLG_DISABLE:
894                     return new AlertDialog.Builder(getActivity())
895                     .setTitle(getActivity().getText(R.string.app_disable_dlg_title))
896                     .setIcon(android.R.drawable.ic_dialog_alert)
897                     .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
898                     .setPositiveButton(R.string.dlg_ok,
899                         new DialogInterface.OnClickListener() {
900                         public void onClick(DialogInterface dialog, int which) {
901                             // Disable the app
902                             new DisableChanger(getOwner(), getOwner().mAppEntry.info,
903                                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
904                             .execute((Object)null);
905                         }
906                     })
907                     .setNegativeButton(R.string.dlg_cancel, null)
908                     .create();
909                 case DLG_DISABLE_NOTIFICATIONS:
910                     return new AlertDialog.Builder(getActivity())
911                     .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
912                     .setIcon(android.R.drawable.ic_dialog_alert)
913                     .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
914                     .setPositiveButton(R.string.dlg_ok,
915                         new DialogInterface.OnClickListener() {
916                         public void onClick(DialogInterface dialog, int which) {
917                             // Disable the package's notifications
918                             getOwner().setNotificationsEnabled(false);
919                         }
920                     })
921                     .setNegativeButton(R.string.dlg_cancel,
922                         new DialogInterface.OnClickListener() {
923                         public void onClick(DialogInterface dialog, int which) {
924                             // Re-enable the checkbox
925                             getOwner().mNotificationSwitch.setChecked(true);
926                         }
927                     })
928                     .create();
929             }
930             throw new IllegalArgumentException("unknown id " + id);
931         }
932     }
933 
934     private void uninstallPkg(String packageName) {
935          // Create new intent to launch Uninstaller activity
936         Uri packageURI = Uri.parse("package:"+packageName);
937         Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
938         startActivity(uninstallIntent);
939         setIntentAndFinish(true, true);
940     }
941 
942     private void forceStopPackage(String pkgName) {
943         ActivityManager am = (ActivityManager)getActivity().getSystemService(
944                 Context.ACTIVITY_SERVICE);
945         am.forceStopPackage(pkgName);
946         mState.invalidatePackage(pkgName);
947         ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
948         if (newEnt != null) {
949             mAppEntry = newEnt;
950         }
951         checkForceStop();
952     }
953 
954     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
955         @Override
956         public void onReceive(Context context, Intent intent) {
957             updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
958         }
959     };
960 
961     private void updateForceStopButton(boolean enabled) {
962         mForceStopButton.setEnabled(enabled);
963         mForceStopButton.setOnClickListener(InstalledAppDetails.this);
964     }
965 
966     private void checkForceStop() {
967         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
968             // User can't force stop device admin.
969             updateForceStopButton(false);
970         } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
971             // If the app isn't explicitly stopped, then always show the
972             // force stop button.
973             updateForceStopButton(true);
974         } else {
975             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
976                     Uri.fromParts("package", mAppEntry.info.packageName, null));
977             intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
978             intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
979             getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
980                     Activity.RESULT_CANCELED, null, null);
981         }
982     }
983 
984     static class DisableChanger extends AsyncTask<Object, Object, Object> {
985         final PackageManager mPm;
986         final WeakReference<InstalledAppDetails> mActivity;
987         final ApplicationInfo mInfo;
988         final int mState;
989 
990         DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
991             mPm = activity.mPm;
992             mActivity = new WeakReference<InstalledAppDetails>(activity);
993             mInfo = info;
994             mState = state;
995         }
996 
997         @Override
998         protected Object doInBackground(Object... params) {
999             mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
1000             return null;
1001         }
1002     }
1003 
1004     private void setNotificationsEnabled(boolean enabled) {
1005         String packageName = mAppEntry.info.packageName;
1006         INotificationManager nm = INotificationManager.Stub.asInterface(
1007                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1008         try {
1009             final boolean enable = mNotificationSwitch.isChecked();
1010             nm.setNotificationsEnabledForPackage(packageName, enabled);
1011         } catch (android.os.RemoteException ex) {
1012             mNotificationSwitch.setChecked(!enabled); // revert
1013         }
1014     }
1015 
1016     /*
1017      * Method implementing functionality of buttons clicked
1018      * @see android.view.View.OnClickListener#onClick(android.view.View)
1019      */
1020     public void onClick(View v) {
1021         String packageName = mAppEntry.info.packageName;
1022         if(v == mUninstallButton) {
1023             if (mUpdatedSysApp) {
1024                 showDialogInner(DLG_FACTORY_RESET, 0);
1025             } else {
1026                 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1027                     if (mAppEntry.info.enabled) {
1028                         showDialogInner(DLG_DISABLE, 0);
1029                     } else {
1030                         new DisableChanger(this, mAppEntry.info,
1031                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
1032                         .execute((Object)null);
1033                     }
1034                 } else {
1035                     uninstallPkg(packageName);
1036                 }
1037             }
1038         } else if(v == mActivitiesButton) {
1039             mPm.clearPackagePreferredActivities(packageName);
1040             try {
1041                 mUsbManager.clearDefaults(packageName);
1042             } catch (RemoteException e) {
1043                 Log.e(TAG, "mUsbManager.clearDefaults", e);
1044             }
1045             mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
1046             TextView autoLaunchTitleView =
1047                     (TextView) mRootView.findViewById(R.id.auto_launch_title);
1048             TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
1049             resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
1050         } else if(v == mClearDataButton) {
1051             if (mAppEntry.info.manageSpaceActivityName != null) {
1052                 if (!Utils.isMonkeyRunning()) {
1053                     Intent intent = new Intent(Intent.ACTION_DEFAULT);
1054                     intent.setClassName(mAppEntry.info.packageName,
1055                             mAppEntry.info.manageSpaceActivityName);
1056                     startActivityForResult(intent, -1);
1057                 }
1058             } else {
1059                 showDialogInner(DLG_CLEAR_DATA, 0);
1060             }
1061         } else if (v == mClearCacheButton) {
1062             // Lazy initialization of observer
1063             if (mClearCacheObserver == null) {
1064                 mClearCacheObserver = new ClearCacheObserver();
1065             }
1066             mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
1067         } else if (v == mForceStopButton) {
1068             showDialogInner(DLG_FORCE_STOP, 0);
1069             //forceStopPackage(mAppInfo.packageName);
1070         } else if (v == mMoveAppButton) {
1071             if (mPackageMoveObserver == null) {
1072                 mPackageMoveObserver = new PackageMoveObserver();
1073             }
1074             int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
1075                     PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
1076             mMoveInProgress = true;
1077             refreshButtons();
1078             mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
1079         }
1080     }
1081 
1082     @Override
1083     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1084         String packageName = mAppEntry.info.packageName;
1085         ActivityManager am = (ActivityManager)
1086                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1087         if (buttonView == mAskCompatibilityCB) {
1088             am.setPackageAskScreenCompat(packageName, isChecked);
1089         } else if (buttonView == mEnableCompatibilityCB) {
1090             am.setPackageScreenCompatMode(packageName, isChecked ?
1091                     ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
1092         } else if (buttonView == mNotificationSwitch) {
1093             if (!isChecked) {
1094                 showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
1095             } else {
1096                 setNotificationsEnabled(true);
1097             }
1098         }
1099     }
1100 }
1101 
1102