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