InstalledAppDetails.java revision 173d658babe0bcb6add829d1621550cebeed2296
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(getTemplate(getContext()), app), 250 mDataCallbacks); 251 if (mPackageInfo != null) { 252 new BatteryUpdater().execute(); 253 } 254 } 255 256 @Override 257 public void onPause() { 258 getLoaderManager().destroyLoader(LOADER_CHART_DATA); 259 super.onPause(); 260 } 261 262 @Override 263 public void onDestroy() { 264 TrafficStats.closeQuietly(mStatsSession); 265 266 super.onDestroy(); 267 } 268 269 public void onActivityCreated(Bundle savedInstanceState) { 270 super.onActivityCreated(savedInstanceState); 271 handleHeader(); 272 273 mNotificationPreference = findPreference(KEY_NOTIFICATION); 274 mNotificationPreference.setOnPreferenceClickListener(this); 275 mStoragePreference = findPreference(KEY_STORAGE); 276 mStoragePreference.setOnPreferenceClickListener(this); 277 mPermissionsPreference = findPreference(KEY_PERMISSION); 278 mPermissionsPreference.setOnPreferenceClickListener(this); 279 mDataPreference = findPreference(KEY_DATA); 280 mDataPreference.setOnPreferenceClickListener(this); 281 mBatteryPreference = findPreference(KEY_BATTERY); 282 mBatteryPreference.setEnabled(false); 283 mBatteryPreference.setOnPreferenceClickListener(this); 284 285 mLaunchPreference = findPreference(KEY_LAUNCH); 286 if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) { 287 mLaunchPreference.setEnabled(false); 288 } else if (!mAppEntry.info.enabled) { 289 mLaunchPreference.setEnabled(false); 290 } else { 291 mLaunchPreference.setOnPreferenceClickListener(this); 292 } 293 } 294 295 private void handleHeader() { 296 mHeader = (LayoutPreference) findPreference(KEY_HEADER); 297 298 // Get Control button panel 299 View btnPanel = mHeader.findViewById(R.id.control_buttons_panel); 300 mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button); 301 mForceStopButton.setText(R.string.force_stop); 302 mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button); 303 mForceStopButton.setEnabled(false); 304 } 305 306 @Override 307 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 308 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 309 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 310 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 311 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 312 } 313 314 @Override 315 public void onPrepareOptionsMenu(Menu menu) { 316 boolean showIt = true; 317 if (mUpdatedSysApp) { 318 showIt = false; 319 } else if (mAppEntry == null) { 320 showIt = false; 321 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 322 showIt = false; 323 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 324 showIt = false; 325 } else if (UserHandle.myUserId() != 0) { 326 showIt = false; 327 } else if (mUserManager.getUsers().size() < 2) { 328 showIt = false; 329 } 330 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 331 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 332 menu.findItem(UNINSTALL_UPDATES).setVisible(mUpdatedSysApp && !mAppControlRestricted); 333 } 334 335 @Override 336 public boolean onOptionsItemSelected(MenuItem item) { 337 switch (item.getItemId()) { 338 case UNINSTALL_ALL_USERS_MENU: 339 uninstallPkg(mAppEntry.info.packageName, true, false); 340 return true; 341 case UNINSTALL_UPDATES: 342 showDialogInner(DLG_FACTORY_RESET, 0); 343 return true; 344 } 345 return false; 346 } 347 348 @Override 349 public void onActivityResult(int requestCode, int resultCode, Intent data) { 350 super.onActivityResult(requestCode, resultCode, data); 351 if (requestCode == REQUEST_UNINSTALL) { 352 if (mDisableAfterUninstall) { 353 mDisableAfterUninstall = false; 354 try { 355 ApplicationInfo ainfo = getActivity().getPackageManager().getApplicationInfo( 356 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 357 | PackageManager.GET_DISABLED_COMPONENTS); 358 if ((ainfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) { 359 new DisableChanger(this, mAppEntry.info, 360 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 361 .execute((Object)null); 362 } 363 } catch (NameNotFoundException e) { 364 } 365 } 366 if (!refreshUi()) { 367 setIntentAndFinish(true, true); 368 } 369 } 370 } 371 372 // Utility method to set application label and icon. 373 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 374 final View appSnippet = mHeader.findViewById(R.id.app_snippet); 375 appSnippet.setPaddingRelative(0, appSnippet.getPaddingTop(), 0, 376 appSnippet.getPaddingBottom()); 377 378 ImageView icon = (ImageView) appSnippet.findViewById(R.id.app_icon); 379 mState.ensureIcon(mAppEntry); 380 icon.setImageDrawable(mAppEntry.icon); 381 // Set application name. 382 TextView label = (TextView) appSnippet.findViewById(R.id.app_name); 383 label.setText(mAppEntry.label); 384 // Version number of application 385 mAppVersion = (TextView) appSnippet.findViewById(R.id.app_summary); 386 387 if (pkgInfo != null && pkgInfo.versionName != null) { 388 mAppVersion.setVisibility(View.VISIBLE); 389 mAppVersion.setText(getActivity().getString(R.string.version_text, 390 String.valueOf(pkgInfo.versionName))); 391 } else { 392 mAppVersion.setVisibility(View.INVISIBLE); 393 } 394 } 395 396 private boolean signaturesMatch(String pkg1, String pkg2) { 397 if (pkg1 != null && pkg2 != null) { 398 try { 399 final int match = mPm.checkSignatures(pkg1, pkg2); 400 if (match >= PackageManager.SIGNATURE_MATCH) { 401 return true; 402 } 403 } catch (Exception e) { 404 // e.g. named alternate package not found during lookup; 405 // this is an expected case sometimes 406 } 407 } 408 return false; 409 } 410 411 @Override 412 protected boolean refreshUi() { 413 retrieveAppEntry(); 414 if (mAppEntry == null) { 415 return false; // onCreate must have failed, make sure to exit 416 } 417 418 if (mPackageInfo == null) { 419 return false; // onCreate must have failed, make sure to exit 420 } 421 422 // Get list of "home" apps and trace through any meta-data references 423 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 424 mPm.getHomeActivities(homeActivities); 425 mHomePackages.clear(); 426 for (int i = 0; i< homeActivities.size(); i++) { 427 ResolveInfo ri = homeActivities.get(i); 428 final String activityPkg = ri.activityInfo.packageName; 429 mHomePackages.add(activityPkg); 430 431 // Also make sure to include anything proxying for the home app 432 final Bundle metadata = ri.activityInfo.metaData; 433 if (metadata != null) { 434 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 435 if (signaturesMatch(metaPkg, activityPkg)) { 436 mHomePackages.add(metaPkg); 437 } 438 } 439 } 440 441 checkForceStop(); 442 setAppLabelAndIcon(mPackageInfo); 443 initUninstallButtons(); 444 445 // Update the preference summaries. 446 Activity context = getActivity(); 447 mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); 448 mPermissionsPreference.setSummary(AppPermissionSettings.getSummary(mAppEntry, context)); 449 mLaunchPreference.setSummary(Utils.getLaunchByDeafaultSummary(mAppEntry, mUsbManager, 450 mPm, context)); 451 mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, 452 mBackend)); 453 mDataPreference.setSummary(getDataSummary()); 454 455 updateBattery(); 456 457 if (!mInitialized) { 458 // First time init: are we displaying an uninstalled app? 459 mInitialized = true; 460 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 461 } else { 462 // All other times: if the app no longer exists then we want 463 // to go away. 464 try { 465 ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( 466 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 467 | PackageManager.GET_DISABLED_COMPONENTS); 468 if (!mShowUninstalled) { 469 // If we did not start out with the app uninstalled, then 470 // it transitioning to the uninstalled state for the current 471 // user means we should go away as well. 472 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 473 } 474 } catch (NameNotFoundException e) { 475 return false; 476 } 477 } 478 479 return true; 480 } 481 482 private void updateBattery() { 483 if (mSipper != null) { 484 mBatteryPreference.setEnabled(true); 485 int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( 486 BatteryStats.STATS_SINCE_CHARGED); 487 final int percentOfMax = (int) ((mSipper.totalPowerMah) 488 / mBatteryHelper.getTotalPower() * dischargeAmount + .5f); 489 mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax)); 490 } else { 491 mBatteryPreference.setEnabled(false); 492 mBatteryPreference.setSummary(getString(R.string.no_battery_summary)); 493 } 494 } 495 496 private CharSequence getDataSummary() { 497 if (mChartData != null) { 498 long totalBytes = mChartData.detail.getTotalBytes(); 499 Context context = getActivity(); 500 return getString(R.string.data_summary_format, 501 Formatter.formatFileSize(context, totalBytes), 502 DateUtils.formatDateTime(context, mChartData.detail.getStart(), 503 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH)); 504 } 505 return getString(R.string.computing_size); 506 } 507 508 @Override 509 protected AlertDialog createDialog(int id, int errorCode) { 510 switch (id) { 511 case DLG_DISABLE: 512 return new AlertDialog.Builder(getActivity()) 513 .setTitle(getActivity().getText(R.string.app_disable_dlg_title)) 514 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 515 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 516 public void onClick(DialogInterface dialog, int which) { 517 // Disable the app 518 new DisableChanger(InstalledAppDetails.this, mAppEntry.info, 519 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 520 .execute((Object)null); 521 } 522 }) 523 .setNegativeButton(R.string.dlg_cancel, null) 524 .create(); 525 case DLG_SPECIAL_DISABLE: 526 return new AlertDialog.Builder(getActivity()) 527 .setTitle(getActivity().getText(R.string.app_special_disable_dlg_title)) 528 .setMessage(getActivity().getText(R.string.app_special_disable_dlg_text)) 529 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 530 public void onClick(DialogInterface dialog, int which) { 531 // Clear user data here 532 uninstallPkg(mAppEntry.info.packageName, 533 false, true); 534 } 535 }) 536 .setNegativeButton(R.string.dlg_cancel, null) 537 .create(); 538 case DLG_FORCE_STOP: 539 return new AlertDialog.Builder(getActivity()) 540 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 541 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 542 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 543 public void onClick(DialogInterface dialog, int which) { 544 // Force stop 545 forceStopPackage(mAppEntry.info.packageName); 546 } 547 }) 548 .setNegativeButton(R.string.dlg_cancel, null) 549 .create(); 550 case DLG_FACTORY_RESET: 551 return new AlertDialog.Builder(getActivity()) 552 .setTitle(getActivity().getText(R.string.app_factory_reset_dlg_title)) 553 .setMessage(getActivity().getText(R.string.app_factory_reset_dlg_text)) 554 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 555 public void onClick(DialogInterface dialog, int which) { 556 // Clear user data here 557 uninstallPkg(mAppEntry.info.packageName, 558 false, false); 559 } 560 }) 561 .setNegativeButton(R.string.dlg_cancel, null) 562 .create(); 563 } 564 return null; 565 } 566 567 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 568 // Create new intent to launch Uninstaller activity 569 Uri packageURI = Uri.parse("package:"+packageName); 570 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 571 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 572 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 573 mDisableAfterUninstall = andDisable; 574 } 575 576 private void forceStopPackage(String pkgName) { 577 ActivityManager am = (ActivityManager)getActivity().getSystemService( 578 Context.ACTIVITY_SERVICE); 579 am.forceStopPackage(pkgName); 580 int userId = UserHandle.getUserId(mAppEntry.info.uid); 581 mState.invalidatePackage(pkgName, userId); 582 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId); 583 if (newEnt != null) { 584 mAppEntry = newEnt; 585 } 586 checkForceStop(); 587 } 588 589 private void updateForceStopButton(boolean enabled) { 590 if (mAppControlRestricted) { 591 mForceStopButton.setEnabled(false); 592 } else { 593 mForceStopButton.setEnabled(enabled); 594 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 595 } 596 } 597 598 private void checkForceStop() { 599 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 600 // User can't force stop device admin. 601 updateForceStopButton(false); 602 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 603 // If the app isn't explicitly stopped, then always show the 604 // force stop button. 605 updateForceStopButton(true); 606 } else { 607 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 608 Uri.fromParts("package", mAppEntry.info.packageName, null)); 609 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 610 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 611 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 612 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 613 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 614 } 615 } 616 617 private void startManagePermissionsActivity() { 618 // start new activity to manage app permissions 619 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 620 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName); 621 intent.putExtra(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true); 622 try { 623 startActivity(intent); 624 } catch (ActivityNotFoundException e) { 625 Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS"); 626 } 627 } 628 629 private void startAppInfoFragment(Class<? extends AppInfoBase> fragment, CharSequence title) { 630 // start new fragment to display extended information 631 Bundle args = new Bundle(); 632 args.putString(InstalledAppDetails.ARG_PACKAGE_NAME, mAppEntry.info.packageName); 633 args.putBoolean(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true); 634 635 SettingsActivity sa = (SettingsActivity) getActivity(); 636 sa.startPreferencePanel(fragment.getName(), args, -1, title, this, SUB_INFO_FRAGMENT); 637 } 638 639 private void startNotifications() { 640 // start new fragment to display extended information 641 getActivity().startActivity(new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS) 642 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 643 .putExtra(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true) 644 .putExtra(Settings.EXTRA_APP_PACKAGE, mAppEntry.info.packageName) 645 .putExtra(Settings.EXTRA_APP_UID, mAppEntry.info.uid)); 646 } 647 648 /* 649 * Method implementing functionality of buttons clicked 650 * @see android.view.View.OnClickListener#onClick(android.view.View) 651 */ 652 public void onClick(View v) { 653 String packageName = mAppEntry.info.packageName; 654 if(v == mUninstallButton) { 655 if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 656 if (mAppEntry.info.enabled) { 657 if (mUpdatedSysApp) { 658 showDialogInner(DLG_SPECIAL_DISABLE, 0); 659 } else { 660 showDialogInner(DLG_DISABLE, 0); 661 } 662 } else { 663 new DisableChanger(this, mAppEntry.info, 664 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 665 .execute((Object) null); 666 } 667 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 668 uninstallPkg(packageName, true, false); 669 } else { 670 uninstallPkg(packageName, false, false); 671 } 672 } else if (v == mForceStopButton) { 673 showDialogInner(DLG_FORCE_STOP, 0); 674 //forceStopPackage(mAppInfo.packageName); 675 } 676 } 677 678 @Override 679 public boolean onPreferenceClick(Preference preference) { 680 if (preference == mStoragePreference) { 681 startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); 682 } else if (preference == mNotificationPreference) { 683 startNotifications(); 684 } else if (preference == mPermissionsPreference) { 685 startManagePermissionsActivity(); 686 } else if (preference == mLaunchPreference) { 687 startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); 688 } else if (preference == mDataPreference) { 689 Bundle args = new Bundle(); 690 args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG, 691 mAppEntry.info.packageName); 692 693 SettingsActivity sa = (SettingsActivity) getActivity(); 694 sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1, 695 getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT); 696 } else if (preference == mBatteryPreference) { 697 BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); 698 PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), 699 mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true); 700 } else { 701 return false; 702 } 703 return true; 704 } 705 706 private static NetworkTemplate getTemplate(Context context) { 707 if (DataUsageSummary.hasReadyMobileRadio(context)) { 708 return NetworkTemplate.buildTemplateMobileWildcard(); 709 } 710 if (DataUsageSummary.hasWifiRadio(context)) { 711 return NetworkTemplate.buildTemplateWifiWildcard(); 712 } 713 return NetworkTemplate.buildTemplateEthernet(); 714 } 715 716 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) { 717 return getNotificationSummary(appEntry, context, new NotificationBackend()); 718 } 719 720 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context, 721 NotificationBackend backend) { 722 AppRow appRow = backend.loadAppRow(context.getPackageManager(), appEntry.info); 723 return getNotificationSummary(appRow, context); 724 } 725 726 public static CharSequence getNotificationSummary(AppRow appRow, Context context) { 727 if (appRow.banned) { 728 return context.getString(R.string.notifications_disabled); 729 } 730 ArrayList<CharSequence> notifSummary = new ArrayList<>(); 731 if (appRow.priority) { 732 notifSummary.add(context.getString(R.string.notifications_priority)); 733 } 734 if (appRow.sensitive) { 735 notifSummary.add(context.getString(R.string.notifications_sensitive)); 736 } 737 if (!appRow.peekable) { 738 notifSummary.add(context.getString(R.string.notifications_no_peeking)); 739 } 740 switch (notifSummary.size()) { 741 case 3: 742 return context.getString(R.string.notifications_three_items, 743 notifSummary.get(0), notifSummary.get(1), notifSummary.get(2)); 744 case 2: 745 return context.getString(R.string.notifications_two_items, 746 notifSummary.get(0), notifSummary.get(1)); 747 case 1: 748 return notifSummary.get(0); 749 default: 750 return context.getString(R.string.notifications_enabled); 751 } 752 } 753 754 private class BatteryUpdater extends AsyncTask<Void, Void, Void> { 755 @Override 756 protected Void doInBackground(Void... params) { 757 mBatteryHelper.create((Bundle) null); 758 mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, 759 mUserManager.getUserProfiles()); 760 List<BatterySipper> usageList = mBatteryHelper.getUsageList(); 761 final int N = usageList.size(); 762 for (int i = 0; i < N; i++) { 763 BatterySipper sipper = usageList.get(i); 764 if (sipper.getUid() == mPackageInfo.applicationInfo.uid) { 765 mSipper = sipper; 766 break; 767 } 768 } 769 return null; 770 } 771 772 @Override 773 protected void onPostExecute(Void result) { 774 refreshUi(); 775 } 776 } 777 778 private static class DisableChanger extends AsyncTask<Object, Object, Object> { 779 final PackageManager mPm; 780 final WeakReference<InstalledAppDetails> mActivity; 781 final ApplicationInfo mInfo; 782 final int mState; 783 784 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 785 mPm = activity.mPm; 786 mActivity = new WeakReference<InstalledAppDetails>(activity); 787 mInfo = info; 788 mState = state; 789 } 790 791 @Override 792 protected Object doInBackground(Object... params) { 793 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 794 return null; 795 } 796 } 797 798 private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() { 799 800 @Override 801 public Loader<ChartData> onCreateLoader(int id, Bundle args) { 802 return new ChartDataLoader(getActivity(), mStatsSession, args); 803 } 804 805 @Override 806 public void onLoadFinished(Loader<ChartData> loader, ChartData data) { 807 mChartData = data; 808 mDataPreference.setSummary(getDataSummary()); 809 } 810 811 @Override 812 public void onLoaderReset(Loader<ChartData> loader) { 813 mChartData = null; 814 mDataPreference.setSummary(getDataSummary()); 815 } 816 }; 817 818 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 819 @Override 820 public void onReceive(Context context, Intent intent) { 821 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 822 } 823 }; 824} 825 826 827