1 /** 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17 package com.android.settings.applications; 18 19 import com.android.internal.telephony.ISms; 20 import com.android.internal.telephony.SmsUsageMonitor; 21 import com.android.settings.R; 22 import com.android.settings.SettingsActivity; 23 import com.android.settings.Utils; 24 import com.android.settings.applications.ApplicationsState.AppEntry; 25 26 import android.app.Activity; 27 import android.app.ActivityManager; 28 import android.app.AlertDialog; 29 import android.app.Dialog; 30 import android.app.DialogFragment; 31 import android.app.Fragment; 32 import android.app.INotificationManager; 33 import android.app.admin.DevicePolicyManager; 34 import android.appwidget.AppWidgetManager; 35 import android.content.BroadcastReceiver; 36 import android.content.ComponentName; 37 import android.content.Context; 38 import android.content.DialogInterface; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.pm.ApplicationInfo; 42 import android.content.pm.IPackageDataObserver; 43 import android.content.pm.IPackageMoveObserver; 44 import android.content.pm.PackageInfo; 45 import android.content.pm.PackageManager; 46 import android.content.pm.ResolveInfo; 47 import android.content.pm.PackageManager.NameNotFoundException; 48 import android.content.res.Resources; 49 import android.hardware.usb.IUsbManager; 50 import android.net.Uri; 51 import android.os.AsyncTask; 52 import android.os.Bundle; 53 import android.os.Environment; 54 import android.os.Handler; 55 import android.os.IBinder; 56 import android.os.Message; 57 import android.os.RemoteException; 58 import android.os.ServiceManager; 59 import android.os.UserHandle; 60 import android.os.UserManager; 61 import android.text.SpannableString; 62 import android.text.TextUtils; 63 import android.text.format.Formatter; 64 import android.text.style.BulletSpan; 65 import android.util.Log; 66 67 import java.lang.ref.WeakReference; 68 import java.util.ArrayList; 69 import java.util.HashSet; 70 import java.util.List; 71 import android.view.LayoutInflater; 72 import android.view.Menu; 73 import android.view.MenuInflater; 74 import android.view.MenuItem; 75 import android.view.View; 76 import android.view.ViewGroup; 77 import android.widget.AdapterView; 78 import android.widget.AppSecurityPermissions; 79 import android.widget.ArrayAdapter; 80 import android.widget.Button; 81 import android.widget.CheckBox; 82 import android.widget.CompoundButton; 83 import android.widget.ImageView; 84 import android.widget.LinearLayout; 85 import android.widget.Spinner; 86 import 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 */ 97 public 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 { 424 mNotificationSwitch.setEnabled(true); 425 mNotificationSwitch.setOnCheckedChangeListener(this); 426 } 427 } 428 429 /** Called when the activity is first created. */ 430 @Override 431 public void onCreate(Bundle icicle) { 432 super.onCreate(icicle); 433 434 mState = ApplicationsState.getInstance(getActivity().getApplication()); 435 mSession = mState.newSession(this); 436 mPm = getActivity().getPackageManager(); 437 mUserManager = (UserManager)getActivity().getSystemService(Context.USER_SERVICE); 438 IBinder b = ServiceManager.getService(Context.USB_SERVICE); 439 mUsbManager = IUsbManager.Stub.asInterface(b); 440 mAppWidgetManager = AppWidgetManager.getInstance(getActivity()); 441 mDpm = (DevicePolicyManager)getActivity().getSystemService(Context.DEVICE_POLICY_SERVICE); 442 mSmsManager = ISms.Stub.asInterface(ServiceManager.getService("isms")); 443 444 mCanBeOnSdCardChecker = new CanBeOnSdCardChecker(); 445 446 // Need to make sure we have loaded applications at this point. 447 mSession.resume(); 448 449 retrieveAppEntry(); 450 451 setHasOptionsMenu(true); 452 } 453 454 @Override 455 public View onCreateView( 456 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 457 final View view = inflater.inflate(R.layout.installed_app_details, container, false); 458 459 final ViewGroup allDetails = (ViewGroup) view.findViewById(R.id.all_details); 460 Utils.forceCustomPadding(allDetails, true /* additive padding */); 461 462 mRootView = view; 463 mComputingStr = getActivity().getText(R.string.computing_size); 464 465 // Set default values on sizes 466 mTotalSize = (TextView) view.findViewById(R.id.total_size_text); 467 mAppSize = (TextView) view.findViewById(R.id.application_size_text); 468 mDataSize = (TextView) view.findViewById(R.id.data_size_text); 469 mExternalCodeSize = (TextView) view.findViewById(R.id.external_code_size_text); 470 mExternalDataSize = (TextView) view.findViewById(R.id.external_data_size_text); 471 472 if (Environment.isExternalStorageEmulated()) { 473 ((View)mExternalCodeSize.getParent()).setVisibility(View.GONE); 474 ((View)mExternalDataSize.getParent()).setVisibility(View.GONE); 475 } 476 477 // Get Control button panel 478 View btnPanel = view.findViewById(R.id.control_buttons_panel); 479 mForceStopButton = (Button) btnPanel.findViewById(R.id.left_button); 480 mForceStopButton.setText(R.string.force_stop); 481 mUninstallButton = (Button) btnPanel.findViewById(R.id.right_button); 482 mForceStopButton.setEnabled(false); 483 484 // Get More Control button panel 485 mMoreControlButtons = view.findViewById(R.id.more_control_buttons_panel); 486 mMoreControlButtons.findViewById(R.id.left_button).setVisibility(View.INVISIBLE); 487 mSpecialDisableButton = (Button) mMoreControlButtons.findViewById(R.id.right_button); 488 mMoreControlButtons.setVisibility(View.GONE); 489 490 // Initialize clear data and move install location buttons 491 View data_buttons_panel = view.findViewById(R.id.data_buttons_panel); 492 mClearDataButton = (Button) data_buttons_panel.findViewById(R.id.right_button); 493 mMoveAppButton = (Button) data_buttons_panel.findViewById(R.id.left_button); 494 495 // Cache section 496 mCacheSize = (TextView) view.findViewById(R.id.cache_size_text); 497 mClearCacheButton = (Button) view.findViewById(R.id.clear_cache_button); 498 499 mActivitiesButton = (Button) view.findViewById(R.id.clear_activities_button); 500 501 // Screen compatibility control 502 mScreenCompatSection = view.findViewById(R.id.screen_compatibility_section); 503 mAskCompatibilityCB = (CheckBox) view.findViewById(R.id.ask_compatibility_cb); 504 mEnableCompatibilityCB = (CheckBox) view.findViewById(R.id.enable_compatibility_cb); 505 506 mNotificationSwitch = (CompoundButton) view.findViewById(R.id.notification_switch); 507 508 return view; 509 } 510 511 @Override 512 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 513 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 514 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 515 } 516 517 @Override 518 public void onPrepareOptionsMenu(Menu menu) { 519 boolean showIt = true; 520 if (mUpdatedSysApp) { 521 showIt = false; 522 } else if (mAppEntry == null) { 523 showIt = false; 524 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 525 showIt = false; 526 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 527 showIt = false; 528 } else if (UserHandle.myUserId() != 0) { 529 showIt = false; 530 } else if (mUserManager.getUsers().size() < 2) { 531 showIt = false; 532 } 533 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 534 } 535 536 @Override 537 public boolean onOptionsItemSelected(MenuItem item) { 538 int menuId = item.getItemId(); 539 if (menuId == UNINSTALL_ALL_USERS_MENU) { 540 uninstallPkg(mAppEntry.info.packageName, true, false); 541 return true; 542 } 543 return false; 544 } 545 546 @Override 547 public void onActivityResult(int requestCode, int resultCode, Intent data) { 548 super.onActivityResult(requestCode, resultCode, data); 549 if (requestCode == REQUEST_UNINSTALL) { 550 if (mDisableAfterUninstall) { 551 mDisableAfterUninstall = false; 552 try { 553 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 554 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 555 | PackageManager.GET_DISABLED_COMPONENTS); 556 if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 557 new DisableChanger(this, mAppEntry.info, 558 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 559 .execute((Object)null); 560 } 561 } catch (NameNotFoundException e) { 562 } 563 } 564 if (!refreshUi()) { 565 setIntentAndFinish(true, true); 566 } 567 } 568 } 569 570 // Utility method to set application label and icon. 571 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 572 final View appSnippet = mRootView.findViewById(R.id.app_snippet); 573 appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, appSnippet.getPaddingBottom()); 574 575 ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon); 576 mState.ensureIcon(mAppEntry); 577 icon.setImageDrawable(mAppEntry.icon); 578 // Set application name. 579 TextView label = (TextView) appSnippet.findViewById(R.id.app_name); 580 label.setText(mAppEntry.label); 581 // Version number of application 582 mAppVersion = (TextView) appSnippet.findViewById(R.id.app_size); 583 584 if (pkgInfo != null && pkgInfo.versionName != null) { 585 mAppVersion.setVisibility(View.VISIBLE); 586 mAppVersion.setText(getActivity().getString(R.string.version_text, 587 String.valueOf(pkgInfo.versionName))); 588 } else { 589 mAppVersion.setVisibility(View.INVISIBLE); 590 } 591 } 592 593 @Override 594 public void onResume() { 595 super.onResume(); 596 597 mAppControlRestricted = mUserManager.hasUserRestriction(UserManager.DISALLOW_APPS_CONTROL); 598 mSession.resume(); 599 if (!refreshUi()) { 600 setIntentAndFinish(true, true); 601 } 602 } 603 604 @Override 605 public void onPause() { 606 super.onPause(); 607 mSession.pause(); 608 } 609 610 @Override 611 public void onDestroyView() { 612 super.onDestroyView(); 613 mSession.release(); 614 } 615 616 @Override 617 public void onAllSizesComputed() { 618 } 619 620 @Override 621 public void onPackageIconChanged() { 622 } 623 624 @Override 625 public void onPackageListChanged() { 626 refreshUi(); 627 } 628 629 @Override 630 public void onRebuildComplete(ArrayList<AppEntry> apps) { 631 } 632 633 @Override 634 public void onPackageSizeChanged(String packageName) { 635 if (packageName.equals(mAppEntry.info.packageName)) { 636 refreshSizeInfo(); 637 } 638 } 639 640 @Override 641 public void onRunningStateChanged(boolean running) { 642 } 643 644 private String retrieveAppEntry() { 645 final Bundle args = getArguments(); 646 String packageName = (args != null) ? args.getString(ARG_PACKAGE_NAME) : null; 647 if (packageName == null) { 648 Intent intent = (args == null) ? 649 getActivity().getIntent() : (Intent) args.getParcelable("intent"); 650 if (intent != null) { 651 packageName = intent.getData().getSchemeSpecificPart(); 652 } 653 } 654 mAppEntry = mState.getEntry(packageName); 655 if (mAppEntry != null) { 656 // Get application info again to refresh changed properties of application 657 try { 658 mPackageInfo = mPm.getPackageInfo(mAppEntry.info.packageName, 659 PackageManager.GET_DISABLED_COMPONENTS | 660 PackageManager.GET_UNINSTALLED_PACKAGES | 661 PackageManager.GET_SIGNATURES); 662 } catch (NameNotFoundException e) { 663 Log.e(TAG, "Exception when retrieving package:" + mAppEntry.info.packageName, e); 664 } 665 } else { 666 Log.w(TAG, "Missing AppEntry; maybe reinstalling?"); 667 mPackageInfo = null; 668 } 669 670 return packageName; 671 } 672 673 private boolean signaturesMatch(String pkg1, String pkg2) { 674 if (pkg1 != null && pkg2 != null) { 675 try { 676 final int match = mPm.checkSignatures(pkg1, pkg2); 677 if (match >= PackageManager.SIGNATURE_MATCH) { 678 return true; 679 } 680 } catch (Exception e) { 681 // e.g. named alternate package not found during lookup; 682 // this is an expected case sometimes 683 } 684 } 685 return false; 686 } 687 688 private boolean refreshUi() { 689 if (mMoveInProgress) { 690 return true; 691 } 692 final String packageName = retrieveAppEntry(); 693 694 if (mAppEntry == null) { 695 return false; // onCreate must have failed, make sure to exit 696 } 697 698 if (mPackageInfo == null) { 699 return false; // onCreate must have failed, make sure to exit 700 } 701 702 // Get list of "home" apps and trace through any meta-data references 703 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 704 mPm.getHomeActivities(homeActivities); 705 mHomePackages.clear(); 706 for (int i = 0; i< homeActivities.size(); i++) { 707 ResolveInfo ri = homeActivities.get(i); 708 final String activityPkg = ri.activityInfo.packageName; 709 mHomePackages.add(activityPkg); 710 711 // Also make sure to include anything proxying for the home app 712 final Bundle metadata = ri.activityInfo.metaData; 713 if (metadata != null) { 714 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 715 if (signaturesMatch(metaPkg, activityPkg)) { 716 mHomePackages.add(metaPkg); 717 } 718 } 719 } 720 721 // Get list of preferred activities 722 List<ComponentName> prefActList = new ArrayList<ComponentName>(); 723 724 // Intent list cannot be null. so pass empty list 725 List<IntentFilter> intentList = new ArrayList<IntentFilter>(); 726 mPm.getPreferredActivities(intentList, prefActList, packageName); 727 if (localLOGV) 728 Log.i(TAG, "Have " + prefActList.size() + " number of activities in preferred list"); 729 boolean hasUsbDefaults = false; 730 try { 731 hasUsbDefaults = mUsbManager.hasDefaults(packageName, UserHandle.myUserId()); 732 } catch (RemoteException e) { 733 Log.e(TAG, "mUsbManager.hasDefaults", e); 734 } 735 boolean hasBindAppWidgetPermission = 736 mAppWidgetManager.hasBindAppWidgetPermission(mAppEntry.info.packageName); 737 738 TextView autoLaunchTitleView = (TextView) mRootView.findViewById(R.id.auto_launch_title); 739 TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); 740 boolean autoLaunchEnabled = prefActList.size() > 0 || hasUsbDefaults; 741 if (!autoLaunchEnabled && !hasBindAppWidgetPermission) { 742 resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); 743 } else { 744 boolean useBullets = hasBindAppWidgetPermission && autoLaunchEnabled; 745 746 if (hasBindAppWidgetPermission) { 747 autoLaunchTitleView.setText(R.string.auto_launch_label_generic); 748 } else { 749 autoLaunchTitleView.setText(R.string.auto_launch_label); 750 } 751 752 CharSequence text = null; 753 int bulletIndent = getResources() 754 .getDimensionPixelSize(R.dimen.installed_app_details_bullet_offset); 755 if (autoLaunchEnabled) { 756 CharSequence autoLaunchEnableText = getText(R.string.auto_launch_enable_text); 757 SpannableString s = new SpannableString(autoLaunchEnableText); 758 if (useBullets) { 759 s.setSpan(new BulletSpan(bulletIndent), 0, autoLaunchEnableText.length(), 0); 760 } 761 text = (text == null) ? 762 TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); 763 } 764 if (hasBindAppWidgetPermission) { 765 CharSequence alwaysAllowBindAppWidgetsText = 766 getText(R.string.always_allow_bind_appwidgets_text); 767 SpannableString s = new SpannableString(alwaysAllowBindAppWidgetsText); 768 if (useBullets) { 769 s.setSpan(new BulletSpan(bulletIndent), 770 0, alwaysAllowBindAppWidgetsText.length(), 0); 771 } 772 text = (text == null) ? 773 TextUtils.concat(s, "\n") : TextUtils.concat(text, "\n", s, "\n"); 774 } 775 autoLaunchView.setText(text); 776 mActivitiesButton.setEnabled(true); 777 mActivitiesButton.setOnClickListener(this); 778 } 779 780 // Screen compatibility section. 781 ActivityManager am = (ActivityManager) 782 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 783 int compatMode = am.getPackageScreenCompatMode(packageName); 784 // For now these are always off; this is the old UI model which we 785 // are no longer using. 786 if (false && (compatMode == ActivityManager.COMPAT_MODE_DISABLED 787 || compatMode == ActivityManager.COMPAT_MODE_ENABLED)) { 788 mScreenCompatSection.setVisibility(View.VISIBLE); 789 mAskCompatibilityCB.setChecked(am.getPackageAskScreenCompat(packageName)); 790 mAskCompatibilityCB.setOnCheckedChangeListener(this); 791 mEnableCompatibilityCB.setChecked(compatMode == ActivityManager.COMPAT_MODE_ENABLED); 792 mEnableCompatibilityCB.setOnCheckedChangeListener(this); 793 } else { 794 mScreenCompatSection.setVisibility(View.GONE); 795 } 796 797 // Security permissions section 798 LinearLayout permsView = (LinearLayout) mRootView.findViewById(R.id.permissions_section); 799 AppSecurityPermissions asp = new AppSecurityPermissions(getActivity(), packageName); 800 int premiumSmsPermission = getPremiumSmsPermission(packageName); 801 // Premium SMS permission implies the app also has SEND_SMS permission, so the original 802 // application permissions list doesn't have to be shown/hidden separately. The premium 803 // SMS subsection should only be visible if the app has tried to send to a premium SMS. 804 if (asp.getPermissionCount() > 0 805 || premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 806 permsView.setVisibility(View.VISIBLE); 807 } else { 808 permsView.setVisibility(View.GONE); 809 } 810 // Premium SMS permission subsection 811 TextView securityBillingDesc = (TextView) permsView.findViewById( 812 R.id.security_settings_billing_desc); 813 LinearLayout securityBillingList = (LinearLayout) permsView.findViewById( 814 R.id.security_settings_billing_list); 815 if (premiumSmsPermission != SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 816 // Show the premium SMS permission selector 817 securityBillingDesc.setVisibility(View.VISIBLE); 818 securityBillingList.setVisibility(View.VISIBLE); 819 Spinner spinner = (Spinner) permsView.findViewById( 820 R.id.security_settings_premium_sms_list); 821 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getActivity(), 822 R.array.security_settings_premium_sms_values, 823 android.R.layout.simple_spinner_item); 824 adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); 825 spinner.setAdapter(adapter); 826 // List items are in the same order as SmsUsageMonitor constants, offset by 1. 827 spinner.setSelection(premiumSmsPermission - 1); 828 spinner.setOnItemSelectedListener(new PremiumSmsSelectionListener( 829 packageName, mSmsManager)); 830 } else { 831 // Hide the premium SMS permission selector 832 securityBillingDesc.setVisibility(View.GONE); 833 securityBillingList.setVisibility(View.GONE); 834 } 835 // App permissions subsection 836 if (asp.getPermissionCount() > 0) { 837 // Make the security sections header visible 838 LinearLayout securityList = (LinearLayout) permsView.findViewById( 839 R.id.security_settings_list); 840 securityList.removeAllViews(); 841 securityList.addView(asp.getPermissionsViewWithRevokeButtons()); 842 // If this app is running under a shared user ID with other apps, 843 // update the description to explain this. 844 String[] packages = mPm.getPackagesForUid(mPackageInfo.applicationInfo.uid); 845 if (packages != null && packages.length > 1) { 846 ArrayList<CharSequence> pnames = new ArrayList<CharSequence>(); 847 for (int i=0; i<packages.length; i++) { 848 String pkg = packages[i]; 849 if (mPackageInfo.packageName.equals(pkg)) { 850 continue; 851 } 852 try { 853 ApplicationInfo ainfo = mPm.getApplicationInfo(pkg, 0); 854 pnames.add(ainfo.loadLabel(mPm)); 855 } catch (PackageManager.NameNotFoundException e) { 856 } 857 } 858 final int N = pnames.size(); 859 if (N > 0) { 860 final Resources res = getActivity().getResources(); 861 String appListStr; 862 if (N == 1) { 863 appListStr = pnames.get(0).toString(); 864 } else if (N == 2) { 865 appListStr = res.getString(R.string.join_two_items, pnames.get(0), 866 pnames.get(1)); 867 } else { 868 appListStr = pnames.get(N-2).toString(); 869 for (int i=N-3; i>=0; i--) { 870 appListStr = res.getString(i == 0 ? R.string.join_many_items_first 871 : R.string.join_many_items_middle, pnames.get(i), appListStr); 872 } 873 appListStr = res.getString(R.string.join_many_items_last, 874 appListStr, pnames.get(N-1)); 875 } 876 TextView descr = (TextView) mRootView.findViewById( 877 R.id.security_settings_desc); 878 descr.setText(res.getString(R.string.security_settings_desc_multi, 879 mPackageInfo.applicationInfo.loadLabel(mPm), appListStr)); 880 } 881 } 882 } 883 884 checkForceStop(); 885 setAppLabelAndIcon(mPackageInfo); 886 refreshButtons(); 887 refreshSizeInfo(); 888 889 if (!mInitialized) { 890 // First time init: are we displaying an uninstalled app? 891 mInitialized = true; 892 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 893 } else { 894 // All other times: if the app no longer exists then we want 895 // to go away. 896 try { 897 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 898 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 899 | PackageManager.GET_DISABLED_COMPONENTS); 900 if (!mShowUninstalled) { 901 // If we did not start out with the app uninstalled, then 902 // it transitioning to the uninstalled state for the current 903 // user means we should go away as well. 904 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 905 } 906 } catch (NameNotFoundException e) { 907 return false; 908 } 909 } 910 911 return true; 912 } 913 914 private static class PremiumSmsSelectionListener implements AdapterView.OnItemSelectedListener { 915 private final String mPackageName; 916 private final ISms mSmsManager; 917 918 PremiumSmsSelectionListener(String packageName, ISms smsManager) { 919 mPackageName = packageName; 920 mSmsManager = smsManager; 921 } 922 923 @Override 924 public void onItemSelected(AdapterView<?> parent, View view, int position, 925 long id) { 926 if (position >= 0 && position < 3) { 927 Log.d(TAG, "Selected premium SMS policy " + position); 928 setPremiumSmsPermission(mPackageName, (position + 1)); 929 } else { 930 Log.e(TAG, "Error: unknown premium SMS policy " + position); 931 } 932 } 933 934 @Override 935 public void onNothingSelected(AdapterView<?> parent) { 936 // Ignored 937 } 938 939 private void setPremiumSmsPermission(String packageName, int permission) { 940 try { 941 if (mSmsManager != null) { 942 mSmsManager.setPremiumSmsPermission(packageName, permission); 943 } 944 } catch (RemoteException ex) { 945 // ignored 946 } 947 } 948 } 949 950 private void resetLaunchDefaultsUi(TextView title, TextView autoLaunchView) { 951 title.setText(R.string.auto_launch_label); 952 autoLaunchView.setText(R.string.auto_launch_disable_text); 953 // Disable clear activities button 954 mActivitiesButton.setEnabled(false); 955 } 956 957 private void setIntentAndFinish(boolean finish, boolean appChanged) { 958 if(localLOGV) Log.i(TAG, "appChanged="+appChanged); 959 Intent intent = new Intent(); 960 intent.putExtra(ManageApplications.APP_CHG, appChanged); 961 SettingsActivity sa = (SettingsActivity)getActivity(); 962 sa.finishPreferencePanel(this, Activity.RESULT_OK, intent); 963 } 964 965 private void refreshSizeInfo() { 966 if (mAppEntry.size == ApplicationsState.SIZE_INVALID 967 || mAppEntry.size == ApplicationsState.SIZE_UNKNOWN) { 968 mLastCodeSize = mLastDataSize = mLastCacheSize = mLastTotalSize = -1; 969 if (!mHaveSizes) { 970 mAppSize.setText(mComputingStr); 971 mDataSize.setText(mComputingStr); 972 mCacheSize.setText(mComputingStr); 973 mTotalSize.setText(mComputingStr); 974 } 975 mClearDataButton.setEnabled(false); 976 mClearCacheButton.setEnabled(false); 977 978 } else { 979 mHaveSizes = true; 980 long codeSize = mAppEntry.codeSize; 981 long dataSize = mAppEntry.dataSize; 982 if (Environment.isExternalStorageEmulated()) { 983 codeSize += mAppEntry.externalCodeSize; 984 dataSize += mAppEntry.externalDataSize; 985 } else { 986 if (mLastExternalCodeSize != mAppEntry.externalCodeSize) { 987 mLastExternalCodeSize = mAppEntry.externalCodeSize; 988 mExternalCodeSize.setText(getSizeStr(mAppEntry.externalCodeSize)); 989 } 990 if (mLastExternalDataSize != mAppEntry.externalDataSize) { 991 mLastExternalDataSize = mAppEntry.externalDataSize; 992 mExternalDataSize.setText(getSizeStr( mAppEntry.externalDataSize)); 993 } 994 } 995 if (mLastCodeSize != codeSize) { 996 mLastCodeSize = codeSize; 997 mAppSize.setText(getSizeStr(codeSize)); 998 } 999 if (mLastDataSize != dataSize) { 1000 mLastDataSize = dataSize; 1001 mDataSize.setText(getSizeStr(dataSize)); 1002 } 1003 long cacheSize = mAppEntry.cacheSize + mAppEntry.externalCacheSize; 1004 if (mLastCacheSize != cacheSize) { 1005 mLastCacheSize = cacheSize; 1006 mCacheSize.setText(getSizeStr(cacheSize)); 1007 } 1008 if (mLastTotalSize != mAppEntry.size) { 1009 mLastTotalSize = mAppEntry.size; 1010 mTotalSize.setText(getSizeStr(mAppEntry.size)); 1011 } 1012 1013 if ((mAppEntry.dataSize+ mAppEntry.externalDataSize) <= 0 || !mCanClearData) { 1014 mClearDataButton.setEnabled(false); 1015 } else { 1016 mClearDataButton.setEnabled(true); 1017 mClearDataButton.setOnClickListener(this); 1018 } 1019 if (cacheSize <= 0) { 1020 mClearCacheButton.setEnabled(false); 1021 } else { 1022 mClearCacheButton.setEnabled(true); 1023 mClearCacheButton.setOnClickListener(this); 1024 } 1025 } 1026 if (mAppControlRestricted) { 1027 mClearCacheButton.setEnabled(false); 1028 mClearDataButton.setEnabled(false); 1029 } 1030 } 1031 1032 /* 1033 * Private method to handle clear message notification from observer when 1034 * the async operation from PackageManager is complete 1035 */ 1036 private void processClearMsg(Message msg) { 1037 int result = msg.arg1; 1038 String packageName = mAppEntry.info.packageName; 1039 mClearDataButton.setText(R.string.clear_user_data_text); 1040 if(result == OP_SUCCESSFUL) { 1041 Log.i(TAG, "Cleared user data for package : "+packageName); 1042 mState.requestSize(mAppEntry.info.packageName); 1043 } else { 1044 mClearDataButton.setEnabled(true); 1045 } 1046 checkForceStop(); 1047 } 1048 1049 private void refreshButtons() { 1050 if (!mMoveInProgress) { 1051 initUninstallButtons(); 1052 initDataButtons(); 1053 initMoveButton(); 1054 initNotificationButton(); 1055 } else { 1056 mMoveAppButton.setText(R.string.moving); 1057 mMoveAppButton.setEnabled(false); 1058 mUninstallButton.setEnabled(false); 1059 mSpecialDisableButton.setEnabled(false); 1060 } 1061 } 1062 1063 private void processMoveMsg(Message msg) { 1064 int result = msg.arg1; 1065 String packageName = mAppEntry.info.packageName; 1066 // Refresh the button attributes. 1067 mMoveInProgress = false; 1068 if (result == PackageManager.MOVE_SUCCEEDED) { 1069 Log.i(TAG, "Moved resources for " + packageName); 1070 // Refresh size information again. 1071 mState.requestSize(mAppEntry.info.packageName); 1072 } else { 1073 showDialogInner(DLG_MOVE_FAILED, result); 1074 } 1075 refreshUi(); 1076 } 1077 1078 /* 1079 * Private method to initiate clearing user data when the user clicks the clear data 1080 * button for a system package 1081 */ 1082 private void initiateClearUserData() { 1083 mClearDataButton.setEnabled(false); 1084 // Invoke uninstall or clear user data based on sysPackage 1085 String packageName = mAppEntry.info.packageName; 1086 Log.i(TAG, "Clearing user data for package : " + packageName); 1087 if (mClearDataObserver == null) { 1088 mClearDataObserver = new ClearUserDataObserver(); 1089 } 1090 ActivityManager am = (ActivityManager) 1091 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 1092 boolean res = am.clearApplicationUserData(packageName, mClearDataObserver); 1093 if (!res) { 1094 // Clearing data failed for some obscure reason. Just log error for now 1095 Log.i(TAG, "Couldnt clear application user data for package:"+packageName); 1096 showDialogInner(DLG_CANNOT_CLEAR_DATA, 0); 1097 } else { 1098 mClearDataButton.setText(R.string.recompute_size); 1099 } 1100 } 1101 1102 private void showDialogInner(int id, int moveErrorCode) { 1103 DialogFragment newFragment = MyAlertDialogFragment.newInstance(id, moveErrorCode); 1104 newFragment.setTargetFragment(this, 0); 1105 newFragment.show(getFragmentManager(), "dialog " + id); 1106 } 1107 1108 public static class MyAlertDialogFragment extends DialogFragment { 1109 1110 public static MyAlertDialogFragment newInstance(int id, int moveErrorCode) { 1111 MyAlertDialogFragment frag = new MyAlertDialogFragment(); 1112 Bundle args = new Bundle(); 1113 args.putInt("id", id); 1114 args.putInt("moveError", moveErrorCode); 1115 frag.setArguments(args); 1116 return frag; 1117 } 1118 1119 InstalledAppDetails getOwner() { 1120 return (InstalledAppDetails)getTargetFragment(); 1121 } 1122 1123 @Override 1124 public Dialog onCreateDialog(Bundle savedInstanceState) { 1125 int id = getArguments().getInt("id"); 1126 int moveErrorCode = getArguments().getInt("moveError"); 1127 switch (id) { 1128 case DLG_CLEAR_DATA: 1129 return new AlertDialog.Builder(getActivity()) 1130 .setTitle(getActivity().getText(R.string.clear_data_dlg_title)) 1131 .setMessage(getActivity().getText(R.string.clear_data_dlg_text)) 1132 .setPositiveButton(R.string.dlg_ok, 1133 new DialogInterface.OnClickListener() { 1134 public void onClick(DialogInterface dialog, int which) { 1135 // Clear user data here 1136 getOwner().initiateClearUserData(); 1137 } 1138 }) 1139 .setNegativeButton(R.string.dlg_cancel, null) 1140 .create(); 1141 case DLG_FACTORY_RESET: 1142 return new AlertDialog.Builder(getActivity()) 1143 .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) 1144 .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) 1145 .setPositiveButton(R.string.dlg_ok, 1146 new DialogInterface.OnClickListener() { 1147 public void onClick(DialogInterface dialog, int which) { 1148 // Clear user data here 1149 getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, 1150 false, false); 1151 } 1152 }) 1153 .setNegativeButton(R.string.dlg_cancel, null) 1154 .create(); 1155 case DLG_APP_NOT_FOUND: 1156 return new AlertDialog.Builder(getActivity()) 1157 .setTitle(getActivity().getText(R.string.app_not_found_dlg_title)) 1158 .setMessage(getActivity().getText(R.string.app_not_found_dlg_title)) 1159 .setNeutralButton(getActivity().getText(R.string.dlg_ok), 1160 new DialogInterface.OnClickListener() { 1161 public void onClick(DialogInterface dialog, int which) { 1162 //force to recompute changed value 1163 getOwner().setIntentAndFinish(true, true); 1164 } 1165 }) 1166 .create(); 1167 case DLG_CANNOT_CLEAR_DATA: 1168 return new AlertDialog.Builder(getActivity()) 1169 .setTitle(getActivity().getText(R.string.clear_failed_dlg_title)) 1170 .setMessage(getActivity().getText(R.string.clear_failed_dlg_text)) 1171 .setNeutralButton(R.string.dlg_ok, 1172 new DialogInterface.OnClickListener() { 1173 public void onClick(DialogInterface dialog, int which) { 1174 getOwner().mClearDataButton.setEnabled(false); 1175 //force to recompute changed value 1176 getOwner().setIntentAndFinish(false, false); 1177 } 1178 }) 1179 .create(); 1180 case DLG_FORCE_STOP: 1181 return new AlertDialog.Builder(getActivity()) 1182 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 1183 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 1184 .setPositiveButton(R.string.dlg_ok, 1185 new DialogInterface.OnClickListener() { 1186 public void onClick(DialogInterface dialog, int which) { 1187 // Force stop 1188 getOwner().forceStopPackage(getOwner().mAppEntry.info.packageName); 1189 } 1190 }) 1191 .setNegativeButton(R.string.dlg_cancel, null) 1192 .create(); 1193 case DLG_MOVE_FAILED: 1194 CharSequence msg = getActivity().getString(R.string.move_app_failed_dlg_text, 1195 getOwner().getMoveErrMsg(moveErrorCode)); 1196 return new AlertDialog.Builder(getActivity()) 1197 .setTitle(getActivity().getText(R.string.move_app_failed_dlg_title)) 1198 .setMessage(msg) 1199 .setNeutralButton(R.string.dlg_ok, null) 1200 .create(); 1201 case DLG_DISABLE: 1202 return new AlertDialog.Builder(getActivity()) 1203 .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) 1204 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 1205 .setPositiveButton(R.string.dlg_ok, 1206 new DialogInterface.OnClickListener() { 1207 public void onClick(DialogInterface dialog, int which) { 1208 // Disable the app 1209 new DisableChanger(getOwner(), getOwner().mAppEntry.info, 1210 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 1211 .execute((Object)null); 1212 } 1213 }) 1214 .setNegativeButton(R.string.dlg_cancel, null) 1215 .create(); 1216 case DLG_DISABLE_NOTIFICATIONS: 1217 return new AlertDialog.Builder(getActivity()) 1218 .setTitle(getActivity().getText(R.string.app_disable_notifications_dlg_title)) 1219 .setMessage(getActivity().getText(R.string.app_disable_notifications_dlg_text)) 1220 .setPositiveButton(R.string.dlg_ok, 1221 new DialogInterface.OnClickListener() { 1222 public void onClick(DialogInterface dialog, int which) { 1223 // Disable the package's notifications 1224 getOwner().setNotificationsEnabled(false); 1225 } 1226 }) 1227 .setNegativeButton(R.string.dlg_cancel, 1228 new DialogInterface.OnClickListener() { 1229 public void onClick(DialogInterface dialog, int which) { 1230 // Re-enable the checkbox 1231 getOwner().mNotificationSwitch.setChecked(true); 1232 } 1233 }) 1234 .create(); 1235 case DLG_SPECIAL_DISABLE: 1236 return new AlertDialog.Builder(getActivity()) 1237 .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) 1238 .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) 1239 .setPositiveButton(R.string.dlg_ok, 1240 new DialogInterface.OnClickListener() { 1241 public void onClick(DialogInterface dialog, int which) { 1242 // Clear user data here 1243 getOwner().uninstallPkg(getOwner().mAppEntry.info.packageName, 1244 false, true); 1245 } 1246 }) 1247 .setNegativeButton(R.string.dlg_cancel, null) 1248 .create(); 1249 } 1250 throw new IllegalArgumentException("unknown id " + id); 1251 } 1252 } 1253 1254 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 1255 // Create new intent to launch Uninstaller activity 1256 Uri packageURI = Uri.parse("package:"+packageName); 1257 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 1258 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 1259 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 1260 mDisableAfterUninstall = andDisable; 1261 } 1262 1263 private void forceStopPackage(String pkgName) { 1264 ActivityManager am = (ActivityManager)getActivity().getSystemService( 1265 Context.ACTIVITY_SERVICE); 1266 am.forceStopPackage(pkgName); 1267 mState.invalidatePackage(pkgName); 1268 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName); 1269 if (newEnt != null) { 1270 mAppEntry = newEnt; 1271 } 1272 checkForceStop(); 1273 } 1274 1275 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 1276 @Override 1277 public void onReceive(Context context, Intent intent) { 1278 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 1279 } 1280 }; 1281 1282 private void updateForceStopButton(boolean enabled) { 1283 if (mAppControlRestricted) { 1284 mForceStopButton.setEnabled(false); 1285 } else { 1286 mForceStopButton.setEnabled(enabled); 1287 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 1288 } 1289 } 1290 1291 private void checkForceStop() { 1292 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 1293 // User can't force stop device admin. 1294 updateForceStopButton(false); 1295 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 1296 // If the app isn't explicitly stopped, then always show the 1297 // force stop button. 1298 updateForceStopButton(true); 1299 } else { 1300 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 1301 Uri.fromParts("package", mAppEntry.info.packageName, null)); 1302 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 1303 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 1304 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 1305 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 1306 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 1307 } 1308 } 1309 1310 static class DisableChanger extends AsyncTask<Object, Object, Object> { 1311 final PackageManager mPm; 1312 final WeakReference<InstalledAppDetails> mActivity; 1313 final ApplicationInfo mInfo; 1314 final int mState; 1315 1316 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 1317 mPm = activity.mPm; 1318 mActivity = new WeakReference<InstalledAppDetails>(activity); 1319 mInfo = info; 1320 mState = state; 1321 } 1322 1323 @Override 1324 protected Object doInBackground(Object... params) { 1325 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 1326 return null; 1327 } 1328 } 1329 1330 private void setNotificationsEnabled(boolean enabled) { 1331 String packageName = mAppEntry.info.packageName; 1332 INotificationManager nm = INotificationManager.Stub.asInterface( 1333 ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 1334 try { 1335 final boolean enable = mNotificationSwitch.isChecked(); 1336 nm.setNotificationsEnabledForPackage(packageName, mAppEntry.info.uid, enabled); 1337 } catch (android.os.RemoteException ex) { 1338 mNotificationSwitch.setChecked(!enabled); // revert 1339 } 1340 } 1341 1342 private int getPremiumSmsPermission(String packageName) { 1343 try { 1344 if (mSmsManager != null) { 1345 return mSmsManager.getPremiumSmsPermission(packageName); 1346 } 1347 } catch (RemoteException ex) { 1348 // ignored 1349 } 1350 return SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN; 1351 } 1352 1353 /* 1354 * Method implementing functionality of buttons clicked 1355 * @see android.view.View.OnClickListener#onClick(android.view.View) 1356 */ 1357 public void onClick(View v) { 1358 String packageName = mAppEntry.info.packageName; 1359 if(v == mUninstallButton) { 1360 if (mUpdatedSysApp) { 1361 showDialogInner(DLG_FACTORY_RESET, 0); 1362 } else { 1363 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 1364 if (mAppEntry.info.enabled) { 1365 showDialogInner(DLG_DISABLE, 0); 1366 } else { 1367 new DisableChanger(this, mAppEntry.info, 1368 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 1369 .execute((Object)null); 1370 } 1371 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 1372 uninstallPkg(packageName, true, false); 1373 } else { 1374 uninstallPkg(packageName, false, false); 1375 } 1376 } 1377 } else if(v == mSpecialDisableButton) { 1378 showDialogInner(DLG_SPECIAL_DISABLE, 0); 1379 } else if(v == mActivitiesButton) { 1380 mPm.clearPackagePreferredActivities(packageName); 1381 try { 1382 mUsbManager.clearDefaults(packageName, UserHandle.myUserId()); 1383 } catch (RemoteException e) { 1384 Log.e(TAG, "mUsbManager.clearDefaults", e); 1385 } 1386 mAppWidgetManager.setBindAppWidgetPermission(packageName, false); 1387 TextView autoLaunchTitleView = 1388 (TextView) mRootView.findViewById(R.id.auto_launch_title); 1389 TextView autoLaunchView = (TextView) mRootView.findViewById(R.id.auto_launch); 1390 resetLaunchDefaultsUi(autoLaunchTitleView, autoLaunchView); 1391 } else if(v == mClearDataButton) { 1392 if (mAppEntry.info.manageSpaceActivityName != null) { 1393 if (!Utils.isMonkeyRunning()) { 1394 Intent intent = new Intent(Intent.ACTION_DEFAULT); 1395 intent.setClassName(mAppEntry.info.packageName, 1396 mAppEntry.info.manageSpaceActivityName); 1397 startActivityForResult(intent, REQUEST_MANAGE_SPACE); 1398 } 1399 } else { 1400 showDialogInner(DLG_CLEAR_DATA, 0); 1401 } 1402 } else if (v == mClearCacheButton) { 1403 // Lazy initialization of observer 1404 if (mClearCacheObserver == null) { 1405 mClearCacheObserver = new ClearCacheObserver(); 1406 } 1407 mPm.deleteApplicationCacheFiles(packageName, mClearCacheObserver); 1408 } else if (v == mForceStopButton) { 1409 showDialogInner(DLG_FORCE_STOP, 0); 1410 //forceStopPackage(mAppInfo.packageName); 1411 } else if (v == mMoveAppButton) { 1412 if (mPackageMoveObserver == null) { 1413 mPackageMoveObserver = new PackageMoveObserver(); 1414 } 1415 int moveFlags = (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? 1416 PackageManager.MOVE_INTERNAL : PackageManager.MOVE_EXTERNAL_MEDIA; 1417 mMoveInProgress = true; 1418 refreshButtons(); 1419 mPm.movePackage(mAppEntry.info.packageName, mPackageMoveObserver, moveFlags); 1420 } 1421 } 1422 1423 @Override 1424 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1425 String packageName = mAppEntry.info.packageName; 1426 ActivityManager am = (ActivityManager) 1427 getActivity().getSystemService(Context.ACTIVITY_SERVICE); 1428 if (buttonView == mAskCompatibilityCB) { 1429 am.setPackageAskScreenCompat(packageName, isChecked); 1430 } else if (buttonView == mEnableCompatibilityCB) { 1431 am.setPackageScreenCompatMode(packageName, isChecked ? 1432 ActivityManager.COMPAT_MODE_ENABLED : ActivityManager.COMPAT_MODE_DISABLED); 1433 } else if (buttonView == mNotificationSwitch) { 1434 if (!isChecked) { 1435 showDialogInner(DLG_DISABLE_NOTIFICATIONS, 0); 1436 } else { 1437 setNotificationsEnabled(true); 1438 } 1439 } 1440 } 1441 } 1442 1443