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