• 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.internal.telephony.ISms;
20 import com.android.internal.telephony.SmsUsageMonitor;
21 import com.android.settings.R;
22 import com.android.settings.Utils;
23 import com.android.settings.applications.ApplicationsState.AppEntry;
24 
25 import android.app.Activity;
26 import android.app.ActivityManager;
27 import android.app.AlertDialog;
28 import android.app.Dialog;
29 import android.app.DialogFragment;
30 import android.app.Fragment;
31 import android.app.INotificationManager;
32 import android.app.admin.DevicePolicyManager;
33 import android.appwidget.AppWidgetManager;
34 import android.content.BroadcastReceiver;
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.content.DialogInterface;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.pm.ApplicationInfo;
41 import android.content.pm.IPackageDataObserver;
42 import android.content.pm.IPackageMoveObserver;
43 import android.content.pm.PackageInfo;
44 import android.content.pm.PackageManager;
45 import android.content.pm.ResolveInfo;
46 import android.content.pm.PackageManager.NameNotFoundException;
47 import android.content.res.Resources;
48 import android.hardware.usb.IUsbManager;
49 import android.net.Uri;
50 import android.os.AsyncTask;
51 import android.os.Bundle;
52 import android.os.Environment;
53 import android.os.Handler;
54 import android.os.IBinder;
55 import android.os.Message;
56 import android.os.RemoteException;
57 import android.os.ServiceManager;
58 import android.os.UserHandle;
59 import android.os.UserManager;
60 import android.preference.PreferenceActivity;
61 import android.text.SpannableString;
62 import android.text.TextUtils;
63 import android.text.format.Formatter;
64 import android.text.style.BulletSpan;
65 import android.util.Log;
66 
67 import java.lang.ref.WeakReference;
68 import java.util.ArrayList;
69 import java.util.List;
70 import android.view.LayoutInflater;
71 import android.view.Menu;
72 import android.view.MenuInflater;
73 import android.view.MenuItem;
74 import android.view.View;
75 import android.view.ViewGroup;
76 import android.widget.AdapterView;
77 import android.widget.AppSecurityPermissions;
78 import android.widget.ArrayAdapter;
79 import android.widget.Button;
80 import android.widget.CheckBox;
81 import android.widget.CompoundButton;
82 import android.widget.ImageView;
83 import android.widget.LinearLayout;
84 import android.widget.Spinner;
85 import android.widget.TextView;
86 
87 /**
88  * Activity to display application information from Settings. This activity presents
89  * extended information associated with a package like code, data, total size, permissions
90  * used by the application and also the set of default launchable activities.
91  * For system applications, an option to clear user data is displayed only if data size is > 0.
92  * System applications that do not want clear user data do not have this option.
93  * For non-system applications, there is no option to clear data. Instead there is an option to
94  * uninstall the application.
95  */
96 public class InstalledAppDetails extends Fragment
97         implements View.OnClickListener, CompoundButton.OnCheckedChangeListener,
98         ApplicationsState.Callbacks {
99     private static final String TAG="InstalledAppDetails";
100     private static final boolean localLOGV = false;
101 
102     public static final String ARG_PACKAGE_NAME = "package";
103 
104     private PackageManager mPm;
105     private UserManager mUserManager;
106     private IUsbManager mUsbManager;
107     private AppWidgetManager mAppWidgetManager;
108     private DevicePolicyManager mDpm;
109     private ISms mSmsManager;
110     private ApplicationsState mState;
111     private ApplicationsState.Session mSession;
112     private ApplicationsState.AppEntry mAppEntry;
113     private boolean mInitialized;
114     private boolean mShowUninstalled;
115     private PackageInfo mPackageInfo;
116     private CanBeOnSdCardChecker mCanBeOnSdCardChecker;
117     private View mRootView;
118     private Button mUninstallButton;
119     private View mMoreControlButtons;
120     private Button mSpecialDisableButton;
121     private boolean mMoveInProgress = false;
122     private boolean mUpdatedSysApp = false;
123     private Button mActivitiesButton;
124     private View mScreenCompatSection;
125     private CheckBox mAskCompatibilityCB;
126     private CheckBox mEnableCompatibilityCB;
127     private boolean mCanClearData = true;
128     private TextView mAppVersion;
129     private TextView mTotalSize;
130     private TextView mAppSize;
131     private TextView mDataSize;
132     private TextView mExternalCodeSize;
133     private TextView mExternalDataSize;
134     private ClearUserDataObserver mClearDataObserver;
135     // Views related to cache info
136     private TextView mCacheSize;
137     private Button mClearCacheButton;
138     private ClearCacheObserver mClearCacheObserver;
139     private Button mForceStopButton;
140     private Button mClearDataButton;
141     private Button mMoveAppButton;
142     private CompoundButton mNotificationSwitch;
143 
144     private PackageMoveObserver mPackageMoveObserver;
145 
146     private boolean mDisableAfterUninstall;
147 
148     private boolean mHaveSizes = false;
149     private long mLastCodeSize = -1;
150     private long mLastDataSize = -1;
151     private long mLastExternalCodeSize = -1;
152     private long mLastExternalDataSize = -1;
153     private long mLastCacheSize = -1;
154     private long mLastTotalSize = -1;
155 
156     //internal constants used in Handler
157     private static final int OP_SUCCESSFUL = 1;
158     private static final int OP_FAILED = 2;
159     private static final int CLEAR_USER_DATA = 1;
160     private static final int CLEAR_CACHE = 3;
161     private static final int PACKAGE_MOVE = 4;
162 
163     // invalid size value used initially and also when size retrieval through PackageManager
164     // fails for whatever reason
165     private static final int SIZE_INVALID = -1;
166 
167     // Resource strings
168     private CharSequence mInvalidSizeStr;
169     private CharSequence mComputingStr;
170 
171     // Dialog identifiers used in showDialog
172     private static final int DLG_BASE = 0;
173     private static final int DLG_CLEAR_DATA = DLG_BASE + 1;
174     private static final int DLG_FACTORY_RESET = DLG_BASE + 2;
175     private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3;
176     private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4;
177     private static final int DLG_FORCE_STOP = DLG_BASE + 5;
178     private static final int DLG_MOVE_FAILED = DLG_BASE + 6;
179     private static final int DLG_DISABLE = DLG_BASE + 7;
180     private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8;
181     private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9;
182 
183     // Menu identifiers
184     public static final int UNINSTALL_ALL_USERS_MENU = 1;
185 
186     // Result code identifiers
187     public static final int REQUEST_UNINSTALL = 1;
188     public static final int REQUEST_MANAGE_SPACE = 2;
189 
190     private Handler mHandler = new Handler() {
191         public void handleMessage(Message msg) {
192             // If the fragment is gone, don't process any more messages.
193             if (getView() == null) {
194                 return;
195             }
196             switch (msg.what) {
197                 case CLEAR_USER_DATA:
198                     processClearMsg(msg);
199                     break;
200                 case CLEAR_CACHE:
201                     // Refresh size info
202                     mState.requestSize(mAppEntry.info.packageName);
203                     break;
204                 case PACKAGE_MOVE:
205                     processMoveMsg(msg);
206                     break;
207                 default:
208                     break;
209             }
210         }
211     };
212 
213     class ClearUserDataObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)214        public void onRemoveCompleted(final String packageName, final boolean succeeded) {
215            final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA);
216            msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED;
217            mHandler.sendMessage(msg);
218         }
219     }
220 
221     class ClearCacheObserver extends IPackageDataObserver.Stub {
onRemoveCompleted(final String packageName, final boolean succeeded)222         public void onRemoveCompleted(final String packageName, final boolean succeeded) {
223             final Message msg = mHandler.obtainMessage(CLEAR_CACHE);
224             msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED;
225             mHandler.sendMessage(msg);
226          }
227      }
228 
229     class PackageMoveObserver extends IPackageMoveObserver.Stub {
packageMoved(String packageName, int returnCode)230         public void packageMoved(String packageName, int returnCode) throws RemoteException {
231             final Message msg = mHandler.obtainMessage(PACKAGE_MOVE);
232             msg.arg1 = returnCode;
233             mHandler.sendMessage(msg);
234         }
235     }
236 
getSizeStr(long size)237     private String getSizeStr(long size) {
238         if (size == SIZE_INVALID) {
239             return mInvalidSizeStr.toString();
240         }
241         return Formatter.formatFileSize(getActivity(), size);
242     }
243 
initDataButtons()244     private void initDataButtons() {
245         if ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM
246                 | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA))
247                 == ApplicationInfo.FLAG_SYSTEM
248                 || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
249             mClearDataButton.setText(R.string.clear_user_data_text);
250             mClearDataButton.setEnabled(false);
251             mCanClearData = false;
252         } else {
253             if (mAppEntry.info.manageSpaceActivityName != null) {
254                 mClearDataButton.setText(R.string.manage_space_text);
255             } else {
256                 mClearDataButton.setText(R.string.clear_user_data_text);
257             }
258             mClearDataButton.setOnClickListener(this);
259         }
260     }
261 
getMoveErrMsg(int errCode)262     private CharSequence getMoveErrMsg(int errCode) {
263         switch (errCode) {
264             case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE:
265                 return getActivity().getString(R.string.insufficient_storage);
266             case PackageManager.MOVE_FAILED_DOESNT_EXIST:
267                 return getActivity().getString(R.string.does_not_exist);
268             case PackageManager.MOVE_FAILED_FORWARD_LOCKED:
269                 return getActivity().getString(R.string.app_forward_locked);
270             case PackageManager.MOVE_FAILED_INVALID_LOCATION:
271                 return getActivity().getString(R.string.invalid_location);
272             case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE:
273                 return getActivity().getString(R.string.system_package);
274             case PackageManager.MOVE_FAILED_INTERNAL_ERROR:
275                 return "";
276         }
277         return "";
278     }
279 
initMoveButton()280     private void initMoveButton() {
281         if (Environment.isExternalStorageEmulated()) {
282             mMoveAppButton.setVisibility(View.INVISIBLE);
283             return;
284         }
285         boolean dataOnly = false;
286         dataOnly = (mPackageInfo == null) && (mAppEntry != null);
287         boolean moveDisable = true;
288         if (dataOnly) {
289             mMoveAppButton.setText(R.string.move_app);
290         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
291             mMoveAppButton.setText(R.string.move_app_to_internal);
292             // Always let apps move to internal storage from sdcard.
293             moveDisable = false;
294         } else {
295             mMoveAppButton.setText(R.string.move_app_to_sdcard);
296             mCanBeOnSdCardChecker.init();
297             moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info);
298         }
299         if (moveDisable) {
300             mMoveAppButton.setEnabled(false);
301         } else {
302             mMoveAppButton.setOnClickListener(this);
303             mMoveAppButton.setEnabled(true);
304         }
305     }
306 
isThisASystemPackage()307     private boolean isThisASystemPackage() {
308         try {
309             PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES);
310             return (mPackageInfo != null && mPackageInfo.signatures != null &&
311                     sys.signatures[0].equals(mPackageInfo.signatures[0]));
312         } catch (PackageManager.NameNotFoundException e) {
313             return false;
314         }
315     }
316 
handleDisableable(Button button)317     private boolean handleDisableable(Button button) {
318         boolean disableable = false;
319         try {
320             // Try to prevent the user from bricking their phone
321             // by not allowing disabling of apps signed with the
322             // system cert and any launcher app in the system.
323             PackageInfo sys = mPm.getPackageInfo("android",
324                     PackageManager.GET_SIGNATURES);
325             Intent intent = new Intent(Intent.ACTION_MAIN);
326             intent.addCategory(Intent.CATEGORY_HOME);
327             intent.setPackage(mAppEntry.info.packageName);
328             List<ResolveInfo> homes = mPm.queryIntentActivities(intent, 0);
329             if ((homes != null && homes.size() > 0) || isThisASystemPackage()) {
330                 // Disable button for core system applications.
331                 button.setText(R.string.disable_text);
332             } else if (mAppEntry.info.enabled) {
333                 button.setText(R.string.disable_text);
334                 disableable = true;
335             } else {
336                 button.setText(R.string.enable_text);
337                 disableable = true;
338             }
339         } catch (PackageManager.NameNotFoundException e) {
340             Log.w(TAG, "Unable to get package info", e);
341         }
342         return disableable;
343     }
344 
initUninstallButtons()345     private void initUninstallButtons() {
346         mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
347         boolean enabled = true;
348         if (mUpdatedSysApp) {
349             mUninstallButton.setText(R.string.app_factory_reset);
350             boolean specialDisable = false;
351             if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
352                 specialDisable = handleDisableable(mSpecialDisableButton);
353                 mSpecialDisableButton.setOnClickListener(this);
354             }
355             mMoreControlButtons.setVisibility(specialDisable ? View.VISIBLE : View.GONE);
356         } else {
357             mMoreControlButtons.setVisibility(View.GONE);
358             if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
359                 enabled = handleDisableable(mUninstallButton);
360             } else if ((mPackageInfo.applicationInfo.flags
361                     & ApplicationInfo.FLAG_INSTALLED) == 0
362                     && mUserManager.getUsers().size() >= 2) {
363                 // When we have multiple users, there is a separate menu
364                 // to uninstall for all users.
365                 mUninstallButton.setText(R.string.uninstall_text);
366                 enabled = false;
367             } else {
368                 mUninstallButton.setText(R.string.uninstall_text);
369             }
370         }
371         // If this is a device admin, it can't be uninstall or disabled.
372         // We do this here so the text of the button is still set correctly.
373         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
374             enabled = false;
375         }
376         mUninstallButton.setEnabled(enabled);
377         if (enabled) {
378             // Register listener
379             mUninstallButton.setOnClickListener(this);
380         }
381     }
382 
initNotificationButton()383     private void initNotificationButton() {
384         INotificationManager nm = INotificationManager.Stub.asInterface(
385                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
386         boolean enabled = true; // default on
387         try {
388             enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName,
389                     mAppEntry.info.uid);
390         } catch (android.os.RemoteException ex) {
391             // this does not bode well
392         }
393         mNotificationSwitch.setChecked(enabled);
394         if (isThisASystemPackage()) {
395             mNotificationSwitch.setEnabled(false);
396         } else {
397             mNotificationSwitch.setEnabled(true);
398             mNotificationSwitch.setOnCheckedChangeListener(this);
399         }
400     }
401 
402     /** Called when the activity is first created. */
403     @Override
onCreate(Bundle icicle)404     public void onCreate(Bundle icicle) {
405         super.onCreate(icicle);
406 
407         mState = ApplicationsState.getInstance(getActivity().getApplication());
408         mSession = mState.newSession(this);
409         mPm = getActivity().getPackageManager();
410         mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE);
411         IBinder b = ServiceManager.getService(Context.USB_SERVICE);
412         mUsbManager = IUsbManager.Stub.asInterface(b);
413         mAppWidgetManager = AppWidgetManager.getInstance(getActivity());
414         mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE);
415         mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms"));
416 
417         mCanBeOnSdCardChecker = new CanBeOnSdCardChecker();
418 
419         // Need to make sure we have loaded applications at this point.
420         mSession.resume();
421 
422         retrieveAppEntry();
423 
424         setHasOptionsMenu(true);
425     }
426 
427     @Override
onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)428     public View onCreateView(
429             LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
430         final View view = inflater.inflate(R.layout.installed_app_details, container, false);
431         Utils.prepareCustomPreferencesList(container, view, view, false);
432 
433         mRootView = view;
434         mComputingStr = getActivity().getText(R.string.computing_size);
435 
436         // Set default values on sizes
437         mTotalSize = (TextView)view.findViewById(R.id.total_size_text);
438         mAppSize = (TextView)view.findViewById(R.id.application_size_text);
439         mDataSize = (TextView)view.findViewById(R.id.data_size_text);
440         mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text);
441         mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text);
442 
443         if (Environment.isExternalStorageEmulated()) {
444             ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE);
445             ((View)mExternalDataSize.getParent()).setVisibility(View.GONE);
446         }
447 
448         // Get Control button panel
449         View btnPanel = view.findViewById(R.id.control_buttons_panel);
450         mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button);
451         mForceStopButton.setText(R.string.force_stop);
452         mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button);
453         mForceStopButton.setEnabled(false);
454 
455         // Get More Control button panel
456         mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel);
457         mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE);
458         mSpecialDisableButton = (Button)mMoreControlButtons.findViewById(R.id.right_button);
459         mMoreControlButtons.setVisibility(View.GONE);
460 
461         // Initialize clear data and move install location buttons
462         View data_buttons_panel = view.findViewById(R.id.data_buttons_panel);
463         mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button);
464         mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button);
465 
466         // Cache section
467         mCacheSize = (TextView) view.findViewById(R.id.cache_size_text);
468         mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button);
469 
470         mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button);
471 
472         // Screen compatibility control
473         mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section);
474         mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb);
475         mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb);
476 
477         mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch);
478 
479         return view;
480     }
481 
482     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)483     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
484         menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
485                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
486     }
487 
488     @Override
onPrepareOptionsMenu(Menu menu)489     public void onPrepareOptionsMenu(Menu menu) {
490         boolean showIt = true;
491         if (mUpdatedSysApp) {
492             showIt = false;
493         } else if (mAppEntry == null) {
494             showIt = false;
495         } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
496             showIt = false;
497         } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
498             showIt = false;
499         } else if (UserHandle.myUserId() != 0) {
500             showIt = false;
501         } else if (mUserManager.getUsers().size() < 2) {
502             showIt = false;
503         }
504         menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
505     }
506 
507     @Override
onOptionsItemSelected(MenuItem item)508     public boolean onOptionsItemSelected(MenuItem item) {
509         int menuId = item.getItemId();
510         if (menuId == UNINSTALL_ALL_USERS_MENU) {
511             uninstallPkg(mAppEntry.info.packageName, true, false);
512             return true;
513         }
514         return false;
515     }
516 
517     @Override
onActivityResult(int requestCode, int resultCode, Intent data)518     public void onActivityResult(int requestCode, int resultCode, Intent data) {
519         super.onActivityResult(requestCode, resultCode, data);
520         if (requestCode == REQUEST_UNINSTALL) {
521             if (mDisableAfterUninstall) {
522                 mDisableAfterUninstall = false;
523                 try {
524                     ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
525                             mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
526                             | PackageManager.GET_DISABLED_COMPONENTS);
527                     if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
528                         new DisableChanger(this, mAppEntry.info,
529                                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
530                                 .execute((Object)null);
531                     }
532                 } catch (NameNotFoundException e) {
533                 }
534             }
535             if (!refreshUi()) {
536                 setIntentAndFinish(true, true);
537             }
538         }
539     }
540 
541     // Utility method to set application label and icon.
setAppLabelAndIcon(PackageInfo pkgInfo)542     private void setAppLabelAndIcon(PackageInfo pkgInfo) {
543         final View appSnippet = mRootView.findViewById(R.id.app_snippet);
544         appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom());
545 
546         ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon);
547         mState.ensureIcon(mAppEntry);
548         icon.setImageDrawable(mAppEntry.icon);
549         // Set application name.
550         TextView label = (TextView) appSnippet.findViewById(R.id.app_name);
551         label.setText(mAppEntry.label);
552         // Version number of application
553         mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size);
554 
555         if (pkgInfo != null && pkgInfo.versionName != null) {
556             mAppVersion.setVisibility(View.VISIBLE);
557             mAppVersion.setText(getActivity().getString(R.string.version_text,
558                     String.valueOf(pkgInfo.versionName)));
559         } else {
560             mAppVersion.setVisibility(View.INVISIBLE);
561         }
562     }
563 
564     @Override
onResume()565     public void onResume() {
566         super.onResume();
567 
568         mSession.resume();
569         if (!refreshUi()) {
570             setIntentAndFinish(true, true);
571         }
572     }
573 
574     @Override
onPause()575     public void onPause() {
576         super.onPause();
577         mSession.pause();
578     }
579 
580     @Override
onAllSizesComputed()581     public void onAllSizesComputed() {
582     }
583 
584     @Override
onPackageIconChanged()585     public void onPackageIconChanged() {
586     }
587 
588     @Override
onPackageListChanged()589     public void onPackageListChanged() {
590         refreshUi();
591     }
592 
593     @Override
onRebuildComplete(ArrayList<AppEntry> apps)594     public void onRebuildComplete(ArrayList<AppEntry> apps) {
595     }
596 
597     @Override
onPackageSizeChanged(String packageName)598     public void onPackageSizeChanged(String packageName) {
599         if (packageName.equals(mAppEntry.info.packageName)) {
600             refreshSizeInfo();
601         }
602     }
603 
604     @Override
onRunningStateChanged(boolean running)605     public void onRunningStateChanged(boolean running) {
606     }
607 
retrieveAppEntry()608     private String retrieveAppEntry() {
609         final Bundle args = getArguments();
610         String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null;
611         if (packageName == null) {
612             Intent intent = (args == null) ?
613                     getActivity().getIntent() : (Intent) args.getParcelable("intent");
614             if (intent != null) {
615                 packageName = intent.getData().getSchemeSpecificPart();
616             }
617         }
618         mAppEntry = mState.getEntry(packageName);
619         if (mAppEntry != null) {
620             // Get application info again to refresh changed properties of application
621             try {
622                 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName,
623                         PackageManager.GET_DISABLED_COMPONENTS |
624                         PackageManager.GET_UNINSTALLED_PACKAGES |
625                         PackageManager.GET_SIGNATURES);
626             } catch (NameNotFoundException e) {
627                 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e);
628             }
629         } else {
630             Log.w(TAG, "Missing AppEntry; maybe reinstalling?");
631             mPackageInfo = null;
632         }
633 
634         return packageName;
635     }
636 
refreshUi()637     private boolean refreshUi() {
638         if (mMoveInProgress) {
639             return true;
640         }
641         final String packageName = retrieveAppEntry();
642 
643         if (mAppEntry == null) {
644             return false; // onCreate must have failed, make sure to exit
645         }
646 
647         if (mPackageInfo == null) {
648             return false; // onCreate must have failed, make sure to exit
649         }
650 
651         // Get list of preferred activities
652         List<ComponentName> prefActList = new ArrayList<ComponentName>();
653 
654         // Intent list cannot be null. so pass empty list
655         List<IntentFilter> intentList = new ArrayList<IntentFilter>();
656         mPm.getPreferredActivities(intentList, prefActList, packageName);
657         if (localLOGV)
658             Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list");
659         boolean hasUsbDefaults = false;
660         try {
661             hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId());
662         } catch (RemoteException e) {
663             Log.e(TAG, "mUsbManager.hasDefaults", e);
664         }
665         boolean hasBindAppWidgetPermission =
666                 mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName);
667 
668         TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title);
669         TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
670         boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults;
671         if (!autoLaunchEnabled && !hasBindAppWidgetPermission) {
672             resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
673         } else {
674             boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled;
675 
676             if (hasBindAppWidgetPermission) {
677                 autoLaunchTitleView.setText(R.string.auto_launch_label_generic);
678             } else {
679                 autoLaunchTitleView.setText(R.string.auto_launch_label);
680             }
681 
682             CharSequence text = null;
683             int bulletIndent = getResources()
684                     .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset);
685             if (autoLaunchEnabled) {
686                 CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text);
687                 SpannableString s = new SpannableString(autoLaunchEnableText);
688                 if (useBullets) {
689                     s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0);
690                 }
691                 text = (text == null) ?
692                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
693             }
694             if (hasBindAppWidgetPermission) {
695                 CharSequence alwaysAllowBindAppWidgetsText =
696                         getText(R.string.always_allow_bind_appwidgets_text);
697                 SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText);
698                 if (useBullets) {
699                     s.setSpan(new BulletSpan(bulletIndent),
700                             0, alwaysAllowBindAppWidgetsText.length(), 0);
701                 }
702                 text = (text == null) ?
703                         TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n");
704             }
705             autoLaunchView.setText(text);
706             mActivitiesButton.setEnabled(true);
707             mActivitiesButton.setOnClickListener(this);
708         }
709 
710         // Screen compatibility section.
711         ActivityManager am = (ActivityManager)
712                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
713         int compatMode = am.getPackageScreenCompatMode(packageName);
714         // For now these are always off; this is the old UI model which we
715         // are no longer using.
716         if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED
717                 || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) {
718             mScreenCompatSection.setVisibility(View.VISIBLE);
719             mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName));
720             mAskCompatibilityCB.setOnCheckedChangeListener(this);
721             mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED);
722             mEnableCompatibilityCB.setOnCheckedChangeListener(this);
723         } else {
724             mScreenCompatSection.setVisibility(View.GONE);
725         }
726 
727         // Security permissions section
728         LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section);
729         AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName);
730         int premiumSmsPermission = getPremiumSmsPermission(packageName);
731         // Premium SMS permission implies the app also has SEND_SMS permission, so the original
732         // application permissions list doesn't have to be shown/hidden separately. The premium
733         // SMS subsection should only be visible if the app has tried to send to a premium SMS.
734         if (asp.getPermissionCount() > 0
735                 || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
736             permsView.setVisibility(View.VISIBLE);
737         } else {
738             permsView.setVisibility(View.GONE);
739         }
740         // Premium SMS permission subsection
741         TextView securityBillingDesc = (TextView) permsView.findViewById(
742                 R.id.security_settings_billing_desc);
743         LinearLayout securityBillingList = (LinearLayout) permsView.findViewById(
744                 R.id.security_settings_billing_list);
745         if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) {
746             // Show the premium SMS permission selector
747             securityBillingDesc.setVisibility(View.VISIBLE);
748             securityBillingList.setVisibility(View.VISIBLE);
749             Spinner spinner = (Spinner) permsView.findViewById(
750                     R.id.security_settings_premium_sms_list);
751             ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(),
752                     R.array.security_settings_premium_sms_values,
753                     android.R.layout.simple_spinner_item);
754             adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
755             spinner.setAdapter(adapter);
756             // List items are in the same order as SmsUsageMonitor constants, offset by 1.
757             spinner.setSelection(premiumSmsPermission - 1);
758             spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener(
759                     packageName, mSmsManager));
760         } else {
761             // Hide the premium SMS permission selector
762             securityBillingDesc.setVisibility(View.GONE);
763             securityBillingList.setVisibility(View.GONE);
764         }
765         // App permissions subsection
766         if (asp.getPermissionCount() > 0) {
767             // Make the security sections header visible
768             LinearLayout securityList = (LinearLayout) permsView.findViewById(
769                     R.id.security_settings_list);
770             securityList.removeAllViews();
771             securityList.addView(asp.getPermissionsViewWithRevokeButtons());
772             // If this app is running under a shared user ID with other apps,
773             // update the description to explain this.
774             String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid);
775             if (packages != null && packages.length > 1) {
776                 ArrayList<CharSequence> pnames = new ArrayList<CharSequence>();
777                 for (int i=0; i<packages.length; i++) {
778                     String pkg = packages[i];
779                     if (mPackageInfo.packageName.equals(pkg)) {
780                         continue;
781                     }
782                     try {
783                         ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0);
784                         pnames.add(ainfo.loadLabel(mPm));
785                     } catch (PackageManager.NameNotFoundException e) {
786                     }
787                 }
788                 final int N = pnames.size();
789                 if (N > 0) {
790                     final Resources res = getActivity().getResources();
791                     String appListStr;
792                     if (N == 1) {
793                         appListStr = pnames.get(0).toString();
794                     } else if (N == 2) {
795                         appListStr = res.getString(R.string.join_two_items, pnames.get(0),
796                                 pnames.get(1));
797                     } else {
798                         appListStr = pnames.get(N-2).toString();
799                         for (int i=N-3; i>=0; i--) {
800                             appListStr = res.getString(i == 0 ? R.string.join_many_items_first
801                                     : R.string.join_many_items_middle, pnames.get(i), appListStr);
802                         }
803                         appListStr = res.getString(R.string.join_many_items_last,
804                                 appListStr, pnames.get(N-1));
805                     }
806                     TextView descr = (TextView) mRootView.findViewById(
807                             R.id.security_settings_desc);
808                     descr.setText(res.getString(R.string.security_settings_desc_multi,
809                             mPackageInfo.applicationInfo.loadLabel(mPm), appListStr));
810                 }
811             }
812         }
813 
814         checkForceStop();
815         setAppLabelAndIcon(mPackageInfo);
816         refreshButtons();
817         refreshSizeInfo();
818 
819         if (!mInitialized) {
820             // First time init: are we displaying an uninstalled app?
821             mInitialized = true;
822             mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
823         } else {
824             // All other times: if the app no longer exists then we want
825             // to go away.
826             try {
827                 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo(
828                         mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
829                         | PackageManager.GET_DISABLED_COMPONENTS);
830                 if (!mShowUninstalled) {
831                     // If we did not start out with the app uninstalled, then
832                     // it transitioning to the uninstalled state for the current
833                     // user means we should go away as well.
834                     return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
835                 }
836             } catch (NameNotFoundException e) {
837                 return false;
838             }
839         }
840 
841         return true;
842     }
843 
844     private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener {
845         private final String mPackageName;
846         private final ISms mSmsManager;
847 
PremiumSmsSelectionListener(String packageName, ISms smsManager)848         PremiumSmsSelectionListener(String packageName, ISms smsManager) {
849             mPackageName = packageName;
850             mSmsManager = smsManager;
851         }
852 
853         @Override
onItemSelected(AdapterView<?> parent, View view, int position, long id)854         public void onItemSelected(AdapterView<?> parent, View view, int position,
855                 long id) {
856             if (position >= 0 && position < 3) {
857                 Log.d(TAG, "Selected premium SMS policy " + position);
858                 setPremiumSmsPermission(mPackageName, (position + 1));
859             } else {
860                 Log.e(TAG, "Error: unknown premium SMS policy " + position);
861             }
862         }
863 
864         @Override
onNothingSelected(AdapterView<?> parent)865         public void onNothingSelected(AdapterView<?> parent) {
866             // Ignored
867         }
868 
setPremiumSmsPermission(String packageName, int permission)869         private void setPremiumSmsPermission(String packageName, int permission) {
870             try {
871                 if (mSmsManager != null) {
872                     mSmsManager.setPremiumSmsPermission(packageName, permission);
873                 }
874             } catch (RemoteException ex) {
875                 // ignored
876             }
877         }
878     }
879 
resetLaunchDefaultsUi(TextView title, TextView autoLaunchView)880     private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) {
881         title.setText(R.string.auto_launch_label);
882         autoLaunchView.setText(R.string.auto_launch_disable_text);
883         // Disable clear activities button
884         mActivitiesButton.setEnabled(false);
885     }
886 
setIntentAndFinish(boolean finish, boolean appChanged)887     private void setIntentAndFinish(boolean finish, boolean appChanged) {
888         if(localLOGV) Log.i(TAG, "appChanged="+appChanged);
889         Intent intent = new Intent();
890         intent.putExtra(ManageApplications.APP_CHG, appChanged);
891         PreferenceActivity pa = (PreferenceActivity)getActivity();
892         pa.finishPreferencePanel(this, Activity.RESULT_OK, intent);
893     }
894 
refreshSizeInfo()895     private void refreshSizeInfo() {
896         if (mAppEntry.size == ApplicationsState.SIZE_INVALID
897                 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) {
898             mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1;
899             if (!mHaveSizes) {
900                 mAppSize.setText(mComputingStr);
901                 mDataSize.setText(mComputingStr);
902                 mCacheSize.setText(mComputingStr);
903                 mTotalSize.setText(mComputingStr);
904             }
905             mClearDataButton.setEnabled(false);
906             mClearCacheButton.setEnabled(false);
907 
908         } else {
909             mHaveSizes = true;
910             long codeSize = mAppEntry.codeSize;
911             long dataSize = mAppEntry.dataSize;
912             if (Environment.isExternalStorageEmulated()) {
913                 codeSize += mAppEntry.externalCodeSize;
914                 dataSize +=  mAppEntry.externalDataSize;
915             } else {
916                 if (mLastExternalCodeSize != mAppEntry.externalCodeSize) {
917                     mLastExternalCodeSize = mAppEntry.externalCodeSize;
918                     mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize));
919                 }
920                 if (mLastExternalDataSize !=  mAppEntry.externalDataSize) {
921                     mLastExternalDataSize =  mAppEntry.externalDataSize;
922                     mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize));
923                 }
924             }
925             if (mLastCodeSize != codeSize) {
926                 mLastCodeSize = codeSize;
927                 mAppSize.setText(getSizeStr(codeSize));
928             }
929             if (mLastDataSize != dataSize) {
930                 mLastDataSize = dataSize;
931                 mDataSize.setText(getSizeStr(dataSize));
932             }
933             long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize;
934             if (mLastCacheSize != cacheSize) {
935                 mLastCacheSize = cacheSize;
936                 mCacheSize.setText(getSizeStr(cacheSize));
937             }
938             if (mLastTotalSize != mAppEntry.size) {
939                 mLastTotalSize = mAppEntry.size;
940                 mTotalSize.setText(getSizeStr(mAppEntry.size));
941             }
942 
943             if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) {
944                 mClearDataButton.setEnabled(false);
945             } else {
946                 mClearDataButton.setEnabled(true);
947                 mClearDataButton.setOnClickListener(this);
948             }
949             if (cacheSize <= 0) {
950                 mClearCacheButton.setEnabled(false);
951             } else {
952                 mClearCacheButton.setEnabled(true);
953                 mClearCacheButton.setOnClickListener(this);
954             }
955         }
956     }
957 
958     /*
959      * Private method to handle clear message notification from observer when
960      * the async operation from PackageManager is complete
961      */
processClearMsg(Message msg)962     private void processClearMsg(Message msg) {
963         int result = msg.arg1;
964         String packageName = mAppEntry.info.packageName;
965         mClearDataButton.setText(R.string.clear_user_data_text);
966         if(result == OP_SUCCESSFUL) {
967             Log.i(TAG, "Cleared user data for package : "+packageName);
968             mState.requestSize(mAppEntry.info.packageName);
969         } else {
970             mClearDataButton.setEnabled(true);
971         }
972         checkForceStop();
973     }
974 
refreshButtons()975     private void refreshButtons() {
976         if (!mMoveInProgress) {
977             initUninstallButtons();
978             initDataButtons();
979             initMoveButton();
980             initNotificationButton();
981         } else {
982             mMoveAppButton.setText(R.string.moving);
983             mMoveAppButton.setEnabled(false);
984             mUninstallButton.setEnabled(false);
985             mSpecialDisableButton.setEnabled(false);
986         }
987     }
988 
processMoveMsg(Message msg)989     private void processMoveMsg(Message msg) {
990         int result = msg.arg1;
991         String packageName = mAppEntry.info.packageName;
992         // Refresh the button attributes.
993         mMoveInProgress = false;
994         if (result == PackageManager.MOVE_SUCCEEDED) {
995             Log.i(TAG, "Moved resources for " + packageName);
996             // Refresh size information again.
997             mState.requestSize(mAppEntry.info.packageName);
998         } else {
999             showDialogInner(DLG_MOVE_FAILED, result);
1000         }
1001         refreshUi();
1002     }
1003 
1004     /*
1005      * Private method to initiate clearing user data when the user clicks the clear data
1006      * button for a system package
1007      */
initiateClearUserData()1008     private  void initiateClearUserData() {
1009         mClearDataButton.setEnabled(false);
1010         // Invoke uninstall or clear user data based on sysPackage
1011         String packageName = mAppEntry.info.packageName;
1012         Log.i(TAG, "Clearing user data for package : " + packageName);
1013         if (mClearDataObserver == null) {
1014             mClearDataObserver = new ClearUserDataObserver();
1015         }
1016         ActivityManager am = (ActivityManager)
1017                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1018         boolean res = am.clearApplicationUserData(packageName, mClearDataObserver);
1019         if (!res) {
1020             // Clearing data failed for some obscure reason. Just log error for now
1021             Log.i(TAG, "Couldnt clear application user data for package:"+packageName);
1022             showDialogInner(DLG_CANNOT_CLEAR_DATA, 0);
1023         } else {
1024             mClearDataButton.setText(R.string.recompute_size);
1025         }
1026     }
1027 
showDialogInner(int id, int moveErrorCode)1028     private void showDialogInner(int id, int moveErrorCode) {
1029         DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode);
1030         newFragment.setTargetFragment(this, 0);
1031         newFragment.show(getFragmentManager(), "dialog " + id);
1032     }
1033 
1034     public static class MyAlertDialogFragment extends DialogFragment {
1035 
newInstance(int id, int moveErrorCode)1036         public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) {
1037             MyAlertDialogFragment frag = new MyAlertDialogFragment();
1038             Bundle args = new Bundle();
1039             args.putInt("id", id);
1040             args.putInt("moveError", moveErrorCode);
1041             frag.setArguments(args);
1042             return frag;
1043         }
1044 
getOwner()1045         InstalledAppDetails getOwner() {
1046             return (InstalledAppDetails)getTargetFragment();
1047         }
1048 
1049         @Override
onCreateDialog(Bundle savedInstanceState)1050         public Dialog onCreateDialog(Bundle savedInstanceState) {
1051             int id = getArguments().getInt("id");
1052             int moveErrorCode = getArguments().getInt("moveError");
1053             switch (id) {
1054                 case DLG_CLEAR_DATA:
1055                     return new AlertDialog.Builder(getActivity())
1056                     .setTitle(getActivity().getText(R.string.clear_data_dlg_title))
1057                     .setIconAttribute(android.R.attr.alertDialogIcon)
1058                     .setMessage(getActivity().getText(R.string.clear_data_dlg_text))
1059                     .setPositiveButton(R.string.dlg_ok,
1060                             new DialogInterface.OnClickListener() {
1061                         public void onClick(DialogInterface dialog, int which) {
1062                             // Clear user data here
1063                             getOwner().initiateClearUserData();
1064                         }
1065                     })
1066                     .setNegativeButton(R.string.dlg_cancel, null)
1067                     .create();
1068                 case DLG_FACTORY_RESET:
1069                     return new AlertDialog.Builder(getActivity())
1070                     .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title))
1071                     .setIconAttribute(android.R.attr.alertDialogIcon)
1072                     .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text))
1073                     .setPositiveButton(R.string.dlg_ok,
1074                             new DialogInterface.OnClickListener() {
1075                         public void onClick(DialogInterface dialog, int which) {
1076                             // Clear user data here
1077                             getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1078                                     false, false);
1079                         }
1080                     })
1081                     .setNegativeButton(R.string.dlg_cancel, null)
1082                     .create();
1083                 case DLG_APP_NOT_FOUND:
1084                     return new AlertDialog.Builder(getActivity())
1085                     .setTitle(getActivity().getText(R.string.app_not_found_dlg_title))
1086                     .setIconAttribute(android.R.attr.alertDialogIcon)
1087                     .setMessage(getActivity().getText(R.string.app_not_found_dlg_title))
1088                     .setNeutralButton(getActivity().getText(R.string.dlg_ok),
1089                             new DialogInterface.OnClickListener() {
1090                         public void onClick(DialogInterface dialog, int which) {
1091                             //force to recompute changed value
1092                             getOwner().setIntentAndFinish(true, true);
1093                         }
1094                     })
1095                     .create();
1096                 case DLG_CANNOT_CLEAR_DATA:
1097                     return new AlertDialog.Builder(getActivity())
1098                     .setTitle(getActivity().getText(R.string.clear_failed_dlg_title))
1099                     .setIconAttribute(android.R.attr.alertDialogIcon)
1100                     .setMessage(getActivity().getText(R.string.clear_failed_dlg_text))
1101                     .setNeutralButton(R.string.dlg_ok,
1102                             new DialogInterface.OnClickListener() {
1103                         public void onClick(DialogInterface dialog, int which) {
1104                             getOwner().mClearDataButton.setEnabled(false);
1105                             //force to recompute changed value
1106                             getOwner().setIntentAndFinish(false, false);
1107                         }
1108                     })
1109                     .create();
1110                 case DLG_FORCE_STOP:
1111                     return new AlertDialog.Builder(getActivity())
1112                     .setTitle(getActivity().getText(R.string.force_stop_dlg_title))
1113                     .setIconAttribute(android.R.attr.alertDialogIcon)
1114                     .setMessage(getActivity().getText(R.string.force_stop_dlg_text))
1115                     .setPositiveButton(R.string.dlg_ok,
1116                         new DialogInterface.OnClickListener() {
1117                         public void onClick(DialogInterface dialog, int which) {
1118                             // Force stop
1119                             getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName);
1120                         }
1121                     })
1122                     .setNegativeButton(R.string.dlg_cancel, null)
1123                     .create();
1124                 case DLG_MOVE_FAILED:
1125                     CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text,
1126                             getOwner().getMoveErrMsg(moveErrorCode));
1127                     return new AlertDialog.Builder(getActivity())
1128                     .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title))
1129                     .setIconAttribute(android.R.attr.alertDialogIcon)
1130                     .setMessage(msg)
1131                     .setNeutralButton(R.string.dlg_ok, null)
1132                     .create();
1133                 case DLG_DISABLE:
1134                     return new AlertDialog.Builder(getActivity())
1135                     .setTitle(getActivity().getText(R.string.app_disable_dlg_title))
1136                     .setIconAttribute(android.R.attr.alertDialogIcon)
1137                     .setMessage(getActivity().getText(R.string.app_disable_dlg_text))
1138                     .setPositiveButton(R.string.dlg_ok,
1139                         new DialogInterface.OnClickListener() {
1140                         public void onClick(DialogInterface dialog, int which) {
1141                             // Disable the app
1142                             new DisableChanger(getOwner(), getOwner().mAppEntry.info,
1143                                     PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
1144                             .execute((Object)null);
1145                         }
1146                     })
1147                     .setNegativeButton(R.string.dlg_cancel, null)
1148                     .create();
1149                 case DLG_DISABLE_NOTIFICATIONS:
1150                     return new AlertDialog.Builder(getActivity())
1151                     .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title))
1152                     .setIconAttribute(android.R.attr.alertDialogIcon)
1153                     .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text))
1154                     .setPositiveButton(R.string.dlg_ok,
1155                         new DialogInterface.OnClickListener() {
1156                         public void onClick(DialogInterface dialog, int which) {
1157                             // Disable the package's notifications
1158                             getOwner().setNotificationsEnabled(false);
1159                         }
1160                     })
1161                     .setNegativeButton(R.string.dlg_cancel,
1162                         new DialogInterface.OnClickListener() {
1163                         public void onClick(DialogInterface dialog, int which) {
1164                             // Re-enable the checkbox
1165                             getOwner().mNotificationSwitch.setChecked(true);
1166                         }
1167                     })
1168                     .create();
1169                 case DLG_SPECIAL_DISABLE:
1170                     return new AlertDialog.Builder(getActivity())
1171                     .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title))
1172                     .setIconAttribute(android.R.attr.alertDialogIcon)
1173                     .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text))
1174                     .setPositiveButton(R.string.dlg_ok,
1175                             new DialogInterface.OnClickListener() {
1176                         public void onClick(DialogInterface dialog, int which) {
1177                             // Clear user data here
1178                             getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName,
1179                                     false, true);
1180                         }
1181                     })
1182                     .setNegativeButton(R.string.dlg_cancel, null)
1183                     .create();
1184             }
1185             throw new IllegalArgumentException("unknown id " + id);
1186         }
1187     }
1188 
1189     private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
1190          // Create new intent to launch Uninstaller activity
1191         Uri packageURI = Uri.parse("package:"+packageName);
1192         Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
1193         uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
1194         startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
1195         mDisableAfterUninstall = andDisable;
1196     }
1197 
1198     private void forceStopPackage(String pkgName) {
1199         ActivityManager am = (ActivityManager)getActivity().getSystemService(
1200                 Context.ACTIVITY_SERVICE);
1201         am.forceStopPackage(pkgName);
1202         mState.invalidatePackage(pkgName);
1203         ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName);
1204         if (newEnt != null) {
1205             mAppEntry = newEnt;
1206         }
1207         checkForceStop();
1208     }
1209 
1210     private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
1211         @Override
1212         public void onReceive(Context context, Intent intent) {
1213             updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
1214         }
1215     };
1216 
1217     private void updateForceStopButton(boolean enabled) {
1218         mForceStopButton.setEnabled(enabled);
1219         mForceStopButton.setOnClickListener(InstalledAppDetails.this);
1220     }
1221 
1222     private void checkForceStop() {
1223         if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
1224             // User can't force stop device admin.
1225             updateForceStopButton(false);
1226         } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
1227             // If the app isn't explicitly stopped, then always show the
1228             // force stop button.
1229             updateForceStopButton(true);
1230         } else {
1231             Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
1232                     Uri.fromParts("package", mAppEntry.info.packageName, null));
1233             intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
1234             intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
1235             intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
1236             getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null,
1237                     Activity.RESULT_CANCELED, null, null);
1238         }
1239     }
1240 
1241     static class DisableChanger extends AsyncTask<Object, Object, Object> {
1242         final PackageManager mPm;
1243         final WeakReference<InstalledAppDetails> mActivity;
1244         final ApplicationInfo mInfo;
1245         final int mState;
1246 
1247         DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
1248             mPm = activity.mPm;
1249             mActivity = new WeakReference<InstalledAppDetails>(activity);
1250             mInfo = info;
1251             mState = state;
1252         }
1253 
1254         @Override
1255         protected Object doInBackground(Object... params) {
1256             mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
1257             return null;
1258         }
1259     }
1260 
1261     private void setNotificationsEnabled(boolean enabled) {
1262         String packageName = mAppEntry.info.packageName;
1263         INotificationManager nm = INotificationManager.Stub.asInterface(
1264                 ServiceManager.getService(Context.NOTIFICATION_SERVICE));
1265         try {
1266             final boolean enable = mNotificationSwitch.isChecked();
1267             nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled);
1268         } catch (android.os.RemoteException ex) {
1269             mNotificationSwitch.setChecked(!enabled); // revert
1270         }
1271     }
1272 
1273     private int getPremiumSmsPermission(String packageName) {
1274         try {
1275             if (mSmsManager != null) {
1276                 return mSmsManager.getPremiumSmsPermission(packageName);
1277             }
1278         } catch (RemoteException ex) {
1279             // ignored
1280         }
1281         return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN;
1282     }
1283 
1284     /*
1285      * Method implementing functionality of buttons clicked
1286      * @see android.view.View.OnClickListener#onClick(android.view.View)
1287      */
1288     public void onClick(View v) {
1289         String packageName = mAppEntry.info.packageName;
1290         if(v == mUninstallButton) {
1291             if (mUpdatedSysApp) {
1292                 showDialogInner(DLG_FACTORY_RESET, 0);
1293             } else {
1294                 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
1295                     if (mAppEntry.info.enabled) {
1296                         showDialogInner(DLG_DISABLE, 0);
1297                     } else {
1298                         new DisableChanger(this, mAppEntry.info,
1299                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
1300                         .execute((Object)null);
1301                     }
1302                 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
1303                     uninstallPkg(packageName, true, false);
1304                 } else {
1305                     uninstallPkg(packageName, false, false);
1306                 }
1307             }
1308         } else if(v == mSpecialDisableButton) {
1309             showDialogInner(DLG_SPECIAL_DISABLE, 0);
1310         } else if(v == mActivitiesButton) {
1311             mPm.clearPackagePreferredActivities(packageName);
1312             try {
1313                 mUsbManager.clearDefaults(packageName, UserHandle.myUserId());
1314             } catch (RemoteException e) {
1315                 Log.e(TAG, "mUsbManager.clearDefaults", e);
1316             }
1317             mAppWidgetManager.setBindAppWidgetPermission(packageName, false);
1318             TextView autoLaunchTitleView =
1319                     (TextView) mRootView.findViewById(R.id.auto_launch_title);
1320             TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch);
1321             resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView);
1322         } else if(v == mClearDataButton) {
1323             if (mAppEntry.info.manageSpaceActivityName != null) {
1324                 if (!Utils.isMonkeyRunning()) {
1325                     Intent intent = new Intent(Intent.ACTION_DEFAULT);
1326                     intent.setClassName(mAppEntry.info.packageName,
1327                             mAppEntry.info.manageSpaceActivityName);
1328                     startActivityForResult(intent, REQUEST_MANAGE_SPACE);
1329                 }
1330             } else {
1331                 showDialogInner(DLG_CLEAR_DATA, 0);
1332             }
1333         } else if (v == mClearCacheButton) {
1334             // Lazy initialization of observer
1335             if (mClearCacheObserver == null) {
1336                 mClearCacheObserver = new ClearCacheObserver();
1337             }
1338             mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver);
1339         } else if (v == mForceStopButton) {
1340             showDialogInner(DLG_FORCE_STOP, 0);
1341             //forceStopPackage(mAppInfo.packageName);
1342         } else if (v == mMoveAppButton) {
1343             if (mPackageMoveObserver == null) {
1344                 mPackageMoveObserver = new PackageMoveObserver();
1345             }
1346             int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ?
1347                     PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA;
1348             mMoveInProgress = true;
1349             refreshButtons();
1350             mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags);
1351         }
1352     }
1353 
1354     @Override
1355     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
1356         String packageName = mAppEntry.info.packageName;
1357         ActivityManager am = (ActivityManager)
1358                 getActivity().getSystemService(Context.ACTIVITY_SERVICE);
1359         if (buttonView == mAskCompatibilityCB) {
1360             am.setPackageAskScreenCompat(packageName, isChecked);
1361         } else if (buttonView == mEnableCompatibilityCB) {
1362             am.setPackageScreenCompatMode(packageName, isChecked ?
1363                     ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED);
1364         } else if (buttonView == mNotificationSwitch) {
1365             if (!isChecked) {
1366                 showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0);
1367             } else {
1368                 setNotificationsEnabled(true);
1369             }
1370         }
1371     }
1372 }
1373 
1374