InstalledAppDetails.java revision 372b02c997cf265ccc04e03b6495c411472a0ab1
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.internal.telephony.ISms; 20import com.android.internal.telephony.SmsUsageMonitor; 21import com.android.settings.R; 22import com.android.settings.SettingsActivity; 23import com.android.settings.Utils; 24import com.android.settings.applications.ApplicationsState.AppEntry; 25 26import android.app.Activity; 27import android.app.ActivityManager; 28import android.app.AlertDialog; 29import android.app.Dialog; 30import android.app.DialogFragment; 31import android.app.Fragment; 32import android.app.INotificationManager; 33import android.app.admin.DevicePolicyManager; 34import android.appwidget.AppWidgetManager; 35import android.content.BroadcastReceiver; 36import android.content.ComponentName; 37import android.content.Context; 38import android.content.DialogInterface; 39import android.content.Intent; 40import android.content.IntentFilter; 41import android.content.pm.ApplicationInfo; 42import android.content.pm.IPackageDataObserver; 43import android.content.pm.IPackageMoveObserver; 44import android.content.pm.PackageInfo; 45import android.content.pm.PackageManager; 46import android.content.pm.ResolveInfo; 47import android.content.pm.PackageManager.NameNotFoundException; 48import android.content.res.Resources; 49import android.hardware.usb.IUsbManager; 50import android.net.Uri; 51import android.os.AsyncTask; 52import android.os.Bundle; 53import android.os.Environment; 54import android.os.Handler; 55import android.os.IBinder; 56import android.os.Message; 57import android.os.RemoteException; 58import android.os.ServiceManager; 59import android.os.UserHandle; 60import android.os.UserManager; 61import android.text.SpannableString; 62import android.text.TextUtils; 63import android.text.format.Formatter; 64import android.text.style.BulletSpan; 65import android.util.Log; 66 67import java.lang.ref.WeakReference; 68import java.util.ArrayList; 69import java.util.HashSet; 70import java.util.List; 71import android.view.LayoutInflater; 72import android.view.Menu; 73import android.view.MenuInflater; 74import android.view.MenuItem; 75import android.view.View; 76import android.view.ViewGroup; 77import android.widget.AdapterView; 78import android.widget.AppSecurityPermissions; 79import android.widget.ArrayAdapter; 80import android.widget.Button; 81import android.widget.CheckBox; 82import android.widget.CompoundButton; 83import android.widget.ImageView; 84import android.widget.LinearLayout; 85import android.widget.Spinner; 86import android.widget.TextView; 87 88/** 89 * Activity to display application information from Settings. This activity presents 90 * extended information associated with a package like code, data, total size, permissions 91 * used by the application and also the set of default launchable activities. 92 * For system applications, an option to clear user data is displayed only if data size is > 0. 93 * System applications that do not want clear user data do not have this option. 94 * For non-system applications, there is no option to clear data. Instead there is an option to 95 * uninstall the application. 96 */ 97public class InstalledAppDetails extends Fragment 98 implements View.OnClickListener, CompoundButton.OnCheckedChangeListener, 99 ApplicationsState.Callbacks { 100 private static final String TAG="InstalledAppDetails"; 101 private static final boolean localLOGV = false; 102 103 public static final String ARG_PACKAGE_NAME = "package"; 104 105 private PackageManager mPm; 106 private UserManager mUserManager; 107 private IUsbManager mUsbManager; 108 private AppWidgetManager mAppWidgetManager; 109 private DevicePolicyManager mDpm; 110 private ISms mSmsManager; 111 private ApplicationsState mState; 112 private ApplicationsState.Session mSession; 113 private ApplicationsState.AppEntry mAppEntry; 114 private boolean mInitialized; 115 private boolean mShowUninstalled; 116 private PackageInfo mPackageInfo; 117 private CanBeOnSdCardChecker mCanBeOnSdCardChecker; 118 private View mRootView; 119 private Button mUninstallButton; 120 private View mMoreControlButtons; 121 private Button mSpecialDisableButton; 122 private boolean mMoveInProgress = false; 123 private boolean mUpdatedSysApp = false; 124 private Button mActivitiesButton; 125 private View mScreenCompatSection; 126 private CheckBox mAskCompatibilityCB; 127 private CheckBox mEnableCompatibilityCB; 128 private boolean mCanClearData = true; 129 private TextView mAppVersion; 130 private TextView mTotalSize; 131 private TextView mAppSize; 132 private TextView mDataSize; 133 private TextView mExternalCodeSize; 134 private TextView mExternalDataSize; 135 private ClearUserDataObserver mClearDataObserver; 136 // Views related to cache info 137 private TextView mCacheSize; 138 private Button mClearCacheButton; 139 private ClearCacheObserver mClearCacheObserver; 140 private Button mForceStopButton; 141 private Button mClearDataButton; 142 private Button mMoveAppButton; 143 private CompoundButton mNotificationSwitch; 144 145 private PackageMoveObserver mPackageMoveObserver; 146 147 private final HashSet<String> mHomePackages = new HashSet<String>(); 148 149 private boolean mDisableAfterUninstall; 150 151 private boolean mHaveSizes = false; 152 private long mLastCodeSize = -1; 153 private long mLastDataSize = -1; 154 private long mLastExternalCodeSize = -1; 155 private long mLastExternalDataSize = -1; 156 private long mLastCacheSize = -1; 157 private long mLastTotalSize = -1; 158 159 //internal constants used in Handler 160 private static final int OP_SUCCESSFUL = 1; 161 private static final int OP_FAILED = 2; 162 private static final int CLEAR_USER_DATA = 1; 163 private static final int CLEAR_CACHE = 3; 164 private static final int PACKAGE_MOVE = 4; 165 166 // invalid size value used initially and also when size retrieval through PackageManager 167 // fails for whatever reason 168 private static final int SIZE_INVALID = -1; 169 170 // Resource strings 171 private CharSequence mInvalidSizeStr; 172 private CharSequence mComputingStr; 173 174 // Dialog identifiers used in showDialog 175 private static final int DLG_BASE = 0; 176 private static final int DLG_CLEAR_DATA = DLG_BASE + 1; 177 private static final int DLG_FACTORY_RESET = DLG_BASE + 2; 178 private static final int DLG_APP_NOT_FOUND = DLG_BASE + 3; 179 private static final int DLG_CANNOT_CLEAR_DATA = DLG_BASE + 4; 180 private static final int DLG_FORCE_STOP = DLG_BASE + 5; 181 private static final int DLG_MOVE_FAILED = DLG_BASE + 6; 182 private static final int DLG_DISABLE = DLG_BASE + 7; 183 private static final int DLG_DISABLE_NOTIFICATIONS = DLG_BASE + 8; 184 private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 9; 185 186 // Menu identifiers 187 public static final int UNINSTALL_ALL_USERS_MENU = 1; 188 189 // Result code identifiers 190 public static final int REQUEST_UNINSTALL = 1; 191 public static final int REQUEST_MANAGE_SPACE = 2; 192 193 private Handler mHandler = new Handler() { 194 public void handleMessage(Message msg) { 195 // If the fragment is gone, don't process any more messages. 196 if (getView() == null) { 197 return; 198 } 199 switch (msg.what) { 200 case CLEAR_USER_DATA: 201 processClearMsg(msg); 202 break; 203 case CLEAR_CACHE: 204 // Refresh size info 205 mState.requestSize(mAppEntry.info.packageName); 206 break; 207 case PACKAGE_MOVE: 208 processMoveMsg(msg); 209 break; 210 default: 211 break; 212 } 213 } 214 }; 215 216 class ClearUserDataObserver extends IPackageDataObserver.Stub { 217 public void onRemoveCompleted(final String packageName, final boolean succeeded) { 218 final Message msg = mHandler.obtainMessage(CLEAR_USER_DATA); 219 msg.arg1 = succeeded?OP_SUCCESSFUL:OP_FAILED; 220 mHandler.sendMessage(msg); 221 } 222 } 223 224 class ClearCacheObserver extends IPackageDataObserver.Stub { 225 public void onRemoveCompleted(final String packageName, final boolean succeeded) { 226 final Message msg = mHandler.obtainMessage(CLEAR_CACHE); 227 msg.arg1 = succeeded ? OP_SUCCESSFUL:OP_FAILED; 228 mHandler.sendMessage(msg); 229 } 230 } 231 232 class PackageMoveObserver extends IPackageMoveObserver.Stub { 233 public void packageMoved(String packageName, int returnCode) throws RemoteException { 234 final Message msg = mHandler.obtainMessage(PACKAGE_MOVE); 235 msg.arg1 = returnCode; 236 mHandler.sendMessage(msg); 237 } 238 } 239 240 private String getSizeStr(long size) { 241 if (size == SIZE_INVALID) { 242 return mInvalidSizeStr.toString(); 243 } 244 return Formatter.formatFileSize(getActivity(), size); 245 } 246 247 private void initDataButtons() { 248 // If the app doesn't have its own space management UI 249 // And it's a system app that doesn't allow clearing user data or is an active admin 250 // Then disable the Clear Data button. 251 if (mAppEntry.info.manageSpaceActivityName == null 252 && ((mAppEntry.info.flags&(ApplicationInfo.FLAG_SYSTEM 253 | ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA)) 254 == ApplicationInfo.FLAG_SYSTEM 255 || mDpm.packageHasActiveAdmins(mPackageInfo.packageName))) { 256 mClearDataButton.setText(R.string.clear_user_data_text); 257 mClearDataButton.setEnabled(false); 258 mCanClearData = false; 259 } else { 260 if (mAppEntry.info.manageSpaceActivityName != null) { 261 mClearDataButton.setText(R.string.manage_space_text); 262 } else { 263 mClearDataButton.setText(R.string.clear_user_data_text); 264 } 265 mClearDataButton.setOnClickListener(this); 266 } 267 } 268 269 private CharSequence getMoveErrMsg(int errCode) { 270 switch (errCode) { 271 case PackageManager.MOVE_FAILED_INSUFFICIENT_STORAGE: 272 return getActivity().getString(R.string.insufficient_storage); 273 case PackageManager.MOVE_FAILED_DOESNT_EXIST: 274 return getActivity().getString(R.string.does_not_exist); 275 case PackageManager.MOVE_FAILED_FORWARD_LOCKED: 276 return getActivity().getString(R.string.app_forward_locked); 277 case PackageManager.MOVE_FAILED_INVALID_LOCATION: 278 return getActivity().getString(R.string.invalid_location); 279 case PackageManager.MOVE_FAILED_SYSTEM_PACKAGE: 280 return getActivity().getString(R.string.system_package); 281 case PackageManager.MOVE_FAILED_INTERNAL_ERROR: 282 return ""; 283 } 284 return ""; 285 } 286 287 private void initMoveButton() { 288 if (Environment.isExternalStorageEmulated()) { 289 mMoveAppButton.setVisibility(View.INVISIBLE); 290 return; 291 } 292 boolean dataOnly = false; 293 dataOnly = (mPackageInfo == null) && (mAppEntry != null); 294 boolean moveDisable = true; 295 if (dataOnly) { 296 mMoveAppButton.setText(R.string.move_app); 297 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { 298 mMoveAppButton.setText(R.string.move_app_to_internal); 299 // Always let apps move to internal storage from sdcard. 300 moveDisable = false; 301 } else { 302 mMoveAppButton.setText(R.string.move_app_to_sdcard); 303 mCanBeOnSdCardChecker.init(); 304 moveDisable = !mCanBeOnSdCardChecker.check(mAppEntry.info); 305 } 306 if (moveDisable) { 307 mMoveAppButton.setEnabled(false); 308 } else { 309 mMoveAppButton.setOnClickListener(this); 310 mMoveAppButton.setEnabled(true); 311 } 312 } 313 314 private boolean isThisASystemPackage() { 315 try { 316 PackageInfo sys = mPm.getPackageInfo("android", PackageManager.GET_SIGNATURES); 317 return (mPackageInfo != null && mPackageInfo.signatures != null && 318 sys.signatures[0].equals(mPackageInfo.signatures[0])); 319 } catch (PackageManager.NameNotFoundException e) { 320 return false; 321 } 322 } 323 324 private boolean handleDisableable(Button button) { 325 boolean disableable = false; 326 // Try to prevent the user from bricking their phone 327 // by not allowing disabling of apps signed with the 328 // system cert and any launcher app in the system. 329 if (mHomePackages.contains(mAppEntry.info.packageName) || isThisASystemPackage()) { 330 // Disable button for core system applications. 331 button.setText(R.string.disable_text); 332 } else if (mAppEntry.info.enabled) { 333 button.setText(R.string.disable_text); 334 disableable = true; 335 } else { 336 button.setText(R.string.enable_text); 337 disableable = true; 338 } 339 340 return disableable; 341 } 342 343 private void initUninstallButtons() { 344 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 345 boolean enabled = true; 346 if (mUpdatedSysApp) { 347 mUninstallButton.setText(R.string.app_factory_reset); 348 boolean specialDisable = false; 349 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 350 specialDisable = handleDisableable(mSpecialDisableButton); 351 mSpecialDisableButton.setOnClickListener(this); 352 } 353 mMoreControlButtons.setVisibility(specialDisable ? View.VISIBLE : View.GONE); 354 } else { 355 mMoreControlButtons.setVisibility(View.GONE); 356 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 357 enabled = handleDisableable(mUninstallButton); 358 } else if ((mPackageInfo.applicationInfo.flags 359 & ApplicationInfo.FLAG_INSTALLED) == 0 360 && mUserManager.getUsers().size() >= 2) { 361 // When we have multiple users, there is a separate menu 362 // to uninstall for all users. 363 mUninstallButton.setText(R.string.uninstall_text); 364 enabled = false; 365 } else { 366 mUninstallButton.setText(R.string.uninstall_text); 367 } 368 } 369 // If this is a device admin, it can't be uninstalled or disabled. 370 // We do this here so the text of the button is still set correctly. 371 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 372 enabled = false; 373 } 374 375 // If this is the default (or only) home app, suppress uninstall (even if 376 // we still think it should be allowed for other reasons) 377 if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { 378 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 379 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 380 if (currentDefaultHome == null) { 381 // No preferred default, so permit uninstall only when 382 // there is more than one candidate 383 enabled = (mHomePackages.size() > 1); 384 } else { 385 // There is an explicit default home app -- forbid uninstall of 386 // that one, but permit it for installed-but-inactive ones. 387 enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); 388 } 389 } 390 391 mUninstallButton.setEnabled(enabled); 392 if (enabled) { 393 // Register listener 394 mUninstallButton.setOnClickListener(this); 395 } 396 } 397 398 private void initNotificationButton() { 399 INotificationManager nm = INotificationManager.Stub.asInterface( 400 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 401 boolean enabled = true; // default on 402 try { 403 enabled = nm.areNotificationsEnabledForPackage(mAppEntry.info.packageName, 404 mAppEntry.info.uid); 405 } catch (android.os.RemoteException ex) { 406 // this does not bode well 407 } 408 mNotificationSwitch.setChecked(enabled); 409 if (isThisASystemPackage()) { 410 mNotificationSwitch.setEnabled(false); 411 } else { 412 mNotificationSwitch.setEnabled(true); 413 mNotificationSwitch.setOnCheckedChangeListener(this); 414 } 415 } 416 417 /** Called when the activity is first created. */ 418 @Override 419 public void onCreate(Bundle icicle) { 420 super.onCreate(icicle); 421 422 mState = ApplicationsState.getInstance(getActivity().getApplication()); 423 mSession = mState.newSession(this); 424 mPm = getActivity().getPackageManager(); 425 mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE); 426 IBinder b = ServiceManager.getService(Context.USB_SERVICE); 427 mUsbManager = IUsbManager.Stub.asInterface(b); 428 mAppWidgetManager = AppWidgetManager.getInstance(getActivity()); 429 mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); 430 mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms")); 431 432 mCanBeOnSdCardChecker = new CanBeOnSdCardChecker(); 433 434 // Need to make sure we have loaded applications at this point. 435 mSession.resume(); 436 437 retrieveAppEntry(); 438 439 setHasOptionsMenu(true); 440 } 441 442 @Override 443 public View onCreateView( 444 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 445 final View view = inflater.inflate(R.layout.installed_app_details, container, false); 446 Utils.prepareCustomPreferencesList(container, view, view, false); 447 448 mRootView = view; 449 mComputingStr = getActivity().getText(R.string.computing_size); 450 451 // Set default values on sizes 452 mTotalSize = (TextView)view.findViewById(R.id.total_size_text); 453 mAppSize = (TextView)view.findViewById(R.id.application_size_text); 454 mDataSize = (TextView)view.findViewById(R.id.data_size_text); 455 mExternalCodeSize = (TextView)view.findViewById(R.id.external_code_size_text); 456 mExternalDataSize = (TextView)view.findViewById(R.id.external_data_size_text); 457 458 if (Environment.isExternalStorageEmulated()) { 459 ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE); 460 ((View)mExternalDataSize.getParent()).setVisibility(View.GONE); 461 } 462 463 // Get Control button panel 464 View btnPanel = view.findViewById(R.id.control_buttons_panel); 465 mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button); 466 mForceStopButton.setText(R.string.force_stop); 467 mUninstallButton = (Button)btnPanel.findViewById(R.id.right_button); 468 mForceStopButton.setEnabled(false); 469 470 // Get More Control button panel 471 mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel); 472 mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); 473 mSpecialDisableButton = (Button)mMoreControlButtons.findViewById(R.id.right_button); 474 mMoreControlButtons.setVisibility(View.GONE); 475 476 // Initialize clear data and move install location buttons 477 View data_buttons_panel = view.findViewById(R.id.data_buttons_panel); 478 mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button); 479 mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button); 480 481 // Cache section 482 mCacheSize = (TextView) view.findViewById(R.id.cache_size_text); 483 mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button); 484 485 mActivitiesButton = (Button)view.findViewById(R.id.clear_activities_button); 486 487 // Screen compatibility control 488 mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section); 489 mAskCompatibilityCB = (CheckBox)view.findViewById(R.id.ask_compatibility_cb); 490 mEnableCompatibilityCB = (CheckBox)view.findViewById(R.id.enable_compatibility_cb); 491 492 mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch); 493 494 return view; 495 } 496 497 @Override 498 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 499 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 500 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 501 } 502 503 @Override 504 public void onPrepareOptionsMenu(Menu menu) { 505 boolean showIt = true; 506 if (mUpdatedSysApp) { 507 showIt = false; 508 } else if (mAppEntry == null) { 509 showIt = false; 510 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 511 showIt = false; 512 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 513 showIt = false; 514 } else if (UserHandle.myUserId() != 0) { 515 showIt = false; 516 } else if (mUserManager.getUsers().size() < 2) { 517 showIt = false; 518 } 519 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 520 } 521 522 @Override 523 public boolean onOptionsItemSelected(MenuItem item) { 524 int menuId = item.getItemId(); 525 if (menuId == UNINSTALL_ALL_USERS_MENU) { 526 uninstallPkg(mAppEntry.info.packageName, true, false); 527 return true; 528 } 529 return false; 530 } 531 532 @Override 533 public void onActivityResult(int requestCode, int resultCode, Intent data) { 534 super.onActivityResult(requestCode, resultCode, data); 535 if (requestCode == REQUEST_UNINSTALL) { 536 if (mDisableAfterUninstall) { 537 mDisableAfterUninstall = false; 538 try { 539 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 540 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 541 | PackageManager.GET_DISABLED_COMPONENTS); 542 if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 543 new DisableChanger(this, mAppEntry.info, 544 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 545 .execute((Object)null); 546 } 547 } catch (NameNotFoundException e) { 548 } 549 } 550 if (!refreshUi()) { 551 setIntentAndFinish(true, true); 552 } 553 } 554 } 555 556 // Utility method to set application label and icon. 557 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 558 final View appSnippet = mRootView.findViewById(R.id.app_snippet); 559 appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom()); 560 561 ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon); 562 mState.ensureIcon(mAppEntry); 563 icon.setImageDrawable(mAppEntry.icon); 564 // Set application name. 565 TextView label = (TextView) appSnippet.findViewById(R.id.app_name); 566 label.setText(mAppEntry.label); 567 // Version number of application 568 mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size); 569 570 if (pkgInfo != null && pkgInfo.versionName != null) { 571 mAppVersion.setVisibility(View.VISIBLE); 572 mAppVersion.setText(getActivity().getString(R.string.version_text, 573 String.valueOf(pkgInfo.versionName))); 574 } else { 575 mAppVersion.setVisibility(View.INVISIBLE); 576 } 577 } 578 579 @Override 580 public void onResume() { 581 super.onResume(); 582 583 mSession.resume(); 584 if (!refreshUi()) { 585 setIntentAndFinish(true, true); 586 } 587 } 588 589 @Override 590 public void onPause() { 591 super.onPause(); 592 mSession.pause(); 593 } 594 595 @Override 596 public void onDestroyView() { 597 super.onDestroyView(); 598 mSession.release(); 599 } 600 601 @Override 602 public void onAllSizesComputed() { 603 } 604 605 @Override 606 public void onPackageIconChanged() { 607 } 608 609 @Override 610 public void onPackageListChanged() { 611 refreshUi(); 612 } 613 614 @Override 615 public void onRebuildComplete(ArrayList<AppEntry> apps) { 616 } 617 618 @Override 619 public void onPackageSizeChanged(String packageName) { 620 if (packageName.equals(mAppEntry.info.packageName)) { 621 refreshSizeInfo(); 622 } 623 } 624 625 @Override 626 public void onRunningStateChanged(boolean running) { 627 } 628 629 private String retrieveAppEntry() { 630 final Bundle args = getArguments(); 631 String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 632 if (packageName == null) { 633 Intent intent = (args == null) ? 634 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 635 if (intent != null) { 636 packageName = intent.getData().getSchemeSpecificPart(); 637 } 638 } 639 mAppEntry = mState.getEntry(packageName); 640 if (mAppEntry != null) { 641 // Get application info again to refresh changed properties of application 642 try { 643 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName, 644 PackageManager.GET_DISABLED_COMPONENTS | 645 PackageManager.GET_UNINSTALLED_PACKAGES | 646 PackageManager.GET_SIGNATURES); 647 } catch (NameNotFoundException e) { 648 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 649 } 650 } else { 651 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 652 mPackageInfo = null; 653 } 654 655 return packageName; 656 } 657 658 private boolean signaturesMatch(String pkg1, String pkg2) { 659 if (pkg1 != null && pkg2 != null) { 660 try { 661 final int match = mPm.checkSignatures(pkg1, pkg2); 662 if (match >= PackageManager.SIGNATURE_MATCH) { 663 return true; 664 } 665 } catch (Exception e) { 666 // e.g. named alternate package not found during lookup; 667 // this is an expected case sometimes 668 } 669 } 670 return false; 671 } 672 673 private boolean refreshUi() { 674 if (mMoveInProgress) { 675 return true; 676 } 677 final String packageName = retrieveAppEntry(); 678 679 if (mAppEntry == null) { 680 return false; // onCreate must have failed, make sure to exit 681 } 682 683 if (mPackageInfo == null) { 684 return false; // onCreate must have failed, make sure to exit 685 } 686 687 // Get list of "home" apps and trace through any meta-data references 688 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 689 mPm.getHomeActivities(homeActivities); 690 mHomePackages.clear(); 691 for (int i = 0; i< homeActivities.size(); i++) { 692 ResolveInfo ri = homeActivities.get(i); 693 final String activityPkg = ri.activityInfo.packageName; 694 mHomePackages.add(activityPkg); 695 696 // Also make sure to include anything proxying for the home app 697 final Bundle metadata = ri.activityInfo.metaData; 698 if (metadata != null) { 699 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 700 if (signaturesMatch(metaPkg, activityPkg)) { 701 mHomePackages.add(metaPkg); 702 } 703 } 704 } 705 706 // Get list of preferred activities 707 List<ComponentName> prefActList = new ArrayList<ComponentName>(); 708 709 // Intent list cannot be null. so pass empty list 710 List<IntentFilter> intentList = new ArrayList<IntentFilter>(); 711 mPm.getPreferredActivities(intentList, prefActList, packageName); 712 if (localLOGV) 713 Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); 714 boolean hasUsbDefaults = false; 715 try { 716 hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId()); 717 } catch (RemoteException e) { 718 Log.e(TAG, "mUsbManager.hasDefaults", e); 719 } 720 boolean hasBindAppWidgetPermission = 721 mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); 722 723 TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); 724 TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); 725 boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults; 726 if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { 727 resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); 728 } else { 729 boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; 730 731 if (hasBindAppWidgetPermission) { 732 autoLaunchTitleView.setText(R.string.auto_launch_label_generic); 733 } else { 734 autoLaunchTitleView.setText(R.string.auto_launch_label); 735 } 736 737 CharSequence text = null; 738 int bulletIndent = getResources() 739 .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); 740 if (autoLaunchEnabled) { 741 CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); 742 SpannableString s = new SpannableString(autoLaunchEnableText); 743 if (useBullets) { 744 s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); 745 } 746 text = (text == null) ? 747 TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); 748 } 749 if (hasBindAppWidgetPermission) { 750 CharSequence alwaysAllowBindAppWidgetsText = 751 getText(R.string.always_allow_bind_appwidgets_text); 752 SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); 753 if (useBullets) { 754 s.setSpan(new BulletSpan(bulletIndent), 755 0, alwaysAllowBindAppWidgetsText.length(), 0); 756 } 757 text = (text == null) ? 758 TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); 759 } 760 autoLaunchView.setText(text); 761 mActivitiesButton.setEnabled(true); 762 mActivitiesButton.setOnClickListener(this); 763 } 764 765 // Screen compatibility section. 766 ActivityManager am = (ActivityManager) 767 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 768 int compatMode = am.getPackageScreenCompatMode(packageName); 769 // For now these are always off; this is the old UI model which we 770 // are no longer using. 771 if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED 772 || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) { 773 mScreenCompatSection.setVisibility(View.VISIBLE); 774 mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName)); 775 mAskCompatibilityCB.setOnCheckedChangeListener(this); 776 mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED); 777 mEnableCompatibilityCB.setOnCheckedChangeListener(this); 778 } else { 779 mScreenCompatSection.setVisibility(View.GONE); 780 } 781 782 // Security permissions section 783 LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section); 784 AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName); 785 int premiumSmsPermission = getPremiumSmsPermission(packageName); 786 // Premium SMS permission implies the app also has SEND_SMS permission, so the original 787 // application permissions list doesn't have to be shown/hidden separately. The premium 788 // SMS subsection should only be visible if the app has tried to send to a premium SMS. 789 if (asp.getPermissionCount() > 0 790 || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 791 permsView.setVisibility(View.VISIBLE); 792 } else { 793 permsView.setVisibility(View.GONE); 794 } 795 // Premium SMS permission subsection 796 TextView securityBillingDesc = (TextView) permsView.findViewById( 797 R.id.security_settings_billing_desc); 798 LinearLayout securityBillingList = (LinearLayout) permsView.findViewById( 799 R.id.security_settings_billing_list); 800 if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 801 // Show the premium SMS permission selector 802 securityBillingDesc.setVisibility(View.VISIBLE); 803 securityBillingList.setVisibility(View.VISIBLE); 804 Spinner spinner = (Spinner) permsView.findViewById( 805 R.id.security_settings_premium_sms_list); 806 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(), 807 R.array.security_settings_premium_sms_values, 808 android.R.layout.simple_spinner_item); 809 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 810 spinner.setAdapter(adapter); 811 // List items are in the same order as SmsUsageMonitor constants, offset by 1. 812 spinner.setSelection(premiumSmsPermission - 1); 813 spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener( 814 packageName, mSmsManager)); 815 } else { 816 // Hide the premium SMS permission selector 817 securityBillingDesc.setVisibility(View.GONE); 818 securityBillingList.setVisibility(View.GONE); 819 } 820 // App permissions subsection 821 if (asp.getPermissionCount() > 0) { 822 // Make the security sections header visible 823 LinearLayout securityList = (LinearLayout) permsView.findViewById( 824 R.id.security_settings_list); 825 securityList.removeAllViews(); 826 securityList.addView(asp.getPermissionsViewWithRevokeButtons()); 827 // If this app is running under a shared user ID with other apps, 828 // update the description to explain this. 829 String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid); 830 if (packages != null && packages.length > 1) { 831 ArrayList<CharSequence> pnames = new ArrayList<CharSequence>(); 832 for (int i=0; i<packages.length; i++) { 833 String pkg = packages[i]; 834 if (mPackageInfo.packageName.equals(pkg)) { 835 continue; 836 } 837 try { 838 ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0); 839 pnames.add(ainfo.loadLabel(mPm)); 840 } catch (PackageManager.NameNotFoundException e) { 841 } 842 } 843 final int N = pnames.size(); 844 if (N > 0) { 845 final Resources res = getActivity().getResources(); 846 String appListStr; 847 if (N == 1) { 848 appListStr = pnames.get(0).toString(); 849 } else if (N == 2) { 850 appListStr = res.getString(R.string.join_two_items, pnames.get(0), 851 pnames.get(1)); 852 } else { 853 appListStr = pnames.get(N-2).toString(); 854 for (int i=N-3; i>=0; i--) { 855 appListStr = res.getString(i == 0 ? R.string.join_many_items_first 856 : R.string.join_many_items_middle, pnames.get(i), appListStr); 857 } 858 appListStr = res.getString(R.string.join_many_items_last, 859 appListStr, pnames.get(N-1)); 860 } 861 TextView descr = (TextView) mRootView.findViewById( 862 R.id.security_settings_desc); 863 descr.setText(res.getString(R.string.security_settings_desc_multi, 864 mPackageInfo.applicationInfo.loadLabel(mPm), appListStr)); 865 } 866 } 867 } 868 869 checkForceStop(); 870 setAppLabelAndIcon(mPackageInfo); 871 refreshButtons(); 872 refreshSizeInfo(); 873 874 if (!mInitialized) { 875 // First time init: are we displaying an uninstalled app? 876 mInitialized = true; 877 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 878 } else { 879 // All other times: if the app no longer exists then we want 880 // to go away. 881 try { 882 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 883 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 884 | PackageManager.GET_DISABLED_COMPONENTS); 885 if (!mShowUninstalled) { 886 // If we did not start out with the app uninstalled, then 887 // it transitioning to the uninstalled state for the current 888 // user means we should go away as well. 889 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 890 } 891 } catch (NameNotFoundException e) { 892 return false; 893 } 894 } 895 896 return true; 897 } 898 899 private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener { 900 private final String mPackageName; 901 private final ISms mSmsManager; 902 903 PremiumSmsSelectionListener(String packageName, ISms smsManager) { 904 mPackageName = packageName; 905 mSmsManager = smsManager; 906 } 907 908 @Override 909 public void onItemSelected(AdapterView<?> parent, View view, int position, 910 long id) { 911 if (position >= 0 && position < 3) { 912 Log.d(TAG, "Selected premium SMS policy " + position); 913 setPremiumSmsPermission(mPackageName, (position + 1)); 914 } else { 915 Log.e(TAG, "Error: unknown premium SMS policy " + position); 916 } 917 } 918 919 @Override 920 public void onNothingSelected(AdapterView<?> parent) { 921 // Ignored 922 } 923 924 private void setPremiumSmsPermission(String packageName, int permission) { 925 try { 926 if (mSmsManager != null) { 927 mSmsManager.setPremiumSmsPermission(packageName, permission); 928 } 929 } catch (RemoteException ex) { 930 // ignored 931 } 932 } 933 } 934 935 private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) { 936 title.setText(R.string.auto_launch_label); 937 autoLaunchView.setText(R.string.auto_launch_disable_text); 938 // Disable clear activities button 939 mActivitiesButton.setEnabled(false); 940 } 941 942 private void setIntentAndFinish(boolean finish, boolean appChanged) { 943 if(localLOGV) Log.i(TAG, "appChanged="+appChanged); 944 Intent intent = new Intent(); 945 intent.putExtra(ManageApplications.APP_CHG, appChanged); 946 SettingsActivity sa = (SettingsActivity)getActivity(); 947 sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); 948 } 949 950 private void refreshSizeInfo() { 951 if (mAppEntry.size == ApplicationsState.SIZE_INVALID 952 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { 953 mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1; 954 if (!mHaveSizes) { 955 mAppSize.setText(mComputingStr); 956 mDataSize.setText(mComputingStr); 957 mCacheSize.setText(mComputingStr); 958 mTotalSize.setText(mComputingStr); 959 } 960 mClearDataButton.setEnabled(false); 961 mClearCacheButton.setEnabled(false); 962 963 } else { 964 mHaveSizes = true; 965 long codeSize = mAppEntry.codeSize; 966 long dataSize = mAppEntry.dataSize; 967 if (Environment.isExternalStorageEmulated()) { 968 codeSize += mAppEntry.externalCodeSize; 969 dataSize += mAppEntry.externalDataSize; 970 } else { 971 if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { 972 mLastExternalCodeSize = mAppEntry.externalCodeSize; 973 mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize)); 974 } 975 if (mLastExternalDataSize != mAppEntry.externalDataSize) { 976 mLastExternalDataSize = mAppEntry.externalDataSize; 977 mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize)); 978 } 979 } 980 if (mLastCodeSize != codeSize) { 981 mLastCodeSize = codeSize; 982 mAppSize.setText(getSizeStr(codeSize)); 983 } 984 if (mLastDataSize != dataSize) { 985 mLastDataSize = dataSize; 986 mDataSize.setText(getSizeStr(dataSize)); 987 } 988 long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; 989 if (mLastCacheSize != cacheSize) { 990 mLastCacheSize = cacheSize; 991 mCacheSize.setText(getSizeStr(cacheSize)); 992 } 993 if (mLastTotalSize != mAppEntry.size) { 994 mLastTotalSize = mAppEntry.size; 995 mTotalSize.setText(getSizeStr(mAppEntry.size)); 996 } 997 998 if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) { 999 mClearDataButton.setEnabled(false); 1000 } else { 1001 mClearDataButton.setEnabled(true); 1002 mClearDataButton.setOnClickListener(this); 1003 } 1004 if (cacheSize <= 0) { 1005 mClearCacheButton.setEnabled(false); 1006 } else { 1007 mClearCacheButton.setEnabled(true); 1008 mClearCacheButton.setOnClickListener(this); 1009 } 1010 } 1011 } 1012 1013 /* 1014 * Private method to handle clear message notification from observer when 1015 * the async operation from PackageManager is complete 1016 */ 1017 private void processClearMsg(Message msg) { 1018 int result = msg.arg1; 1019 String packageName = mAppEntry.info.packageName; 1020 mClearDataButton.setText(R.string.clear_user_data_text); 1021 if(result == OP_SUCCESSFUL) { 1022 Log.i(TAG, "Cleared user data for package : "+packageName); 1023 mState.requestSize(mAppEntry.info.packageName); 1024 } else { 1025 mClearDataButton.setEnabled(true); 1026 } 1027 checkForceStop(); 1028 } 1029 1030 private void refreshButtons() { 1031 if (!mMoveInProgress) { 1032 initUninstallButtons(); 1033 initDataButtons(); 1034 initMoveButton(); 1035 initNotificationButton(); 1036 } else { 1037 mMoveAppButton.setText(R.string.moving); 1038 mMoveAppButton.setEnabled(false); 1039 mUninstallButton.setEnabled(false); 1040 mSpecialDisableButton.setEnabled(false); 1041 } 1042 } 1043 1044 private void processMoveMsg(Message msg) { 1045 int result = msg.arg1; 1046 String packageName = mAppEntry.info.packageName; 1047 // Refresh the button attributes. 1048 mMoveInProgress = false; 1049 if (result == PackageManager.MOVE_SUCCEEDED) { 1050 Log.i(TAG, "Moved resources for " + packageName); 1051 // Refresh size information again. 1052 mState.requestSize(mAppEntry.info.packageName); 1053 } else { 1054 showDialogInner(DLG_MOVE_FAILED, result); 1055 } 1056 refreshUi(); 1057 } 1058 1059 /* 1060 * Private method to initiate clearing user data when the user clicks the clear data 1061 * button for a system package 1062 */ 1063 private void initiateClearUserData() { 1064 mClearDataButton.setEnabled(false); 1065 // Invoke uninstall or clear user data based on sysPackage 1066 String packageName = mAppEntry.info.packageName; 1067 Log.i(TAG, "Clearing user data for package : " + packageName); 1068 if (mClearDataObserver == null) { 1069 mClearDataObserver = new ClearUserDataObserver(); 1070 } 1071 ActivityManager am = (ActivityManager) 1072 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 1073 boolean res = am.clearApplicationUserData(packageName, mClearDataObserver); 1074 if (!res) { 1075 // Clearing data failed for some obscure reason. Just log error for now 1076 Log.i(TAG, "Couldnt clear application user data for package:"+packageName); 1077 showDialogInner(DLG_CANNOT_CLEAR_DATA, 0); 1078 } else { 1079 mClearDataButton.setText(R.string.recompute_size); 1080 } 1081 } 1082 1083 private void showDialogInner(int id, int moveErrorCode) { 1084 DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 1085 newFragment.setTargetFragment(this, 0); 1086 newFragment.show(getFragmentManager(), "dialog " + id); 1087 } 1088 1089 public static class MyAlertDialogFragment extends DialogFragment { 1090 1091 public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) { 1092 MyAlertDialogFragment frag = new MyAlertDialogFragment(); 1093 Bundle args = new Bundle(); 1094 args.putInt("id", id); 1095 args.putInt("moveError", moveErrorCode); 1096 frag.setArguments(args); 1097 return frag; 1098 } 1099 1100 InstalledAppDetails getOwner() { 1101 return (InstalledAppDetails)getTargetFragment(); 1102 } 1103 1104 @Override 1105 public Dialog onCreateDialog(Bundle savedInstanceState) { 1106 int id = getArguments().getInt("id"); 1107 int moveErrorCode = getArguments().getInt("moveError"); 1108 switch (id) { 1109 case DLG_CLEAR_DATA: 1110 return new AlertDialog.Builder(getActivity()) 1111 .setTitle(getActivity().getText(R.string.clear_data_dlg_title)) 1112 .setIconAttribute(android.R.attr.alertDialogIcon) 1113 .setMessage(getActivity().getText(R.string.clear_data_dlg_text)) 1114 .setPositiveButton(R.string.dlg_ok, 1115 new DialogInterface.OnClickListener() { 1116 public void onClick(DialogInterface dialog, int which) { 1117 // Clear user data here 1118 getOwner().initiateClearUserData(); 1119 } 1120 }) 1121 .setNegativeButton(R.string.dlg_cancel, null) 1122 .create(); 1123 case DLG_FACTORY_RESET: 1124 return new AlertDialog.Builder(getActivity()) 1125 .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) 1126 .setIconAttribute(android.R.attr.alertDialogIcon) 1127 .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) 1128 .setPositiveButton(R.string.dlg_ok, 1129 new DialogInterface.OnClickListener() { 1130 public void onClick(DialogInterface dialog, int which) { 1131 // Clear user data here 1132 getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, 1133 false, false); 1134 } 1135 }) 1136 .setNegativeButton(R.string.dlg_cancel, null) 1137 .create(); 1138 case DLG_APP_NOT_FOUND: 1139 return new AlertDialog.Builder(getActivity()) 1140 .setTitle(getActivity().getText(R.string.app_not_found_dlg_title)) 1141 .setIconAttribute(android.R.attr.alertDialogIcon) 1142 .setMessage(getActivity().getText(R.string.app_not_found_dlg_title)) 1143 .setNeutralButton(getActivity().getText(R.string.dlg_ok), 1144 new DialogInterface.OnClickListener() { 1145 public void onClick(DialogInterface dialog, int which) { 1146 //force to recompute changed value 1147 getOwner().setIntentAndFinish(true, true); 1148 } 1149 }) 1150 .create(); 1151 case DLG_CANNOT_CLEAR_DATA: 1152 return new AlertDialog.Builder(getActivity()) 1153 .setTitle(getActivity().getText(R.string.clear_failed_dlg_title)) 1154 .setIconAttribute(android.R.attr.alertDialogIcon) 1155 .setMessage(getActivity().getText(R.string.clear_failed_dlg_text)) 1156 .setNeutralButton(R.string.dlg_ok, 1157 new DialogInterface.OnClickListener() { 1158 public void onClick(DialogInterface dialog, int which) { 1159 getOwner().mClearDataButton.setEnabled(false); 1160 //force to recompute changed value 1161 getOwner().setIntentAndFinish(false, false); 1162 } 1163 }) 1164 .create(); 1165 case DLG_FORCE_STOP: 1166 return new AlertDialog.Builder(getActivity()) 1167 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 1168 .setIconAttribute(android.R.attr.alertDialogIcon) 1169 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 1170 .setPositiveButton(R.string.dlg_ok, 1171 new DialogInterface.OnClickListener() { 1172 public void onClick(DialogInterface dialog, int which) { 1173 // Force stop 1174 getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName); 1175 } 1176 }) 1177 .setNegativeButton(R.string.dlg_cancel, null) 1178 .create(); 1179 case DLG_MOVE_FAILED: 1180 CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, 1181 getOwner().getMoveErrMsg(moveErrorCode)); 1182 return new AlertDialog.Builder(getActivity()) 1183 .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) 1184 .setIconAttribute(android.R.attr.alertDialogIcon) 1185 .setMessage(msg) 1186 .setNeutralButton(R.string.dlg_ok, null) 1187 .create(); 1188 case DLG_DISABLE: 1189 return new AlertDialog.Builder(getActivity()) 1190 .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) 1191 .setIconAttribute(android.R.attr.alertDialogIcon) 1192 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 1193 .setPositiveButton(R.string.dlg_ok, 1194 new DialogInterface.OnClickListener() { 1195 public void onClick(DialogInterface dialog, int which) { 1196 // Disable the app 1197 new DisableChanger(getOwner(), getOwner().mAppEntry.info, 1198 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 1199 .execute((Object)null); 1200 } 1201 }) 1202 .setNegativeButton(R.string.dlg_cancel, null) 1203 .create(); 1204 case DLG_DISABLE_NOTIFICATIONS: 1205 return new AlertDialog.Builder(getActivity()) 1206 .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title)) 1207 .setIconAttribute(android.R.attr.alertDialogIcon) 1208 .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text)) 1209 .setPositiveButton(R.string.dlg_ok, 1210 new DialogInterface.OnClickListener() { 1211 public void onClick(DialogInterface dialog, int which) { 1212 // Disable the package's notifications 1213 getOwner().setNotificationsEnabled(false); 1214 } 1215 }) 1216 .setNegativeButton(R.string.dlg_cancel, 1217 new DialogInterface.OnClickListener() { 1218 public void onClick(DialogInterface dialog, int which) { 1219 // Re-enable the checkbox 1220 getOwner().mNotificationSwitch.setChecked(true); 1221 } 1222 }) 1223 .create(); 1224 case DLG_SPECIAL_DISABLE: 1225 return new AlertDialog.Builder(getActivity()) 1226 .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) 1227 .setIconAttribute(android.R.attr.alertDialogIcon) 1228 .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) 1229 .setPositiveButton(R.string.dlg_ok, 1230 new DialogInterface.OnClickListener() { 1231 public void onClick(DialogInterface dialog, int which) { 1232 // Clear user data here 1233 getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, 1234 false, true); 1235 } 1236 }) 1237 .setNegativeButton(R.string.dlg_cancel, null) 1238 .create(); 1239 } 1240 throw new IllegalArgumentException("unknown id " + id); 1241 } 1242 } 1243 1244 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 1245 // Create new intent to launch Uninstaller activity 1246 Uri packageURI = Uri.parse("package:"+packageName); 1247 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 1248 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 1249 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 1250 mDisableAfterUninstall = andDisable; 1251 } 1252 1253 private void forceStopPackage(String pkgName) { 1254 ActivityManager am = (ActivityManager)getActivity().getSystemService( 1255 Context.ACTIVITY_SERVICE); 1256 am.forceStopPackage(pkgName); 1257 mState.invalidatePackage(pkgName); 1258 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName); 1259 if (newEnt != null) { 1260 mAppEntry = newEnt; 1261 } 1262 checkForceStop(); 1263 } 1264 1265 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 1266 @Override 1267 public void onReceive(Context context, Intent intent) { 1268 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 1269 } 1270 }; 1271 1272 private void updateForceStopButton(boolean enabled) { 1273 mForceStopButton.setEnabled(enabled); 1274 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 1275 } 1276 1277 private void checkForceStop() { 1278 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 1279 // User can't force stop device admin. 1280 updateForceStopButton(false); 1281 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 1282 // If the app isn't explicitly stopped, then always show the 1283 // force stop button. 1284 updateForceStopButton(true); 1285 } else { 1286 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 1287 Uri.fromParts("package", mAppEntry.info.packageName, null)); 1288 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 1289 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 1290 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 1291 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 1292 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 1293 } 1294 } 1295 1296 static class DisableChanger extends AsyncTask<Object, Object, Object> { 1297 final PackageManager mPm; 1298 final WeakReference<InstalledAppDetails> mActivity; 1299 final ApplicationInfo mInfo; 1300 final int mState; 1301 1302 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 1303 mPm = activity.mPm; 1304 mActivity = new WeakReference<InstalledAppDetails>(activity); 1305 mInfo = info; 1306 mState = state; 1307 } 1308 1309 @Override 1310 protected Object doInBackground(Object... params) { 1311 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 1312 return null; 1313 } 1314 } 1315 1316 private void setNotificationsEnabled(boolean enabled) { 1317 String packageName = mAppEntry.info.packageName; 1318 INotificationManager nm = INotificationManager.Stub.asInterface( 1319 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 1320 try { 1321 final boolean enable = mNotificationSwitch.isChecked(); 1322 nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled); 1323 } catch (android.os.RemoteException ex) { 1324 mNotificationSwitch.setChecked(!enabled); // revert 1325 } 1326 } 1327 1328 private int getPremiumSmsPermission(String packageName) { 1329 try { 1330 if (mSmsManager != null) { 1331 return mSmsManager.getPremiumSmsPermission(packageName); 1332 } 1333 } catch (RemoteException ex) { 1334 // ignored 1335 } 1336 return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN; 1337 } 1338 1339 /* 1340 * Method implementing functionality of buttons clicked 1341 * @see android.view.View.OnClickListener#onClick(android.view.View) 1342 */ 1343 public void onClick(View v) { 1344 String packageName = mAppEntry.info.packageName; 1345 if(v == mUninstallButton) { 1346 if (mUpdatedSysApp) { 1347 showDialogInner(DLG_FACTORY_RESET, 0); 1348 } else { 1349 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 1350 if (mAppEntry.info.enabled) { 1351 showDialogInner(DLG_DISABLE, 0); 1352 } else { 1353 new DisableChanger(this, mAppEntry.info, 1354 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 1355 .execute((Object)null); 1356 } 1357 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 1358 uninstallPkg(packageName, true, false); 1359 } else { 1360 uninstallPkg(packageName, false, false); 1361 } 1362 } 1363 } else if(v == mSpecialDisableButton) { 1364 showDialogInner(DLG_SPECIAL_DISABLE, 0); 1365 } else if(v == mActivitiesButton) { 1366 mPm.clearPackagePreferredActivities(packageName); 1367 try { 1368 mUsbManager.clearDefaults(packageName, UserHandle.myUserId()); 1369 } catch (RemoteException e) { 1370 Log.e(TAG, "mUsbManager.clearDefaults", e); 1371 } 1372 mAppWidgetManager.setBindAppWidgetPermission(packageName, false); 1373 TextView autoLaunchTitleView = 1374 (TextView) mRootView.findViewById(R.id.auto_launch_title); 1375 TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); 1376 resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); 1377 } else if(v == mClearDataButton) { 1378 if (mAppEntry.info.manageSpaceActivityName != null) { 1379 if (!Utils.isMonkeyRunning()) { 1380 Intent intent = new Intent(Intent.ACTION_DEFAULT); 1381 intent.setClassName(mAppEntry.info.packageName, 1382 mAppEntry.info.manageSpaceActivityName); 1383 startActivityForResult(intent, REQUEST_MANAGE_SPACE); 1384 } 1385 } else { 1386 showDialogInner(DLG_CLEAR_DATA, 0); 1387 } 1388 } else if (v == mClearCacheButton) { 1389 // Lazy initialization of observer 1390 if (mClearCacheObserver == null) { 1391 mClearCacheObserver = new ClearCacheObserver(); 1392 } 1393 mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver); 1394 } else if (v == mForceStopButton) { 1395 showDialogInner(DLG_FORCE_STOP, 0); 1396 //forceStopPackage(mAppInfo.packageName); 1397 } else if (v == mMoveAppButton) { 1398 if (mPackageMoveObserver == null) { 1399 mPackageMoveObserver = new PackageMoveObserver(); 1400 } 1401 int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? 1402 PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA; 1403 mMoveInProgress = true; 1404 refreshButtons(); 1405 mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags); 1406 } 1407 } 1408 1409 @Override 1410 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1411 String packageName = mAppEntry.info.packageName; 1412 ActivityManager am = (ActivityManager) 1413 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 1414 if (buttonView == mAskCompatibilityCB) { 1415 am.setPackageAskScreenCompat(packageName, isChecked); 1416 } else if (buttonView == mEnableCompatibilityCB) { 1417 am.setPackageScreenCompatMode(packageName, isChecked ? 1418 ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED); 1419 } else if (buttonView == mNotificationSwitch) { 1420 if (!isChecked) { 1421 showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0); 1422 } else { 1423 setNotificationsEnabled(true); 1424 } 1425 } 1426 } 1427} 1428 1429