InstalledAppDetails.java revision aef746c45f14c8a170dba48c4b55f93cae0e24e4
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 = false; 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 if (compatMode == ActivityManager.COMPAT_MODE_DISABLED 486 || compatMode == ActivityManager.COMPAT_MODE_ENABLED) { 487 mScreenCompatSection.setVisibility(View.VISIBLE); 488 mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName)); 489 mAskCompatibilityCB.setOnCheckedChangeListener(this); 490 mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED); 491 mEnableCompatibilityCB.setOnCheckedChangeListener(this); 492 } else { 493 mScreenCompatSection.setVisibility(View.GONE); 494 } 495 496 // Security permissions section 497 LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section); 498 AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName); 499 if (asp.getPermissionCount() > 0) { 500 permsView.setVisibility(View.VISIBLE); 501 // Make the security sections header visible 502 LinearLayout securityList = (LinearLayout) permsView.findViewById( 503 R.id.security_settings_list); 504 securityList.removeAllViews(); 505 securityList.addView(asp.getPermissionsView()); 506 } else { 507 permsView.setVisibility(View.GONE); 508 } 509 510 checkForceStop(); 511 setAppLabelAndIcon(mPackageInfo); 512 refreshButtons(); 513 refreshSizeInfo(); 514 return true; 515 } 516 517 private void setIntentAndFinish(boolean finish, boolean appChanged) { 518 if(localLOGV) Log.i(TAG, "appChanged="+appChanged); 519 Intent intent = new Intent(); 520 intent.putExtra(ManageApplications.APP_CHG, appChanged); 521 PreferenceActivity pa = (PreferenceActivity)getActivity(); 522 pa.finishPreferencePanel(this, Activity.RESULT_OK, intent); 523 } 524 525 private void refreshSizeInfo() { 526 if (mAppEntry.size == ApplicationsState.SIZE_INVALID 527 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { 528 mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1; 529 if (!mHaveSizes) { 530 mAppSize.setText(mComputingStr); 531 mDataSize.setText(mComputingStr); 532 mCacheSize.setText(mComputingStr); 533 mTotalSize.setText(mComputingStr); 534 } 535 mClearDataButton.setEnabled(false); 536 mClearCacheButton.setEnabled(false); 537 538 } else { 539 mHaveSizes = true; 540 if (mLastCodeSize != mAppEntry.codeSize) { 541 mLastCodeSize = mAppEntry.codeSize; 542 mAppSize.setText(getSizeStr(mAppEntry.codeSize)); 543 } 544 if (mLastDataSize != mAppEntry.dataSize) { 545 mLastDataSize = mAppEntry.dataSize; 546 mDataSize.setText(getSizeStr(mAppEntry.dataSize)); 547 } 548 if (mLastExternalSize != mAppEntry.externalSize) { 549 mLastExternalSize = mAppEntry.externalSize; 550 mExternalSize.setText(getSizeStr(mAppEntry.externalSize)); 551 } 552 if (mLastCacheSize != mAppEntry.cacheSize) { 553 mLastCacheSize = mAppEntry.cacheSize; 554 mCacheSize.setText(getSizeStr(mAppEntry.cacheSize)); 555 } 556 if (mLastTotalSize != mAppEntry.size) { 557 mLastTotalSize = mAppEntry.size; 558 mTotalSize.setText(getSizeStr(mAppEntry.size)); 559 } 560 561 if (mAppEntry.dataSize <= 0 || !mCanClearData) { 562 mClearDataButton.setEnabled(false); 563 } else { 564 mClearDataButton.setEnabled(true); 565 mClearDataButton.setOnClickListener(this); 566 } 567 if (mAppEntry.cacheSize <= 0) { 568 mClearCacheButton.setEnabled(false); 569 } else { 570 mClearCacheButton.setEnabled(true); 571 mClearCacheButton.setOnClickListener(this); 572 } 573 } 574 } 575 576 /* 577 * Private method to handle clear message notification from observer when 578 * the async operation from PackageManager is complete 579 */ 580 private void processClearMsg(Message msg) { 581 int result = msg.arg1; 582 String packageName = mAppEntry.info.packageName; 583 mClearDataButton.setText(R.string.clear_user_data_text); 584 if(result == OP_SUCCESSFUL) { 585 Log.i(TAG, "Cleared user data for package : "+packageName); 586 mState.requestSize(mAppEntry.info.packageName); 587 } else { 588 mClearDataButton.setEnabled(true); 589 } 590 checkForceStop(); 591 } 592 593 private void refreshButtons() { 594 if (!mMoveInProgress) { 595 initUninstallButtons(); 596 initDataButtons(); 597 initMoveButton(); 598 } else { 599 mMoveAppButton.setText(R.string.moving); 600 mMoveAppButton.setEnabled(false); 601 mUninstallButton.setEnabled(false); 602 } 603 } 604 605 private void processMoveMsg(Message msg) { 606 int result = msg.arg1; 607 String packageName = mAppEntry.info.packageName; 608 // Refresh the button attributes. 609 mMoveInProgress = false; 610 if (result == PackageManager.MOVE_SUCCEEDED) { 611 Log.i(TAG, "Moved resources for " + packageName); 612 // Refresh size information again. 613 mState.requestSize(mAppEntry.info.packageName); 614 } else { 615 showDialogInner(DLG_MOVE_FAILED, result); 616 } 617 refreshUi(); 618 } 619 620 /* 621 * Private method to initiate clearing user data when the user clicks the clear data 622 * button for a system package 623 */ 624 private void initiateClearUserData() { 625 mClearDataButton.setEnabled(false); 626 // Invoke uninstall or clear user data based on sysPackage 627 String packageName = mAppEntry.info.packageName; 628 Log.i(TAG, "Clearing user data for package : " + packageName); 629 if (mClearDataObserver == null) { 630 mClearDataObserver = new ClearUserDataObserver(); 631 } 632 ActivityManager am = (ActivityManager) 633 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 634 boolean res = am.clearApplicationUserData(packageName, mClearDataObserver); 635 if (!res) { 636 // Clearing data failed for some obscure reason. Just log error for now 637 Log.i(TAG, "Couldnt clear application user data for package:"+packageName); 638 showDialogInner(DLG_CANNOT_CLEAR_DATA, 0); 639 } else { 640 mClearDataButton.setText(R.string.recompute_size); 641 } 642 } 643 644 private void showDialogInner(int id, int moveErrorCode) { 645 DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 646 newFragment.setTargetFragment(this, 0); 647 newFragment.show(getFragmentManager(), "dialog " + id); 648 } 649 650 public static class MyAlertDialogFragment extends DialogFragment { 651 652 public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) { 653 MyAlertDialogFragment frag = new MyAlertDialogFragment(); 654 Bundle args = new Bundle(); 655 args.putInt("id", id); 656 args.putInt("moveError", moveErrorCode); 657 frag.setArguments(args); 658 return frag; 659 } 660 661 InstalledAppDetails getOwner() { 662 return (InstalledAppDetails)getTargetFragment(); 663 } 664 665 @Override 666 public Dialog onCreateDialog(Bundle savedInstanceState) { 667 int id = getArguments().getInt("id"); 668 int moveErrorCode = getArguments().getInt("moveError"); 669 switch (id) { 670 case DLG_CLEAR_DATA: 671 return new AlertDialog.Builder(getActivity()) 672 .setTitle(getActivity().getText(R.string.clear_data_dlg_title)) 673 .setIcon(android.R.drawable.ic_dialog_alert) 674 .setMessage(getActivity().getText(R.string.clear_data_dlg_text)) 675 .setPositiveButton(R.string.dlg_ok, 676 new DialogInterface.OnClickListener() { 677 public void onClick(DialogInterface dialog, int which) { 678 // Clear user data here 679 getOwner().initiateClearUserData(); 680 } 681 }) 682 .setNegativeButton(R.string.dlg_cancel, null) 683 .create(); 684 case DLG_FACTORY_RESET: 685 return new AlertDialog.Builder(getActivity()) 686 .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) 687 .setIcon(android.R.drawable.ic_dialog_alert) 688 .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) 689 .setPositiveButton(R.string.dlg_ok, 690 new DialogInterface.OnClickListener() { 691 public void onClick(DialogInterface dialog, int which) { 692 // Clear user data here 693 getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName); 694 } 695 }) 696 .setNegativeButton(R.string.dlg_cancel, null) 697 .create(); 698 case DLG_APP_NOT_FOUND: 699 return new AlertDialog.Builder(getActivity()) 700 .setTitle(getActivity().getText(R.string.app_not_found_dlg_title)) 701 .setIcon(android.R.drawable.ic_dialog_alert) 702 .setMessage(getActivity().getText(R.string.app_not_found_dlg_title)) 703 .setNeutralButton(getActivity().getText(R.string.dlg_ok), 704 new DialogInterface.OnClickListener() { 705 public void onClick(DialogInterface dialog, int which) { 706 //force to recompute changed value 707 getOwner().setIntentAndFinish(true, true); 708 } 709 }) 710 .create(); 711 case DLG_CANNOT_CLEAR_DATA: 712 return new AlertDialog.Builder(getActivity()) 713 .setTitle(getActivity().getText(R.string.clear_failed_dlg_title)) 714 .setIcon(android.R.drawable.ic_dialog_alert) 715 .setMessage(getActivity().getText(R.string.clear_failed_dlg_text)) 716 .setNeutralButton(R.string.dlg_ok, 717 new DialogInterface.OnClickListener() { 718 public void onClick(DialogInterface dialog, int which) { 719 getOwner().mClearDataButton.setEnabled(false); 720 //force to recompute changed value 721 getOwner().setIntentAndFinish(false, false); 722 } 723 }) 724 .create(); 725 case DLG_FORCE_STOP: 726 return new AlertDialog.Builder(getActivity()) 727 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 728 .setIcon(android.R.drawable.ic_dialog_alert) 729 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 730 .setPositiveButton(R.string.dlg_ok, 731 new DialogInterface.OnClickListener() { 732 public void onClick(DialogInterface dialog, int which) { 733 // Force stop 734 getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName); 735 } 736 }) 737 .setNegativeButton(R.string.dlg_cancel, null) 738 .create(); 739 case DLG_MOVE_FAILED: 740 CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, 741 getOwner().getMoveErrMsg(moveErrorCode)); 742 return new AlertDialog.Builder(getActivity()) 743 .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) 744 .setIcon(android.R.drawable.ic_dialog_alert) 745 .setMessage(msg) 746 .setNeutralButton(R.string.dlg_ok, null) 747 .create(); 748 } 749 throw new IllegalArgumentException("unknown id " + id); 750 } 751 } 752 753 private void uninstallPkg(String packageName) { 754 // Create new intent to launch Uninstaller activity 755 Uri packageURI = Uri.parse("package:"+packageName); 756 Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI); 757 startActivity(uninstallIntent); 758 setIntentAndFinish(true, true); 759 } 760 761 private void forceStopPackage(String pkgName) { 762 ActivityManager am = (ActivityManager)getActivity().getSystemService( 763 Context.ACTIVITY_SERVICE); 764 am.forceStopPackage(pkgName); 765 mState.invalidatePackage(pkgName); 766 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName); 767 if (newEnt != null) { 768 mAppEntry = newEnt; 769 } 770 checkForceStop(); 771 } 772 773 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 774 @Override 775 public void onReceive(Context context, Intent intent) { 776 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 777 } 778 }; 779 780 private void updateForceStopButton(boolean enabled) { 781 mForceStopButton.setEnabled(enabled); 782 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 783 } 784 785 private void checkForceStop() { 786 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 787 Uri.fromParts("package", mAppEntry.info.packageName, null)); 788 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 789 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 790 if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 791 // If the app isn't explicitly stopped, then always show the 792 // force stop button. 793 updateForceStopButton(true); 794 } else { 795 getActivity().sendOrderedBroadcast(intent, null, mCheckKillProcessesReceiver, null, 796 Activity.RESULT_CANCELED, null, null); 797 } 798 } 799 800 static class DisableChanger extends AsyncTask<Object, Object, Object> { 801 final PackageManager mPm; 802 final WeakReference<InstalledAppDetails> mActivity; 803 final ApplicationInfo mInfo; 804 final int mState; 805 806 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 807 mPm = activity.mPm; 808 mActivity = new WeakReference<InstalledAppDetails>(activity); 809 mInfo = info; 810 mState = state; 811 } 812 813 @Override 814 protected Object doInBackground(Object... params) { 815 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 816 return null; 817 } 818 } 819 820 /* 821 * Method implementing functionality of buttons clicked 822 * @see android.view.View.OnClickListener#onClick(android.view.View) 823 */ 824 public void onClick(View v) { 825 String packageName = mAppEntry.info.packageName; 826 if(v == mUninstallButton) { 827 if (mUpdatedSysApp) { 828 showDialogInner(DLG_FACTORY_RESET, 0); 829 } else { 830 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 831 new DisableChanger(this, mAppEntry.info, mAppEntry.info.enabled ? 832 PackageManager.COMPONENT_ENABLED_STATE_DISABLED 833 : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).execute((Object)null); 834 } else { 835 uninstallPkg(packageName); 836 } 837 } 838 } else if(v == mActivitiesButton) { 839 mPm.clearPackagePreferredActivities(packageName); 840 try { 841 mUsbManager.clearDefaults(packageName); 842 } catch (RemoteException e) { 843 Log.e(TAG, "mUsbManager.clearDefaults", e); 844 } 845 mActivitiesButton.setEnabled(false); 846 } else if(v == mClearDataButton) { 847 if (mAppEntry.info.manageSpaceActivityName != null) { 848 Intent intent = new Intent(Intent.ACTION_DEFAULT); 849 intent.setClassName(mAppEntry.info.packageName, 850 mAppEntry.info.manageSpaceActivityName); 851 startActivityForResult(intent, -1); 852 } else { 853 showDialogInner(DLG_CLEAR_DATA, 0); 854 } 855 } else if (v == mClearCacheButton) { 856 // Lazy initialization of observer 857 if (mClearCacheObserver == null) { 858 mClearCacheObserver = new ClearCacheObserver(); 859 } 860 mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver); 861 } else if (v == mForceStopButton) { 862 showDialogInner(DLG_FORCE_STOP, 0); 863 //forceStopPackage(mAppInfo.packageName); 864 } else if (v == mMoveAppButton) { 865 if (mPackageMoveObserver == null) { 866 mPackageMoveObserver = new PackageMoveObserver(); 867 } 868 int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? 869 PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA; 870 mMoveInProgress = true; 871 refreshButtons(); 872 mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags); 873 } 874 } 875 876 @Override 877 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 878 String packageName = mAppEntry.info.packageName; 879 ActivityManager am = (ActivityManager) 880 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 881 if (buttonView == mAskCompatibilityCB) { 882 am.setPackageAskScreenCompat(packageName, isChecked); 883 } else if (buttonView == mEnableCompatibilityCB) { 884 am.setPackageScreenCompatMode(packageName, isChecked ? 885 ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED); 886 } 887 } 888} 889 890