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