InstalledAppDetails.java revision 8f600a4fbcddda07e724815c39ba7fa6f9194e1c
1/** 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17package com.android.settings.applications; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.AlertDialog; 22import android.app.LoaderManager.LoaderCallbacks; 23import android.content.ActivityNotFoundException; 24import android.content.BroadcastReceiver; 25import android.content.ComponentName; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.Loader; 30import android.content.pm.ApplicationInfo; 31import android.content.pm.PackageInfo; 32import android.content.pm.PackageManager; 33import android.content.pm.PackageManager.NameNotFoundException; 34import android.content.pm.ResolveInfo; 35import android.net.INetworkStatsService; 36import android.net.INetworkStatsSession; 37import android.net.NetworkTemplate; 38import android.net.TrafficStats; 39import android.net.Uri; 40import android.os.AsyncTask; 41import android.os.BatteryStats; 42import android.os.Bundle; 43import android.os.RemoteException; 44import android.os.ServiceManager; 45import android.os.UserHandle; 46import android.preference.Preference; 47import android.preference.Preference.OnPreferenceClickListener; 48import android.provider.Settings; 49import android.text.format.DateUtils; 50import android.text.format.Formatter; 51import android.util.Log; 52import android.view.Menu; 53import android.view.MenuInflater; 54import android.view.MenuItem; 55import android.view.View; 56import android.widget.Button; 57import android.widget.ImageView; 58import android.widget.TextView; 59 60import com.android.internal.logging.MetricsLogger; 61import com.android.internal.os.BatterySipper; 62import com.android.internal.os.BatteryStatsHelper; 63import com.android.settings.DataUsageSummary; 64import com.android.settings.DataUsageSummary.AppItem; 65import com.android.settings.R; 66import com.android.settings.SettingsActivity; 67import com.android.settings.Utils; 68import com.android.settings.applications.ApplicationsState.AppEntry; 69import com.android.settings.fuelgauge.BatteryEntry; 70import com.android.settings.fuelgauge.PowerUsageDetail; 71import com.android.settings.net.ChartData; 72import com.android.settings.net.ChartDataLoader; 73import com.android.settings.notification.NotificationBackend; 74import com.android.settings.notification.NotificationBackend.AppRow; 75 76import java.lang.ref.WeakReference; 77import java.util.ArrayList; 78import java.util.HashSet; 79import java.util.List; 80 81/** 82 * Activity to display application information from Settings. This activity presents 83 * extended information associated with a package like code, data, total size, permissions 84 * used by the application and also the set of default launchable activities. 85 * For system applications, an option to clear user data is displayed only if data size is > 0. 86 * System applications that do not want clear user data do not have this option. 87 * For non-system applications, there is no option to clear data. Instead there is an option to 88 * uninstall the application. 89 */ 90public class InstalledAppDetails extends AppInfoBase 91 implements View.OnClickListener, OnPreferenceClickListener { 92 93 private static final String LOG_TAG = "InstalledAppDetails"; 94 95 // Menu identifiers 96 public static final int UNINSTALL_ALL_USERS_MENU = 1; 97 public static final int UNINSTALL_UPDATES = 2; 98 99 // Result code identifiers 100 public static final int REQUEST_UNINSTALL = 0; 101 private static final int SUB_INFO_FRAGMENT = 1; 102 103 private static final int LOADER_CHART_DATA = 2; 104 105 private static final int DLG_FORCE_STOP = DLG_BASE + 1; 106 private static final int DLG_DISABLE = DLG_BASE + 2; 107 private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; 108 private static final int DLG_FACTORY_RESET = DLG_BASE + 4; 109 110 private static final String KEY_HEADER = "header_view"; 111 private static final String KEY_NOTIFICATION = "notification_settings"; 112 private static final String KEY_STORAGE = "storage_settings"; 113 private static final String KEY_PERMISSION = "permission_settings"; 114 private static final String KEY_DATA = "data_settings"; 115 private static final String KEY_LAUNCH = "preferred_settings"; 116 private static final String KEY_BATTERY = "battery"; 117 118 private final HashSet<String> mHomePackages = new HashSet<String>(); 119 120 private boolean mInitialized; 121 private boolean mShowUninstalled; 122 private LayoutPreference mHeader; 123 private Button mUninstallButton; 124 private boolean mUpdatedSysApp = false; 125 private TextView mAppVersion; 126 private Button mForceStopButton; 127 private Preference mNotificationPreference; 128 private Preference mStoragePreference; 129 private Preference mPermissionsPreference; 130 private Preference mLaunchPreference; 131 private Preference mDataPreference; 132 133 private boolean mDisableAfterUninstall; 134 // Used for updating notification preference. 135 private final NotificationBackend mBackend = new NotificationBackend(); 136 137 private ChartData mChartData; 138 private INetworkStatsSession mStatsSession; 139 140 private Preference mBatteryPreference; 141 142 private BatteryStatsHelper mBatteryHelper; 143 private BatterySipper mSipper; 144 145 private boolean handleDisableable(Button button) { 146 boolean disableable = false; 147 // Try to prevent the user from bricking their phone 148 // by not allowing disabling of apps signed with the 149 // system cert and any launcher app in the system. 150 if (mHomePackages.contains(mAppEntry.info.packageName) 151 || Utils.isSystemPackage(mPm, mPackageInfo)) { 152 // Disable button for core system applications. 153 button.setText(R.string.disable_text); 154 } else if (mAppEntry.info.enabled) { 155 button.setText(R.string.disable_text); 156 disableable = true; 157 } else { 158 button.setText(R.string.enable_text); 159 disableable = true; 160 } 161 162 return disableable; 163 } 164 165 private void initUninstallButtons() { 166 final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 167 boolean enabled = true; 168 if (isBundled) { 169 enabled = handleDisableable(mUninstallButton); 170 } else { 171 if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 172 && mUserManager.getUsers().size() >= 2) { 173 // When we have multiple users, there is a separate menu 174 // to uninstall for all users. 175 enabled = false; 176 } 177 mUninstallButton.setText(R.string.uninstall_text); 178 } 179 // If this is a device admin, it can't be uninstalled or disabled. 180 // We do this here so the text of the button is still set correctly. 181 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 182 enabled = false; 183 } 184 185 // Home apps need special handling. Bundled ones we don't risk downgrading 186 // because that can interfere with home-key resolution. Furthermore, we 187 // can't allow uninstallation of the only home app, and we don't want to 188 // allow uninstallation of an explicitly preferred one -- the user can go 189 // to Home settings and pick a different one, after which we'll permit 190 // uninstallation of the now-not-default one. 191 if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { 192 if (isBundled) { 193 enabled = false; 194 } else { 195 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 196 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 197 if (currentDefaultHome == null) { 198 // No preferred default, so permit uninstall only when 199 // there is more than one candidate 200 enabled = (mHomePackages.size() > 1); 201 } else { 202 // There is an explicit default home app -- forbid uninstall of 203 // that one, but permit it for installed-but-inactive ones. 204 enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); 205 } 206 } 207 } 208 209 if (mAppControlRestricted) { 210 enabled = false; 211 } 212 213 mUninstallButton.setEnabled(enabled); 214 if (enabled) { 215 // Register listener 216 mUninstallButton.setOnClickListener(this); 217 } 218 } 219 220 /** Called when the activity is first created. */ 221 @Override 222 public void onCreate(Bundle icicle) { 223 super.onCreate(icicle); 224 225 setHasOptionsMenu(true); 226 addPreferencesFromResource(R.xml.installed_app_details); 227 228 INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( 229 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 230 try { 231 mStatsSession = statsService.openSession(); 232 } catch (RemoteException e) { 233 throw new RuntimeException(e); 234 } 235 mBatteryHelper = new BatteryStatsHelper(getActivity(), true); 236 } 237 238 @Override 239 protected int getMetricsCategory() { 240 return MetricsLogger.APPLICATIONS_INSTALLED_APP_DETAILS; 241 } 242 243 @Override 244 public void onResume() { 245 super.onResume(); 246 AppItem app = new AppItem(mAppEntry.info.uid); 247 app.addUid(mAppEntry.info.uid); 248 getLoaderManager().restartLoader(LOADER_CHART_DATA, 249 ChartDataLoader.buildArgs(NetworkTemplate.buildTemplateMobileWildcard(), app), 250 mDataCallbacks); 251 new BatteryUpdater().execute(); 252 } 253 254 @Override 255 public void onPause() { 256 getLoaderManager().destroyLoader(LOADER_CHART_DATA); 257 super.onPause(); 258 } 259 260 @Override 261 public void onDestroy() { 262 TrafficStats.closeQuietly(mStatsSession); 263 264 super.onDestroy(); 265 } 266 267 public void onActivityCreated(Bundle savedInstanceState) { 268 super.onActivityCreated(savedInstanceState); 269 handleHeader(); 270 271 mNotificationPreference = findPreference(KEY_NOTIFICATION); 272 mNotificationPreference.setOnPreferenceClickListener(this); 273 mStoragePreference = findPreference(KEY_STORAGE); 274 mStoragePreference.setOnPreferenceClickListener(this); 275 mPermissionsPreference = findPreference(KEY_PERMISSION); 276 mPermissionsPreference.setOnPreferenceClickListener(this); 277 mDataPreference = findPreference(KEY_DATA); 278 mDataPreference.setOnPreferenceClickListener(this); 279 mBatteryPreference = findPreference(KEY_BATTERY); 280 mBatteryPreference.setEnabled(false); 281 mBatteryPreference.setOnPreferenceClickListener(this); 282 283 mLaunchPreference = findPreference(KEY_LAUNCH); 284 if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 285 mLaunchPreference.setEnabled(false); 286 } else if (!mAppEntry.info.enabled) { 287 mLaunchPreference.setEnabled(false); 288 } else { 289 mLaunchPreference.setOnPreferenceClickListener(this); 290 } 291 } 292 293 private void handleHeader() { 294 mHeader = (LayoutPreference) findPreference(KEY_HEADER); 295 296 // Get Control button panel 297 View btnPanel = mHeader.findViewById(R.id.control_buttons_panel); 298 mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button); 299 mForceStopButton.setText(R.string.force_stop); 300 mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button); 301 mForceStopButton.setEnabled(false); 302 } 303 304 @Override 305 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 306 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 307 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 308 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 309 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 310 } 311 312 @Override 313 public void onPrepareOptionsMenu(Menu menu) { 314 boolean showIt = true; 315 if (mUpdatedSysApp) { 316 showIt = false; 317 } else if (mAppEntry == null) { 318 showIt = false; 319 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 320 showIt = false; 321 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 322 showIt = false; 323 } else if (UserHandle.myUserId() != 0) { 324 showIt = false; 325 } else if (mUserManager.getUsers().size() < 2) { 326 showIt = false; 327 } 328 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 329 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 330 menu.findItem(UNINSTALL_UPDATES).setVisible(mUpdatedSysApp && !mAppControlRestricted); 331 } 332 333 @Override 334 public boolean onOptionsItemSelected(MenuItem item) { 335 switch (item.getItemId()) { 336 case UNINSTALL_ALL_USERS_MENU: 337 uninstallPkg(mAppEntry.info.packageName, true, false); 338 return true; 339 case UNINSTALL_UPDATES: 340 showDialogInner(DLG_FACTORY_RESET, 0); 341 return true; 342 } 343 return false; 344 } 345 346 @Override 347 public void onActivityResult(int requestCode, int resultCode, Intent data) { 348 super.onActivityResult(requestCode, resultCode, data); 349 if (requestCode == REQUEST_UNINSTALL) { 350 if (mDisableAfterUninstall) { 351 mDisableAfterUninstall = false; 352 try { 353 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 354 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 355 | PackageManager.GET_DISABLED_COMPONENTS); 356 if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 357 new DisableChanger(this, mAppEntry.info, 358 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 359 .execute((Object)null); 360 } 361 } catch (NameNotFoundException e) { 362 } 363 } 364 if (!refreshUi()) { 365 setIntentAndFinish(true, true); 366 } 367 } 368 } 369 370 // Utility method to set application label and icon. 371 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 372 final View appSnippet = mHeader.findViewById(R.id.app_snippet); 373 appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, 374 appSnippet.getPaddingBottom()); 375 376 ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon); 377 mState.ensureIcon(mAppEntry); 378 icon.setImageDrawable(mAppEntry.icon); 379 // Set application name. 380 TextView label = (TextView) appSnippet.findViewById(R.id.app_name); 381 label.setText(mAppEntry.label); 382 // Version number of application 383 mAppVersion = (TextView) appSnippet.findViewById(R.id.app_summary); 384 385 if (pkgInfo != null && pkgInfo.versionName != null) { 386 mAppVersion.setVisibility(View.VISIBLE); 387 mAppVersion.setText(getActivity().getString(R.string.version_text, 388 String.valueOf(pkgInfo.versionName))); 389 } else { 390 mAppVersion.setVisibility(View.INVISIBLE); 391 } 392 } 393 394 private boolean signaturesMatch(String pkg1, String pkg2) { 395 if (pkg1 != null && pkg2 != null) { 396 try { 397 final int match = mPm.checkSignatures(pkg1, pkg2); 398 if (match >= PackageManager.SIGNATURE_MATCH) { 399 return true; 400 } 401 } catch (Exception e) { 402 // e.g. named alternate package not found during lookup; 403 // this is an expected case sometimes 404 } 405 } 406 return false; 407 } 408 409 @Override 410 protected boolean refreshUi() { 411 retrieveAppEntry(); 412 if (mAppEntry == null) { 413 return false; // onCreate must have failed, make sure to exit 414 } 415 416 if (mPackageInfo == null) { 417 return false; // onCreate must have failed, make sure to exit 418 } 419 420 // Get list of "home" apps and trace through any meta-data references 421 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 422 mPm.getHomeActivities(homeActivities); 423 mHomePackages.clear(); 424 for (int i = 0; i< homeActivities.size(); i++) { 425 ResolveInfo ri = homeActivities.get(i); 426 final String activityPkg = ri.activityInfo.packageName; 427 mHomePackages.add(activityPkg); 428 429 // Also make sure to include anything proxying for the home app 430 final Bundle metadata = ri.activityInfo.metaData; 431 if (metadata != null) { 432 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 433 if (signaturesMatch(metaPkg, activityPkg)) { 434 mHomePackages.add(metaPkg); 435 } 436 } 437 } 438 439 checkForceStop(); 440 setAppLabelAndIcon(mPackageInfo); 441 initUninstallButtons(); 442 443 // Update the preference summaries. 444 Activity context = getActivity(); 445 mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); 446 mPermissionsPreference.setSummary(AppPermissionSettings.getSummary(mAppEntry, context)); 447 mLaunchPreference.setSummary(Utils.getLaunchByDeafaultSummary(mAppEntry, mUsbManager, 448 mPm, context)); 449 mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, 450 mBackend)); 451 mDataPreference.setSummary(getDataSummary()); 452 453 updateBattery(); 454 455 if (!mInitialized) { 456 // First time init: are we displaying an uninstalled app? 457 mInitialized = true; 458 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 459 } else { 460 // All other times: if the app no longer exists then we want 461 // to go away. 462 try { 463 ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( 464 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 465 | PackageManager.GET_DISABLED_COMPONENTS); 466 if (!mShowUninstalled) { 467 // If we did not start out with the app uninstalled, then 468 // it transitioning to the uninstalled state for the current 469 // user means we should go away as well. 470 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 471 } 472 } catch (NameNotFoundException e) { 473 return false; 474 } 475 } 476 477 return true; 478 } 479 480 private void updateBattery() { 481 if (mSipper != null) { 482 mBatteryPreference.setEnabled(true); 483 int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( 484 BatteryStats.STATS_SINCE_CHARGED); 485 final int percentOfMax = (int) ((mSipper.totalPowerMah) 486 / mBatteryHelper.getTotalPower() * dischargeAmount + .5f); 487 mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax)); 488 } else { 489 mBatteryPreference.setEnabled(false); 490 mBatteryPreference.setSummary(getString(R.string.no_battery_summary)); 491 } 492 } 493 494 private CharSequence getDataSummary() { 495 if (mChartData != null) { 496 long totalBytes = mChartData.detail.getTotalBytes(); 497 Context context = getActivity(); 498 return getString(R.string.data_summary_format, 499 Formatter.formatFileSize(context, totalBytes), 500 DateUtils.formatDateTime(context, mChartData.detail.getStart(), 501 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH)); 502 } 503 return getString(R.string.computing_size); 504 } 505 506 @Override 507 protected AlertDialog createDialog(int id, int errorCode) { 508 switch (id) { 509 case DLG_DISABLE: 510 return new AlertDialog.Builder(getActivity()) 511 .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) 512 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 513 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 514 public void onClick(DialogInterface dialog, int which) { 515 // Disable the app 516 new DisableChanger(InstalledAppDetails.this, mAppEntry.info, 517 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 518 .execute((Object)null); 519 } 520 }) 521 .setNegativeButton(R.string.dlg_cancel, null) 522 .create(); 523 case DLG_SPECIAL_DISABLE: 524 return new AlertDialog.Builder(getActivity()) 525 .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) 526 .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) 527 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 528 public void onClick(DialogInterface dialog, int which) { 529 // Clear user data here 530 uninstallPkg(mAppEntry.info.packageName, 531 false, true); 532 } 533 }) 534 .setNegativeButton(R.string.dlg_cancel, null) 535 .create(); 536 case DLG_FORCE_STOP: 537 return new AlertDialog.Builder(getActivity()) 538 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 539 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 540 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 541 public void onClick(DialogInterface dialog, int which) { 542 // Force stop 543 forceStopPackage(mAppEntry.info.packageName); 544 } 545 }) 546 .setNegativeButton(R.string.dlg_cancel, null) 547 .create(); 548 case DLG_FACTORY_RESET: 549 return new AlertDialog.Builder(getActivity()) 550 .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) 551 .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) 552 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 553 public void onClick(DialogInterface dialog, int which) { 554 // Clear user data here 555 uninstallPkg(mAppEntry.info.packageName, 556 false, false); 557 } 558 }) 559 .setNegativeButton(R.string.dlg_cancel, null) 560 .create(); 561 } 562 return null; 563 } 564 565 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 566 // Create new intent to launch Uninstaller activity 567 Uri packageURI = Uri.parse("package:"+packageName); 568 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 569 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 570 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 571 mDisableAfterUninstall = andDisable; 572 } 573 574 private void forceStopPackage(String pkgName) { 575 ActivityManager am = (ActivityManager)getActivity().getSystemService( 576 Context.ACTIVITY_SERVICE); 577 am.forceStopPackage(pkgName); 578 int userId = UserHandle.getUserId(mAppEntry.info.uid); 579 mState.invalidatePackage(pkgName, userId); 580 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId); 581 if (newEnt != null) { 582 mAppEntry = newEnt; 583 } 584 checkForceStop(); 585 } 586 587 private void updateForceStopButton(boolean enabled) { 588 if (mAppControlRestricted) { 589 mForceStopButton.setEnabled(false); 590 } else { 591 mForceStopButton.setEnabled(enabled); 592 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 593 } 594 } 595 596 private void checkForceStop() { 597 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 598 // User can't force stop device admin. 599 updateForceStopButton(false); 600 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 601 // If the app isn't explicitly stopped, then always show the 602 // force stop button. 603 updateForceStopButton(true); 604 } else { 605 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 606 Uri.fromParts("package", mAppEntry.info.packageName, null)); 607 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 608 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 609 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 610 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 611 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 612 } 613 } 614 615 private void startManagePermissionsActivity() { 616 // start new activity to manage app permissions 617 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 618 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName); 619 intent.putExtra(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true); 620 try { 621 startActivity(intent); 622 } catch (ActivityNotFoundException e) { 623 Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS"); 624 } 625 } 626 627 private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, CharSequence title) { 628 // start new fragment to display extended information 629 Bundle args = new Bundle(); 630 args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mAppEntry.info.packageName); 631 args.putBoolean(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true); 632 633 SettingsActivity sa = (SettingsActivity) getActivity(); 634 sa.startPreferencePanel(fragment.getName(), args, -1, title, this, SUB_INFO_FRAGMENT); 635 } 636 637 private void startNotifications() { 638 // start new fragment to display extended information 639 getActivity().startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) 640 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 641 .putExtra(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true) 642 .putExtra(Settings.EXTRA_APP_PACKAGE, mAppEntry.info.packageName) 643 .putExtra(Settings.EXTRA_APP_UID, mAppEntry.info.uid)); 644 } 645 646 /* 647 * Method implementing functionality of buttons clicked 648 * @see android.view.View.OnClickListener#onClick(android.view.View) 649 */ 650 public void onClick(View v) { 651 String packageName = mAppEntry.info.packageName; 652 if(v == mUninstallButton) { 653 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 654 if (mAppEntry.info.enabled) { 655 if (mUpdatedSysApp) { 656 showDialogInner(DLG_SPECIAL_DISABLE, 0); 657 } else { 658 showDialogInner(DLG_DISABLE, 0); 659 } 660 } else { 661 new DisableChanger(this, mAppEntry.info, 662 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 663 .execute((Object) null); 664 } 665 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 666 uninstallPkg(packageName, true, false); 667 } else { 668 uninstallPkg(packageName, false, false); 669 } 670 } else if (v == mForceStopButton) { 671 showDialogInner(DLG_FORCE_STOP, 0); 672 //forceStopPackage(mAppInfo.packageName); 673 } 674 } 675 676 @Override 677 public boolean onPreferenceClick(Preference preference) { 678 if (preference == mStoragePreference) { 679 startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); 680 } else if (preference == mNotificationPreference) { 681 startNotifications(); 682 } else if (preference == mPermissionsPreference) { 683 startManagePermissionsActivity(); 684 } else if (preference == mLaunchPreference) { 685 startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); 686 } else if (preference == mDataPreference) { 687 Bundle args = new Bundle(); 688 args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG, 689 mAppEntry.info.packageName); 690 691 SettingsActivity sa = (SettingsActivity) getActivity(); 692 sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1, 693 getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT); 694 } else if (preference == mBatteryPreference) { 695 BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); 696 PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), 697 mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true); 698 } else { 699 return false; 700 } 701 return true; 702 } 703 704 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) { 705 return getNotificationSummary(appEntry, context, new NotificationBackend()); 706 } 707 708 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context, 709 NotificationBackend backend) { 710 AppRow appRow = backend.loadAppRow(context.getPackageManager(), appEntry.info); 711 return getNotificationSummary(appRow, context); 712 } 713 714 public static CharSequence getNotificationSummary(AppRow appRow, Context context) { 715 if (appRow.banned) { 716 return context.getString(R.string.notifications_disabled); 717 } 718 ArrayList<CharSequence> notifSummary = new ArrayList<>(); 719 if (appRow.priority) { 720 notifSummary.add(context.getString(R.string.notifications_priority)); 721 } 722 if (appRow.sensitive) { 723 notifSummary.add(context.getString(R.string.notifications_sensitive)); 724 } 725 if (!appRow.peekable) { 726 notifSummary.add(context.getString(R.string.notifications_no_peeking)); 727 } 728 switch (notifSummary.size()) { 729 case 3: 730 return context.getString(R.string.notifications_three_items, 731 notifSummary.get(0), notifSummary.get(1), notifSummary.get(2)); 732 case 2: 733 return context.getString(R.string.notifications_two_items, 734 notifSummary.get(0), notifSummary.get(1)); 735 case 1: 736 return notifSummary.get(0); 737 default: 738 return context.getString(R.string.notifications_enabled); 739 } 740 } 741 742 private class BatteryUpdater extends AsyncTask<Void, Void, Void> { 743 @Override 744 protected Void doInBackground(Void... params) { 745 mBatteryHelper.create((Bundle) null); 746 mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, 747 mUserManager.getUserProfiles()); 748 List<BatterySipper> usageList = mBatteryHelper.getUsageList(); 749 final int N = usageList.size(); 750 for (int i = 0; i < N; i++) { 751 BatterySipper sipper = usageList.get(i); 752 if (sipper.getUid() == mPackageInfo.applicationInfo.uid) { 753 mSipper = sipper; 754 break; 755 } 756 } 757 return null; 758 } 759 760 @Override 761 protected void onPostExecute(Void result) { 762 refreshUi(); 763 } 764 } 765 766 private static class DisableChanger extends AsyncTask<Object, Object, Object> { 767 final PackageManager mPm; 768 final WeakReference<InstalledAppDetails> mActivity; 769 final ApplicationInfo mInfo; 770 final int mState; 771 772 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 773 mPm = activity.mPm; 774 mActivity = new WeakReference<InstalledAppDetails>(activity); 775 mInfo = info; 776 mState = state; 777 } 778 779 @Override 780 protected Object doInBackground(Object... params) { 781 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 782 return null; 783 } 784 } 785 786 private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() { 787 788 @Override 789 public Loader<ChartData> onCreateLoader(int id, Bundle args) { 790 return new ChartDataLoader(getActivity(), mStatsSession, args); 791 } 792 793 @Override 794 public void onLoadFinished(Loader<ChartData> loader, ChartData data) { 795 mChartData = data; 796 mDataPreference.setSummary(getDataSummary()); 797 } 798 799 @Override 800 public void onLoaderReset(Loader<ChartData> loader) { 801 mChartData = null; 802 mDataPreference.setSummary(getDataSummary()); 803 } 804 }; 805 806 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 807 @Override 808 public void onReceive(Context context, Intent intent) { 809 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 810 } 811 }; 812} 813 814 815