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