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