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