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