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