InstalledAppDetails.java revision c3ab03ecdb505114770cbb87b61457fabf75847f
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.Manifest.permission; 20import android.app.Activity; 21import android.app.ActivityManager; 22import android.app.AlertDialog; 23import android.app.LoaderManager.LoaderCallbacks; 24import android.app.Notification; 25import android.app.admin.DevicePolicyManager; 26import android.content.ActivityNotFoundException; 27import android.content.BroadcastReceiver; 28import android.content.ComponentName; 29import android.content.Context; 30import android.content.DialogInterface; 31import android.content.Intent; 32import android.content.Loader; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageInfo; 35import android.content.pm.PackageManager; 36import android.content.pm.PackageManager.NameNotFoundException; 37import android.content.pm.ResolveInfo; 38import android.content.pm.UserInfo; 39import android.content.res.Resources; 40import android.graphics.drawable.Drawable; 41import android.icu.text.ListFormatter; 42import android.net.INetworkStatsService; 43import android.net.INetworkStatsSession; 44import android.net.NetworkTemplate; 45import android.net.TrafficStats; 46import android.net.Uri; 47import android.os.AsyncTask; 48import android.os.BatteryStats; 49import android.os.Bundle; 50import android.os.RemoteException; 51import android.os.ServiceManager; 52import android.os.UserHandle; 53import android.os.UserManager; 54import android.provider.Settings; 55import android.service.notification.NotificationListenerService.Ranking; 56import android.support.v7.preference.Preference; 57import android.support.v7.preference.Preference.OnPreferenceClickListener; 58import android.support.v7.preference.PreferenceCategory; 59import android.support.v7.preference.PreferenceScreen; 60import android.text.TextUtils; 61import android.text.format.DateUtils; 62import android.text.format.Formatter; 63import android.util.Log; 64import android.view.LayoutInflater; 65import android.view.Menu; 66import android.view.MenuInflater; 67import android.view.MenuItem; 68import android.view.View; 69import android.view.View.OnClickListener; 70import android.view.ViewGroup; 71import android.webkit.IWebViewUpdateService; 72import android.widget.Button; 73import android.widget.ImageView; 74import android.widget.TextView; 75 76import com.android.internal.logging.MetricsProto.MetricsEvent; 77import com.android.internal.os.BatterySipper; 78import com.android.internal.os.BatteryStatsHelper; 79import com.android.internal.widget.LockPatternUtils; 80import com.android.settings.AppHeader; 81import com.android.settings.DeviceAdminAdd; 82import com.android.settings.R; 83import com.android.settings.SettingsActivity; 84import com.android.settings.SettingsPreferenceFragment; 85import com.android.settings.Utils; 86import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback; 87import com.android.settings.datausage.AppDataUsage; 88import com.android.settings.datausage.DataUsageList; 89import com.android.settings.datausage.DataUsageSummary; 90import com.android.settings.fuelgauge.BatteryEntry; 91import com.android.settings.fuelgauge.PowerUsageDetail; 92import com.android.settings.notification.AppNotificationSettings; 93import com.android.settings.notification.NotificationBackend; 94import com.android.settings.notification.NotificationBackend.AppRow; 95import com.android.settingslib.AppItem; 96import com.android.settingslib.RestrictedLockUtils; 97import com.android.settingslib.applications.AppUtils; 98import com.android.settingslib.applications.ApplicationsState; 99import com.android.settingslib.applications.ApplicationsState.AppEntry; 100import com.android.settingslib.net.ChartData; 101import com.android.settingslib.net.ChartDataLoader; 102 103import java.lang.ref.WeakReference; 104import java.util.ArrayList; 105import java.util.Arrays; 106import java.util.HashSet; 107import java.util.List; 108 109import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 110 111/** 112 * Activity to display application information from Settings. This activity presents 113 * extended information associated with a package like code, data, total size, permissions 114 * used by the application and also the set of default launchable activities. 115 * For system applications, an option to clear user data is displayed only if data size is > 0. 116 * System applications that do not want clear user data do not have this option. 117 * For non-system applications, there is no option to clear data. Instead there is an option to 118 * uninstall the application. 119 */ 120public class InstalledAppDetails extends AppInfoBase 121 implements View.OnClickListener, OnPreferenceClickListener { 122 123 private static final String LOG_TAG = "InstalledAppDetails"; 124 125 // Menu identifiers 126 public static final int UNINSTALL_ALL_USERS_MENU = 1; 127 public static final int UNINSTALL_UPDATES = 2; 128 129 // Result code identifiers 130 public static final int REQUEST_UNINSTALL = 0; 131 private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; 132 133 private static final int SUB_INFO_FRAGMENT = 1; 134 135 private static final int LOADER_CHART_DATA = 2; 136 137 private static final int DLG_FORCE_STOP = DLG_BASE + 1; 138 private static final int DLG_DISABLE = DLG_BASE + 2; 139 private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3; 140 141 private static final String KEY_HEADER = "header_view"; 142 private static final String KEY_NOTIFICATION = "notification_settings"; 143 private static final String KEY_STORAGE = "storage_settings"; 144 private static final String KEY_PERMISSION = "permission_settings"; 145 private static final String KEY_DATA = "data_settings"; 146 private static final String KEY_LAUNCH = "preferred_settings"; 147 private static final String KEY_BATTERY = "battery"; 148 private static final String KEY_MEMORY = "memory"; 149 150 private static final String NOTIFICATION_TUNER_SETTING = "show_importance_slider"; 151 152 private final HashSet<String> mHomePackages = new HashSet<String>(); 153 154 private boolean mInitialized; 155 private boolean mShowUninstalled; 156 private LayoutPreference mHeader; 157 private Button mUninstallButton; 158 private boolean mUpdatedSysApp = false; 159 private Button mForceStopButton; 160 private Preference mNotificationPreference; 161 private Preference mStoragePreference; 162 private Preference mPermissionsPreference; 163 private Preference mLaunchPreference; 164 private Preference mDataPreference; 165 private Preference mMemoryPreference; 166 167 private boolean mDisableAfterUninstall; 168 // Used for updating notification preference. 169 private final NotificationBackend mBackend = new NotificationBackend(); 170 171 private ChartData mChartData; 172 private INetworkStatsSession mStatsSession; 173 174 private Preference mBatteryPreference; 175 176 private BatteryStatsHelper mBatteryHelper; 177 private BatterySipper mSipper; 178 179 protected ProcStatsData mStatsManager; 180 protected ProcStatsPackageEntry mStats; 181 182 private BroadcastReceiver mPermissionReceiver; 183 184 private boolean handleDisableable(Button button) { 185 boolean disableable = false; 186 // Try to prevent the user from bricking their phone 187 // by not allowing disabling of apps signed with the 188 // system cert and any launcher app in the system. 189 if (mHomePackages.contains(mAppEntry.info.packageName) 190 || Utils.isSystemPackage(mPm, mPackageInfo)) { 191 // Disable button for core system applications. 192 button.setText(R.string.disable_text); 193 } else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 194 button.setText(R.string.disable_text); 195 disableable = true; 196 } else { 197 button.setText(R.string.enable_text); 198 disableable = true; 199 } 200 201 return disableable; 202 } 203 204 private boolean isDisabledUntilUsed() { 205 return mAppEntry.info.enabledSetting 206 == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED; 207 } 208 209 private void initUninstallButtons() { 210 final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 211 boolean enabled = true; 212 if (isBundled) { 213 enabled = handleDisableable(mUninstallButton); 214 } else { 215 if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0 216 && mUserManager.getUsers().size() >= 2) { 217 // When we have multiple users, there is a separate menu 218 // to uninstall for all users. 219 enabled = false; 220 } 221 mUninstallButton.setText(R.string.uninstall_text); 222 } 223 // If this is a device admin, it can't be uninstalled or disabled. 224 // We do this here so the text of the button is still set correctly. 225 if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 226 enabled = false; 227 } 228 229 // We don't allow uninstalling DO/PO on *any* users, because if it's a system app, 230 // "uninstall" is actually "downgrade to the system version + disable", and "downgrade" 231 // will clear data on all users. 232 if (isProfileOrDeviceOwner(mPackageInfo.packageName)) { 233 enabled = false; 234 } 235 236 // If the uninstall intent is already queued, disable the uninstall button 237 if (mDpm.isUninstallInQueue(mPackageName)) { 238 enabled = false; 239 } 240 241 // Home apps need special handling. Bundled ones we don't risk downgrading 242 // because that can interfere with home-key resolution. Furthermore, we 243 // can't allow uninstallation of the only home app, and we don't want to 244 // allow uninstallation of an explicitly preferred one -- the user can go 245 // to Home settings and pick a different one, after which we'll permit 246 // uninstallation of the now-not-default one. 247 if (enabled && mHomePackages.contains(mPackageInfo.packageName)) { 248 if (isBundled) { 249 enabled = false; 250 } else { 251 ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 252 ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities); 253 if (currentDefaultHome == null) { 254 // No preferred default, so permit uninstall only when 255 // there is more than one candidate 256 enabled = (mHomePackages.size() > 1); 257 } else { 258 // There is an explicit default home app -- forbid uninstall of 259 // that one, but permit it for installed-but-inactive ones. 260 enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName()); 261 } 262 } 263 } 264 265 if (mAppsControlDisallowedBySystem) { 266 enabled = false; 267 } 268 269 try { 270 IWebViewUpdateService webviewUpdateService = 271 IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate")); 272 if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) { 273 enabled = false; 274 } 275 } catch (RemoteException e) { 276 throw new RuntimeException(e); 277 } 278 279 mUninstallButton.setEnabled(enabled); 280 if (enabled) { 281 // Register listener 282 mUninstallButton.setOnClickListener(this); 283 } 284 } 285 286 /** Returns if the supplied package is device owner or profile owner of at least one user */ 287 private boolean isProfileOrDeviceOwner(String packageName) { 288 List<UserInfo> userInfos = mUserManager.getUsers(); 289 DevicePolicyManager dpm = (DevicePolicyManager) 290 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); 291 if (dpm.isDeviceOwnerAppOnAnyUser(packageName)) { 292 return true; 293 } 294 for (UserInfo userInfo : userInfos) { 295 ComponentName cn = dpm.getProfileOwnerAsUser(userInfo.id); 296 if (cn != null && cn.getPackageName().equals(packageName)) { 297 return true; 298 } 299 } 300 return false; 301 } 302 303 /** Called when the activity is first created. */ 304 @Override 305 public void onCreate(Bundle icicle) { 306 super.onCreate(icicle); 307 308 setHasOptionsMenu(true); 309 addPreferencesFromResource(R.xml.installed_app_details); 310 addDynamicPrefs(); 311 312 if (Utils.isBandwidthControlEnabled()) { 313 INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( 314 ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); 315 try { 316 mStatsSession = statsService.openSession(); 317 } catch (RemoteException e) { 318 throw new RuntimeException(e); 319 } 320 } else { 321 removePreference(KEY_DATA); 322 } 323 mBatteryHelper = new BatteryStatsHelper(getActivity(), true); 324 } 325 326 @Override 327 protected int getMetricsCategory() { 328 return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS; 329 } 330 331 @Override 332 public void onResume() { 333 super.onResume(); 334 if (mFinishing) { 335 return; 336 } 337 mState.requestSize(mPackageName, mUserId); 338 AppItem app = new AppItem(mAppEntry.info.uid); 339 app.addUid(mAppEntry.info.uid); 340 if (mStatsSession != null) { 341 getLoaderManager().restartLoader(LOADER_CHART_DATA, 342 ChartDataLoader.buildArgs(getTemplate(getContext()), app), 343 mDataCallbacks); 344 } 345 new BatteryUpdater().execute(); 346 new MemoryUpdater().execute(); 347 updateDynamicPrefs(); 348 } 349 350 @Override 351 public void onPause() { 352 getLoaderManager().destroyLoader(LOADER_CHART_DATA); 353 super.onPause(); 354 } 355 356 @Override 357 public void onDestroy() { 358 TrafficStats.closeQuietly(mStatsSession); 359 if (mPermissionReceiver != null) { 360 getContext().unregisterReceiver(mPermissionReceiver); 361 mPermissionReceiver = null; 362 } 363 364 super.onDestroy(); 365 } 366 367 public void onActivityCreated(Bundle savedInstanceState) { 368 super.onActivityCreated(savedInstanceState); 369 if (mFinishing) { 370 return; 371 } 372 handleHeader(); 373 374 mNotificationPreference = findPreference(KEY_NOTIFICATION); 375 mNotificationPreference.setOnPreferenceClickListener(this); 376 mStoragePreference = findPreference(KEY_STORAGE); 377 mStoragePreference.setOnPreferenceClickListener(this); 378 mPermissionsPreference = findPreference(KEY_PERMISSION); 379 mPermissionsPreference.setOnPreferenceClickListener(this); 380 mDataPreference = findPreference(KEY_DATA); 381 if (mDataPreference != null) { 382 mDataPreference.setOnPreferenceClickListener(this); 383 } 384 mBatteryPreference = findPreference(KEY_BATTERY); 385 mBatteryPreference.setEnabled(false); 386 mBatteryPreference.setOnPreferenceClickListener(this); 387 mMemoryPreference = findPreference(KEY_MEMORY); 388 mMemoryPreference.setOnPreferenceClickListener(this); 389 390 mLaunchPreference = findPreference(KEY_LAUNCH); 391 if (mAppEntry != null && mAppEntry.info != null) { 392 if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 || 393 !mAppEntry.info.enabled) { 394 mLaunchPreference.setEnabled(false); 395 } else { 396 mLaunchPreference.setOnPreferenceClickListener(this); 397 } 398 } else { 399 mLaunchPreference.setEnabled(false); 400 } 401 } 402 403 private void handleHeader() { 404 mHeader = (LayoutPreference) findPreference(KEY_HEADER); 405 406 // Get Control button panel 407 View btnPanel = mHeader.findViewById(R.id.control_buttons_panel); 408 mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button); 409 mForceStopButton.setText(R.string.force_stop); 410 mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button); 411 mForceStopButton.setEnabled(false); 412 413 View gear = mHeader.findViewById(R.id.gear); 414 Intent i = new Intent(Intent.ACTION_APPLICATION_PREFERENCES); 415 i.setPackage(mPackageName); 416 final Intent intent = resolveIntent(i); 417 if (intent != null) { 418 gear.setVisibility(View.VISIBLE); 419 gear.setOnClickListener(new OnClickListener() { 420 @Override 421 public void onClick(View v) { 422 startActivity(intent); 423 } 424 }); 425 } else { 426 gear.setVisibility(View.GONE); 427 } 428 } 429 430 private Intent resolveIntent(Intent i) { 431 ResolveInfo result = getContext().getPackageManager().resolveActivity(i, 0); 432 return result != null ? new Intent(i.getAction()) 433 .setClassName(result.activityInfo.packageName, result.activityInfo.name) : null; 434 } 435 436 @Override 437 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 438 menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset) 439 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 440 menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text) 441 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 442 } 443 444 @Override 445 public void onPrepareOptionsMenu(Menu menu) { 446 if (mFinishing) { 447 return; 448 } 449 boolean showIt = true; 450 if (mUpdatedSysApp) { 451 showIt = false; 452 } else if (mAppEntry == null) { 453 showIt = false; 454 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 455 showIt = false; 456 } else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 457 showIt = false; 458 } else if (UserHandle.myUserId() != 0) { 459 showIt = false; 460 } else if (mUserManager.getUsers().size() < 2) { 461 showIt = false; 462 } 463 menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt); 464 mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; 465 MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES); 466 uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem); 467 if (uninstallUpdatesItem.isVisible()) { 468 RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(), 469 uninstallUpdatesItem, mAppsControlDisallowedAdmin); 470 } 471 } 472 473 @Override 474 public boolean onOptionsItemSelected(MenuItem item) { 475 switch (item.getItemId()) { 476 case UNINSTALL_ALL_USERS_MENU: 477 uninstallPkg(mAppEntry.info.packageName, true, false); 478 return true; 479 case UNINSTALL_UPDATES: 480 uninstallPkg(mAppEntry.info.packageName, false, false); 481 return true; 482 } 483 return false; 484 } 485 486 @Override 487 public void onActivityResult(int requestCode, int resultCode, Intent data) { 488 super.onActivityResult(requestCode, resultCode, data); 489 if (requestCode == REQUEST_UNINSTALL) { 490 if (mDisableAfterUninstall) { 491 mDisableAfterUninstall = false; 492 new DisableChanger(this, mAppEntry.info, 493 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 494 .execute((Object)null); 495 } 496 if (!refreshUi()) { 497 setIntentAndFinish(true, true); 498 } 499 } 500 if (requestCode == REQUEST_REMOVE_DEVICE_ADMIN) { 501 if (!refreshUi()) { 502 setIntentAndFinish(true, true); 503 } 504 } 505 } 506 507 // Utility method to set application label and icon. 508 private void setAppLabelAndIcon(PackageInfo pkgInfo) { 509 final View appSnippet = mHeader.findViewById(R.id.app_snippet); 510 mState.ensureIcon(mAppEntry); 511 setupAppSnippet(appSnippet, mAppEntry.label, mAppEntry.icon, 512 pkgInfo != null ? pkgInfo.versionName : null); 513 } 514 515 private boolean signaturesMatch(String pkg1, String pkg2) { 516 if (pkg1 != null && pkg2 != null) { 517 try { 518 final int match = mPm.checkSignatures(pkg1, pkg2); 519 if (match >= PackageManager.SIGNATURE_MATCH) { 520 return true; 521 } 522 } catch (Exception e) { 523 // e.g. named alternate package not found during lookup; 524 // this is an expected case sometimes 525 } 526 } 527 return false; 528 } 529 530 @Override 531 protected boolean refreshUi() { 532 retrieveAppEntry(); 533 if (mAppEntry == null) { 534 return false; // onCreate must have failed, make sure to exit 535 } 536 537 if (mPackageInfo == null) { 538 return false; // onCreate must have failed, make sure to exit 539 } 540 541 // Get list of "home" apps and trace through any meta-data references 542 List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>(); 543 mPm.getHomeActivities(homeActivities); 544 mHomePackages.clear(); 545 for (int i = 0; i< homeActivities.size(); i++) { 546 ResolveInfo ri = homeActivities.get(i); 547 final String activityPkg = ri.activityInfo.packageName; 548 mHomePackages.add(activityPkg); 549 550 // Also make sure to include anything proxying for the home app 551 final Bundle metadata = ri.activityInfo.metaData; 552 if (metadata != null) { 553 final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE); 554 if (signaturesMatch(metaPkg, activityPkg)) { 555 mHomePackages.add(metaPkg); 556 } 557 } 558 } 559 560 checkForceStop(); 561 setAppLabelAndIcon(mPackageInfo); 562 initUninstallButtons(); 563 564 // Update the preference summaries. 565 Activity context = getActivity(); 566 mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context)); 567 if (mPermissionReceiver != null) { 568 getContext().unregisterReceiver(mPermissionReceiver); 569 } 570 mPermissionReceiver = PermissionsSummaryHelper.getPermissionSummary(getContext(), 571 mPackageName, mPermissionCallback); 572 mLaunchPreference.setSummary(AppUtils.getLaunchByDefaultSummary(mAppEntry, mUsbManager, 573 mPm, context)); 574 mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context, 575 mBackend)); 576 if (mDataPreference != null) { 577 mDataPreference.setSummary(getDataSummary()); 578 } 579 580 updateBattery(); 581 582 if (!mInitialized) { 583 // First time init: are we displaying an uninstalled app? 584 mInitialized = true; 585 mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0; 586 } else { 587 // All other times: if the app no longer exists then we want 588 // to go away. 589 try { 590 ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo( 591 mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES 592 | PackageManager.GET_DISABLED_COMPONENTS); 593 if (!mShowUninstalled) { 594 // If we did not start out with the app uninstalled, then 595 // it transitioning to the uninstalled state for the current 596 // user means we should go away as well. 597 return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0; 598 } 599 } catch (NameNotFoundException e) { 600 return false; 601 } 602 } 603 604 return true; 605 } 606 607 private void updateBattery() { 608 if (mSipper != null) { 609 mBatteryPreference.setEnabled(true); 610 int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount( 611 BatteryStats.STATS_SINCE_CHARGED); 612 final int percentOfMax = (int) ((mSipper.totalPowerMah) 613 / mBatteryHelper.getTotalPower() * dischargeAmount + .5f); 614 mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax)); 615 } else { 616 mBatteryPreference.setEnabled(false); 617 mBatteryPreference.setSummary(getString(R.string.no_battery_summary)); 618 } 619 } 620 621 private CharSequence getDataSummary() { 622 if (mChartData != null) { 623 long totalBytes = mChartData.detail.getTotalBytes(); 624 if (totalBytes == 0) { 625 return getString(R.string.no_data_usage); 626 } 627 Context context = getActivity(); 628 return getString(R.string.data_summary_format, 629 Formatter.formatFileSize(context, totalBytes), 630 DateUtils.formatDateTime(context, mChartData.detail.getStart(), 631 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH)); 632 } 633 return getString(R.string.computing_size); 634 } 635 636 @Override 637 protected AlertDialog createDialog(int id, int errorCode) { 638 switch (id) { 639 case DLG_DISABLE: 640 return new AlertDialog.Builder(getActivity()) 641 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 642 .setPositiveButton(R.string.app_disable_dlg_positive, 643 new DialogInterface.OnClickListener() { 644 public void onClick(DialogInterface dialog, int which) { 645 // Disable the app 646 new DisableChanger(InstalledAppDetails.this, mAppEntry.info, 647 PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER) 648 .execute((Object)null); 649 } 650 }) 651 .setNegativeButton(R.string.dlg_cancel, null) 652 .create(); 653 case DLG_SPECIAL_DISABLE: 654 return new AlertDialog.Builder(getActivity()) 655 .setMessage(getActivity().getText(R.string.app_disable_dlg_text)) 656 .setPositiveButton(R.string.app_disable_dlg_positive, 657 new DialogInterface.OnClickListener() { 658 public void onClick(DialogInterface dialog, int which) { 659 // Disable the app and ask for uninstall 660 uninstallPkg(mAppEntry.info.packageName, 661 false, true); 662 } 663 }) 664 .setNegativeButton(R.string.dlg_cancel, null) 665 .create(); 666 case DLG_FORCE_STOP: 667 return new AlertDialog.Builder(getActivity()) 668 .setTitle(getActivity().getText(R.string.force_stop_dlg_title)) 669 .setMessage(getActivity().getText(R.string.force_stop_dlg_text)) 670 .setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() { 671 public void onClick(DialogInterface dialog, int which) { 672 // Force stop 673 forceStopPackage(mAppEntry.info.packageName); 674 } 675 }) 676 .setNegativeButton(R.string.dlg_cancel, null) 677 .create(); 678 } 679 return null; 680 } 681 682 private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) { 683 // Create new intent to launch Uninstaller activity 684 Uri packageURI = Uri.parse("package:"+packageName); 685 Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI); 686 uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers); 687 startActivityForResult(uninstallIntent, REQUEST_UNINSTALL); 688 mDisableAfterUninstall = andDisable; 689 } 690 691 private void forceStopPackage(String pkgName) { 692 ActivityManager am = (ActivityManager) getActivity().getSystemService( 693 Context.ACTIVITY_SERVICE); 694 am.forceStopPackage(pkgName); 695 int userId = UserHandle.getUserId(mAppEntry.info.uid); 696 mState.invalidatePackage(pkgName, userId); 697 ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId); 698 if (newEnt != null) { 699 mAppEntry = newEnt; 700 } 701 checkForceStop(); 702 } 703 704 private void updateForceStopButton(boolean enabled) { 705 if (mAppsControlDisallowedBySystem) { 706 mForceStopButton.setEnabled(false); 707 } else { 708 mForceStopButton.setEnabled(enabled); 709 mForceStopButton.setOnClickListener(InstalledAppDetails.this); 710 } 711 } 712 713 private void checkForceStop() { 714 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 715 // User can't force stop device admin. 716 updateForceStopButton(false); 717 } else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) { 718 // If the app isn't explicitly stopped, then always show the 719 // force stop button. 720 updateForceStopButton(true); 721 } else { 722 Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART, 723 Uri.fromParts("package", mAppEntry.info.packageName, null)); 724 intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName }); 725 intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid); 726 intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid)); 727 getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, 728 mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null); 729 } 730 } 731 732 private void startManagePermissionsActivity() { 733 // start new activity to manage app permissions 734 Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); 735 intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName); 736 intent.putExtra(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); 737 try { 738 startActivity(intent); 739 } catch (ActivityNotFoundException e) { 740 Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS"); 741 } 742 } 743 744 private void startAppInfoFragment(Class<?> fragment, CharSequence title) { 745 startAppInfoFragment(fragment, title, this, mAppEntry); 746 } 747 748 public static void startAppInfoFragment(Class<?> fragment, CharSequence title, 749 SettingsPreferenceFragment caller, AppEntry appEntry) { 750 // start new fragment to display extended information 751 Bundle args = new Bundle(); 752 args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName); 753 args.putInt(ARG_PACKAGE_UID, appEntry.info.uid); 754 args.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true); 755 756 SettingsActivity sa = (SettingsActivity) caller.getActivity(); 757 sa.startPreferencePanel(fragment.getName(), args, -1, title, caller, SUB_INFO_FRAGMENT); 758 } 759 760 /* 761 * Method implementing functionality of buttons clicked 762 * @see android.view.View.OnClickListener#onClick(android.view.View) 763 */ 764 public void onClick(View v) { 765 if (mAppEntry == null) { 766 setIntentAndFinish(true, true); 767 return; 768 } 769 String packageName = mAppEntry.info.packageName; 770 if (v == mUninstallButton) { 771 if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) { 772 Activity activity = getActivity(); 773 Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class); 774 uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME, 775 mPackageName); 776 activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN); 777 return; 778 } 779 EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(), 780 packageName, mUserId); 781 boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem || 782 RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId); 783 if (admin != null && !uninstallBlockedBySystem) { 784 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin); 785 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { 786 if (mAppEntry.info.enabled && !isDisabledUntilUsed()) { 787 // If the system app has an update and this is the only user on the device, 788 // then offer to downgrade the app, otherwise only offer to disable the 789 // app for this user. 790 if (mUpdatedSysApp && isSingleUser()) { 791 showDialogInner(DLG_SPECIAL_DISABLE, 0); 792 } else { 793 showDialogInner(DLG_DISABLE, 0); 794 } 795 } else { 796 new DisableChanger(this, mAppEntry.info, 797 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) 798 .execute((Object) null); 799 } 800 } else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { 801 uninstallPkg(packageName, true, false); 802 } else { 803 uninstallPkg(packageName, false, false); 804 } 805 } else if (v == mForceStopButton) { 806 if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) { 807 RestrictedLockUtils.sendShowAdminSupportDetailsIntent( 808 getActivity(), mAppsControlDisallowedAdmin); 809 } else { 810 showDialogInner(DLG_FORCE_STOP, 0); 811 //forceStopPackage(mAppInfo.packageName); 812 } 813 } 814 } 815 816 /** Returns whether there is only one user on this device, not including the system-only user */ 817 private boolean isSingleUser() { 818 final int userCount = mUserManager.getUserCount(); 819 return userCount == 1 820 || (mUserManager.isSplitSystemUser() && userCount == 2); 821 } 822 823 @Override 824 public boolean onPreferenceClick(Preference preference) { 825 if (preference == mStoragePreference) { 826 startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); 827 } else if (preference == mNotificationPreference) { 828 startAppInfoFragment(AppNotificationSettings.class, 829 getString(R.string.app_notifications_title)); 830 } else if (preference == mPermissionsPreference) { 831 startManagePermissionsActivity(); 832 } else if (preference == mLaunchPreference) { 833 startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); 834 } else if (preference == mMemoryPreference) { 835 ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(), 836 mStatsManager.getMemInfo(), mStats, false); 837 } else if (preference == mDataPreference) { 838 startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage)); 839 } else if (preference == mBatteryPreference) { 840 BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); 841 PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), 842 mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true, false); 843 } else { 844 return false; 845 } 846 return true; 847 } 848 849 private void addDynamicPrefs() { 850 if (Utils.isManagedProfile(UserManager.get(getContext()))) { 851 return; 852 } 853 final PreferenceScreen screen = getPreferenceScreen(); 854 if (DefaultHomePreference.hasHomePreference(mPackageName, getContext())) { 855 screen.addPreference(new ShortcutPreference(getPrefContext(), 856 AdvancedAppSettings.class, "default_home", R.string.home_app, 857 R.string.configure_apps)); 858 } 859 if (DefaultBrowserPreference.hasBrowserPreference(mPackageName, getContext())) { 860 screen.addPreference(new ShortcutPreference(getPrefContext(), 861 AdvancedAppSettings.class, "default_browser", R.string.default_browser_title, 862 R.string.configure_apps)); 863 } 864 if (DefaultPhonePreference.hasPhonePreference(mPackageName, getContext())) { 865 screen.addPreference(new ShortcutPreference(getPrefContext(), 866 AdvancedAppSettings.class, "default_phone_app", R.string.default_phone_title, 867 R.string.configure_apps)); 868 } 869 if (DefaultEmergencyPreference.hasEmergencyPreference(mPackageName, getContext())) { 870 screen.addPreference(new ShortcutPreference(getPrefContext(), 871 AdvancedAppSettings.class, "default_emergency_app", 872 R.string.default_emergency_app, R.string.configure_apps)); 873 } 874 if (DefaultSmsPreference.hasSmsPreference(mPackageName, getContext())) { 875 screen.addPreference(new ShortcutPreference(getPrefContext(), 876 AdvancedAppSettings.class, "default_sms_app", R.string.sms_application_title, 877 R.string.configure_apps)); 878 } 879 boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW); 880 boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS); 881 if (hasDrawOverOtherApps || hasWriteSettings) { 882 PreferenceCategory category = new PreferenceCategory(getPrefContext()); 883 category.setTitle(R.string.advanced_apps); 884 screen.addPreference(category); 885 886 if (hasDrawOverOtherApps) { 887 Preference pref = new Preference(getPrefContext()); 888 pref.setTitle(R.string.draw_overlay); 889 pref.setKey("system_alert_window"); 890 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 891 @Override 892 public boolean onPreferenceClick(Preference preference) { 893 startAppInfoFragment(DrawOverlayDetails.class, 894 getString(R.string.draw_overlay)); 895 return true; 896 } 897 }); 898 category.addPreference(pref); 899 } 900 if (hasWriteSettings) { 901 Preference pref = new Preference(getPrefContext()); 902 pref.setTitle(R.string.write_settings); 903 pref.setKey("write_settings_apps"); 904 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 905 @Override 906 public boolean onPreferenceClick(Preference preference) { 907 startAppInfoFragment(WriteSettingsDetails.class, 908 getString(R.string.write_settings)); 909 return true; 910 } 911 }); 912 category.addPreference(pref); 913 } 914 } 915 916 final String installerPackageName = 917 getContext().getPackageManager().getInstallerPackageName(mPackageName); 918 if (installerPackageName != null) { 919 final Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO) 920 .setPackage(installerPackageName); 921 final Intent result = resolveIntent(intent); 922 if (result != null) { 923 result.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName); 924 PreferenceCategory category = new PreferenceCategory(getPrefContext()); 925 category.setTitle(R.string.app_install_details_group_title); 926 screen.addPreference(category); 927 Preference pref = new Preference(getPrefContext()); 928 pref.setTitle(R.string.app_install_details_title); 929 pref.setKey("app_info_store"); 930 pref.setSummary(getString(R.string.app_install_details_summary, mAppEntry.label)); 931 pref.setIntent(result); 932 category.addPreference(pref); 933 } 934 } 935 } 936 937 private boolean hasPermission(String permission) { 938 if (mPackageInfo == null || mPackageInfo.requestedPermissions == null) { 939 return false; 940 } 941 for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) { 942 if (mPackageInfo.requestedPermissions[i].equals(permission)) { 943 return true; 944 } 945 } 946 return false; 947 } 948 949 private void updateDynamicPrefs() { 950 Preference pref = findPreference("default_home"); 951 if (pref != null) { 952 pref.setSummary(DefaultHomePreference.isHomeDefault(mPackageName, getContext()) 953 ? R.string.yes : R.string.no); 954 } 955 pref = findPreference("default_browser"); 956 if (pref != null) { 957 pref.setSummary(DefaultBrowserPreference.isBrowserDefault(mPackageName, getContext()) 958 ? R.string.yes : R.string.no); 959 } 960 pref = findPreference("default_phone_app"); 961 if (pref != null) { 962 pref.setSummary(DefaultPhonePreference.isPhoneDefault(mPackageName, getContext()) 963 ? R.string.yes : R.string.no); 964 } 965 pref = findPreference("default_emergency_app"); 966 if (pref != null) { 967 pref.setSummary(DefaultEmergencyPreference.isEmergencyDefault(mPackageName, 968 getContext()) ? R.string.yes : R.string.no); 969 } 970 pref = findPreference("default_sms_app"); 971 if (pref != null) { 972 pref.setSummary(DefaultSmsPreference.isSmsDefault(mPackageName, getContext()) 973 ? R.string.yes : R.string.no); 974 } 975 pref = findPreference("system_alert_window"); 976 if (pref != null) { 977 pref.setSummary(DrawOverlayDetails.getSummary(getContext(), mAppEntry)); 978 } 979 pref = findPreference("write_settings_apps"); 980 if (pref != null) { 981 pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry)); 982 } 983 } 984 985 public static void setupAppSnippet(View appSnippet, CharSequence label, Drawable icon, 986 CharSequence versionName) { 987 LayoutInflater.from(appSnippet.getContext()).inflate(R.layout.widget_text_views, 988 (ViewGroup) appSnippet.findViewById(android.R.id.widget_frame)); 989 990 ImageView iconView = (ImageView) appSnippet.findViewById(android.R.id.icon); 991 iconView.setImageDrawable(icon); 992 // Set application name. 993 TextView labelView = (TextView) appSnippet.findViewById(android.R.id.title); 994 labelView.setText(label); 995 // Version number of application 996 TextView appVersion = (TextView) appSnippet.findViewById(R.id.widget_text1); 997 998 if (!TextUtils.isEmpty(versionName)) { 999 appVersion.setSelected(true); 1000 appVersion.setVisibility(View.VISIBLE); 1001 appVersion.setText(appSnippet.getContext().getString(R.string.version_text, 1002 String.valueOf(versionName))); 1003 } else { 1004 appVersion.setVisibility(View.INVISIBLE); 1005 } 1006 } 1007 1008 public static NetworkTemplate getTemplate(Context context) { 1009 if (DataUsageList.hasReadyMobileRadio(context)) { 1010 return NetworkTemplate.buildTemplateMobileWildcard(); 1011 } 1012 if (DataUsageSummary.hasWifiRadio(context)) { 1013 return NetworkTemplate.buildTemplateWifiWildcard(); 1014 } 1015 return NetworkTemplate.buildTemplateEthernet(); 1016 } 1017 1018 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) { 1019 return getNotificationSummary(appEntry, context, new NotificationBackend()); 1020 } 1021 1022 public static CharSequence getNotificationSummary(AppEntry appEntry, Context context, 1023 NotificationBackend backend) { 1024 AppRow appRow = backend.loadAppRow(context, context.getPackageManager(), appEntry.info); 1025 return getNotificationSummary(appRow, context); 1026 } 1027 1028 public static CharSequence getNotificationSummary(AppRow appRow, Context context) { 1029 boolean showSlider = Settings.Secure.getInt( 1030 context.getContentResolver(), NOTIFICATION_TUNER_SETTING, 0) == 1; 1031 List<String> summaryAttributes = new ArrayList<>(); 1032 StringBuffer summary = new StringBuffer(); 1033 if (showSlider) { 1034 if (appRow.appImportance != Ranking.IMPORTANCE_UNSPECIFIED) { 1035 summaryAttributes.add(context.getString( 1036 R.string.notification_summary_level, appRow.appImportance)); 1037 } 1038 } else { 1039 if (appRow.banned) { 1040 summaryAttributes.add(context.getString(R.string.notifications_disabled)); 1041 } else if (appRow.appImportance > Ranking.IMPORTANCE_NONE 1042 && appRow.appImportance < Ranking.IMPORTANCE_DEFAULT) { 1043 summaryAttributes.add(context.getString(R.string.notifications_silenced)); 1044 } 1045 } 1046 final boolean lockscreenSecure = new LockPatternUtils(context).isSecure( 1047 UserHandle.myUserId()); 1048 if (lockscreenSecure) { 1049 if (appRow.appVisOverride == Notification.VISIBILITY_PRIVATE) { 1050 summaryAttributes.add(context.getString(R.string.notifications_redacted)); 1051 } else if (appRow.appVisOverride == Notification.VISIBILITY_SECRET) { 1052 summaryAttributes.add(context.getString(R.string.notifications_hidden)); 1053 } 1054 } 1055 if (appRow.appBypassDnd) { 1056 summaryAttributes.add(context.getString(R.string.notifications_priority)); 1057 } 1058 final int N = summaryAttributes.size(); 1059 for (int i = 0; i < N; i++) { 1060 if (i > 0) { 1061 summary.append(context.getString(R.string.notifications_summary_divider)); 1062 } 1063 summary.append(summaryAttributes.get(i)); 1064 } 1065 return summary.toString(); 1066 } 1067 1068 private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> { 1069 1070 @Override 1071 protected ProcStatsPackageEntry doInBackground(Void... params) { 1072 if (getActivity() == null) { 1073 return null; 1074 } 1075 if (mPackageInfo == null) { 1076 return null; 1077 } 1078 if (mStatsManager == null) { 1079 mStatsManager = new ProcStatsData(getActivity(), false); 1080 mStatsManager.setDuration(ProcessStatsBase.sDurations[0]); 1081 } 1082 mStatsManager.refreshStats(true); 1083 for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) { 1084 for (ProcStatsEntry entry : pkgEntry.mEntries) { 1085 if (entry.mUid == mPackageInfo.applicationInfo.uid) { 1086 pkgEntry.updateMetrics(); 1087 return pkgEntry; 1088 } 1089 } 1090 } 1091 return null; 1092 } 1093 1094 @Override 1095 protected void onPostExecute(ProcStatsPackageEntry entry) { 1096 if (getActivity() == null) { 1097 return; 1098 } 1099 if (entry != null) { 1100 mStats = entry; 1101 mMemoryPreference.setEnabled(true); 1102 double amount = Math.max(entry.mRunWeight, entry.mBgWeight) 1103 * mStatsManager.getMemInfo().weightToRam; 1104 mMemoryPreference.setSummary(getString(R.string.memory_use_summary, 1105 Formatter.formatShortFileSize(getContext(), (long) amount))); 1106 } else { 1107 mMemoryPreference.setEnabled(false); 1108 mMemoryPreference.setSummary(getString(R.string.no_memory_use_summary)); 1109 } 1110 } 1111 1112 } 1113 1114 private class BatteryUpdater extends AsyncTask<Void, Void, Void> { 1115 @Override 1116 protected Void doInBackground(Void... params) { 1117 mBatteryHelper.create((Bundle) null); 1118 mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, 1119 mUserManager.getUserProfiles()); 1120 List<BatterySipper> usageList = mBatteryHelper.getUsageList(); 1121 final int N = usageList.size(); 1122 for (int i = 0; i < N; i++) { 1123 BatterySipper sipper = usageList.get(i); 1124 if (sipper.getUid() == mPackageInfo.applicationInfo.uid) { 1125 mSipper = sipper; 1126 break; 1127 } 1128 } 1129 return null; 1130 } 1131 1132 @Override 1133 protected void onPostExecute(Void result) { 1134 if (getActivity() == null) { 1135 return; 1136 } 1137 refreshUi(); 1138 } 1139 } 1140 1141 private static class DisableChanger extends AsyncTask<Object, Object, Object> { 1142 final PackageManager mPm; 1143 final WeakReference<InstalledAppDetails> mActivity; 1144 final ApplicationInfo mInfo; 1145 final int mState; 1146 1147 DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) { 1148 mPm = activity.mPm; 1149 mActivity = new WeakReference<InstalledAppDetails>(activity); 1150 mInfo = info; 1151 mState = state; 1152 } 1153 1154 @Override 1155 protected Object doInBackground(Object... params) { 1156 mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0); 1157 return null; 1158 } 1159 } 1160 1161 private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() { 1162 1163 @Override 1164 public Loader<ChartData> onCreateLoader(int id, Bundle args) { 1165 return new ChartDataLoader(getActivity(), mStatsSession, args); 1166 } 1167 1168 @Override 1169 public void onLoadFinished(Loader<ChartData> loader, ChartData data) { 1170 mChartData = data; 1171 mDataPreference.setSummary(getDataSummary()); 1172 } 1173 1174 @Override 1175 public void onLoaderReset(Loader<ChartData> loader) { 1176 // Leave last result. 1177 } 1178 }; 1179 1180 private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() { 1181 @Override 1182 public void onReceive(Context context, Intent intent) { 1183 updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED); 1184 } 1185 }; 1186 1187 private final PermissionsResultCallback mPermissionCallback 1188 = new PermissionsResultCallback() { 1189 @Override 1190 public void onPermissionSummaryResult(int[] counts, CharSequence[] groupLabels) { 1191 if (getActivity() == null) { 1192 return; 1193 } 1194 mPermissionReceiver = null; 1195 final Resources res = getResources(); 1196 CharSequence summary = null; 1197 if (counts != null) { 1198 int totalCount = counts[1]; 1199 int additionalCounts = counts[2]; 1200 1201 if (totalCount == 0) { 1202 summary = res.getString( 1203 R.string.runtime_permissions_summary_no_permissions_requested); 1204 } else { 1205 final ArrayList<CharSequence> list = new ArrayList(Arrays.asList(groupLabels)); 1206 if (additionalCounts > 0) { 1207 // N additional permissions. 1208 list.add(res.getQuantityString( 1209 R.plurals.runtime_permissions_additional_count, 1210 additionalCounts, additionalCounts)); 1211 } 1212 if (list.size() == 0) { 1213 summary = res.getString( 1214 R.string.runtime_permissions_summary_no_permissions_granted); 1215 } else { 1216 summary = ListFormatter.getInstance().format(list); 1217 } 1218 } 1219 } 1220 mPermissionsPreference.setSummary(summary); 1221 } 1222 }; 1223} 1224