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