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