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