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