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