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