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