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