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