Files
app_Settings/src/com/android/settings/applications/InstalledAppDetails.java
Svetoslav Ganov 7f62addc10 Cleanup of the PackageInstaller API - Settings
The PackageInstaller app manages side-loading apps as well
as permission management. It should be updatable, hence
should rely on system APIs to talk to the platform. This
is the first step of defining an API boundary.

Change-Id: Ibb21114a7a9fa28f6db68492fe37c02c1b2f0c6b
2016-04-21 20:34:53 -07:00

1212 lines
51 KiB
Java
Executable File

/**
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package com.android.settings.applications;
import android.Manifest.permission;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.app.LoaderManager.LoaderCallbacks;
import android.app.Notification;
import android.app.admin.DevicePolicyManager;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.icu.text.ListFormatter;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.BatteryStats;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.notification.NotificationListenerService.Ranking;
import android.support.v7.preference.Preference;
import android.support.v7.preference.Preference.OnPreferenceClickListener;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceScreen;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.IWebViewUpdateService;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.android.internal.logging.MetricsProto.MetricsEvent;
import com.android.internal.os.BatterySipper;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.AppHeader;
import com.android.settings.DeviceAdminAdd;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.applications.PermissionsSummaryHelper.PermissionsResultCallback;
import com.android.settings.datausage.AppDataUsage;
import com.android.settings.datausage.DataUsageList;
import com.android.settings.datausage.DataUsageSummary;
import com.android.settings.fuelgauge.BatteryEntry;
import com.android.settings.fuelgauge.PowerUsageDetail;
import com.android.settings.notification.AppNotificationSettings;
import com.android.settings.notification.NotificationBackend;
import com.android.settings.notification.NotificationBackend.AppRow;
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.ChartDataLoader;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
/**
* Activity to display application information from Settings. This activity presents
* extended information associated with a package like code, data, total size, permissions
* used by the application and also the set of default launchable activities.
* For system applications, an option to clear user data is displayed only if data size is > 0.
* System applications that do not want clear user data do not have this option.
* For non-system applications, there is no option to clear data. Instead there is an option to
* uninstall the application.
*/
public class InstalledAppDetails extends AppInfoBase
implements View.OnClickListener, OnPreferenceClickListener {
private static final String LOG_TAG = "InstalledAppDetails";
// Menu identifiers
public static final int UNINSTALL_ALL_USERS_MENU = 1;
public static final int UNINSTALL_UPDATES = 2;
// Result code identifiers
public static final int REQUEST_UNINSTALL = 0;
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
private static final int SUB_INFO_FRAGMENT = 1;
private static final int LOADER_CHART_DATA = 2;
private static final int DLG_FORCE_STOP = DLG_BASE + 1;
private static final int DLG_DISABLE = DLG_BASE + 2;
private static final int DLG_SPECIAL_DISABLE = DLG_BASE + 3;
private static final String KEY_HEADER = "header_view";
private static final String KEY_NOTIFICATION = "notification_settings";
private static final String KEY_STORAGE = "storage_settings";
private static final String KEY_PERMISSION = "permission_settings";
private static final String KEY_DATA = "data_settings";
private static final String KEY_LAUNCH = "preferred_settings";
private static final String KEY_BATTERY = "battery";
private static final String KEY_MEMORY = "memory";
private static final String NOTIFICATION_TUNER_SETTING = "show_importance_slider";
private final HashSet<String> mHomePackages = new HashSet<String>();
private boolean mInitialized;
private boolean mShowUninstalled;
private LayoutPreference mHeader;
private Button mUninstallButton;
private boolean mUpdatedSysApp = false;
private Button mForceStopButton;
private Preference mNotificationPreference;
private Preference mStoragePreference;
private Preference mPermissionsPreference;
private Preference mLaunchPreference;
private Preference mDataPreference;
private Preference mMemoryPreference;
private boolean mDisableAfterUninstall;
// Used for updating notification preference.
private final NotificationBackend mBackend = new NotificationBackend();
private ChartData mChartData;
private INetworkStatsSession mStatsSession;
private Preference mBatteryPreference;
private BatteryStatsHelper mBatteryHelper;
private BatterySipper mSipper;
protected ProcStatsData mStatsManager;
protected ProcStatsPackageEntry mStats;
private boolean handleDisableable(Button button) {
boolean disableable = false;
// Try to prevent the user from bricking their phone
// by not allowing disabling of apps signed with the
// system cert and any launcher app in the system.
if (mHomePackages.contains(mAppEntry.info.packageName)
|| Utils.isSystemPackage(mPm, mPackageInfo)) {
// Disable button for core system applications.
button.setText(R.string.disable_text);
} else if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
button.setText(R.string.disable_text);
disableable = true;
} else {
button.setText(R.string.enable_text);
disableable = true;
}
return disableable;
}
private boolean isDisabledUntilUsed() {
return mAppEntry.info.enabledSetting
== PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
}
private void initUninstallButtons() {
final boolean isBundled = (mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
boolean enabled = true;
if (isBundled) {
enabled = handleDisableable(mUninstallButton);
} else {
if ((mPackageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0
&& mUserManager.getUsers().size() >= 2) {
// When we have multiple users, there is a separate menu
// to uninstall for all users.
enabled = false;
}
mUninstallButton.setText(R.string.uninstall_text);
}
// If this is a device admin, it can't be uninstalled or disabled.
// We do this here so the text of the button is still set correctly.
if (isBundled && mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
enabled = false;
}
// We don't allow uninstalling DO/PO on *any* users, because if it's a system app,
// "uninstall" is actually "downgrade to the system version + disable", and "downgrade"
// will clear data on all users.
if (isProfileOrDeviceOwner(mPackageInfo.packageName)) {
enabled = false;
}
// If the uninstall intent is already queued, disable the uninstall button
if (mDpm.isUninstallInQueue(mPackageName)) {
enabled = false;
}
// Home apps need special handling. Bundled ones we don't risk downgrading
// because that can interfere with home-key resolution. Furthermore, we
// can't allow uninstallation of the only home app, and we don't want to
// allow uninstallation of an explicitly preferred one -- the user can go
// to Home settings and pick a different one, after which we'll permit
// uninstallation of the now-not-default one.
if (enabled && mHomePackages.contains(mPackageInfo.packageName)) {
if (isBundled) {
enabled = false;
} else {
ArrayList<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
ComponentName currentDefaultHome = mPm.getHomeActivities(homeActivities);
if (currentDefaultHome == null) {
// No preferred default, so permit uninstall only when
// there is more than one candidate
enabled = (mHomePackages.size() > 1);
} else {
// There is an explicit default home app -- forbid uninstall of
// that one, but permit it for installed-but-inactive ones.
enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
}
}
}
if (mAppsControlDisallowedBySystem) {
enabled = false;
}
try {
IWebViewUpdateService webviewUpdateService =
IWebViewUpdateService.Stub.asInterface(ServiceManager.getService("webviewupdate"));
if (webviewUpdateService.isFallbackPackage(mAppEntry.info.packageName)) {
enabled = false;
}
} catch (RemoteException e) {
throw new RuntimeException(e);
}
mUninstallButton.setEnabled(enabled);
if (enabled) {
// Register listener
mUninstallButton.setOnClickListener(this);
}
}
/** Returns if the supplied package is device owner or profile owner of at least one user */
private boolean isProfileOrDeviceOwner(String packageName) {
List<UserInfo> userInfos = mUserManager.getUsers();
DevicePolicyManager dpm = (DevicePolicyManager)
getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
if (dpm.isDeviceOwnerAppOnAnyUser(packageName)) {
return true;
}
for (UserInfo userInfo : userInfos) {
ComponentName cn = dpm.getProfileOwnerAsUser(userInfo.id);
if (cn != null && cn.getPackageName().equals(packageName)) {
return true;
}
}
return false;
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setHasOptionsMenu(true);
addPreferencesFromResource(R.xml.installed_app_details);
addDynamicPrefs();
if (Utils.isBandwidthControlEnabled()) {
INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
try {
mStatsSession = statsService.openSession();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
} else {
removePreference(KEY_DATA);
}
mBatteryHelper = new BatteryStatsHelper(getActivity(), true);
}
@Override
protected int getMetricsCategory() {
return MetricsEvent.APPLICATIONS_INSTALLED_APP_DETAILS;
}
@Override
public void onResume() {
super.onResume();
if (mFinishing) {
return;
}
mState.requestSize(mPackageName, mUserId);
AppItem app = new AppItem(mAppEntry.info.uid);
app.addUid(mAppEntry.info.uid);
if (mStatsSession != null) {
getLoaderManager().restartLoader(LOADER_CHART_DATA,
ChartDataLoader.buildArgs(getTemplate(getContext()), app),
mDataCallbacks);
}
new BatteryUpdater().execute();
new MemoryUpdater().execute();
updateDynamicPrefs();
}
@Override
public void onPause() {
getLoaderManager().destroyLoader(LOADER_CHART_DATA);
super.onPause();
}
@Override
public void onDestroy() {
TrafficStats.closeQuietly(mStatsSession);
super.onDestroy();
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mFinishing) {
return;
}
handleHeader();
mNotificationPreference = findPreference(KEY_NOTIFICATION);
mNotificationPreference.setOnPreferenceClickListener(this);
mStoragePreference = findPreference(KEY_STORAGE);
mStoragePreference.setOnPreferenceClickListener(this);
mPermissionsPreference = findPreference(KEY_PERMISSION);
mPermissionsPreference.setOnPreferenceClickListener(this);
mDataPreference = findPreference(KEY_DATA);
if (mDataPreference != null) {
mDataPreference.setOnPreferenceClickListener(this);
}
mBatteryPreference = findPreference(KEY_BATTERY);
mBatteryPreference.setEnabled(false);
mBatteryPreference.setOnPreferenceClickListener(this);
mMemoryPreference = findPreference(KEY_MEMORY);
mMemoryPreference.setOnPreferenceClickListener(this);
mLaunchPreference = findPreference(KEY_LAUNCH);
if (mAppEntry != null && mAppEntry.info != null) {
if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 ||
!mAppEntry.info.enabled) {
mLaunchPreference.setEnabled(false);
} else {
mLaunchPreference.setOnPreferenceClickListener(this);
}
} else {
mLaunchPreference.setEnabled(false);
}
}
private void handleHeader() {
mHeader = (LayoutPreference) findPreference(KEY_HEADER);
// Get Control button panel
View btnPanel = mHeader.findViewById(R.id.control_buttons_panel);
mForceStopButton = (Button) btnPanel.findViewById(R.id.right_button);
mForceStopButton.setText(R.string.force_stop);
mUninstallButton = (Button) btnPanel.findViewById(R.id.left_button);
mForceStopButton.setEnabled(false);
View gear = mHeader.findViewById(R.id.gear);
Intent i = new Intent(Intent.ACTION_APPLICATION_PREFERENCES);
i.setPackage(mPackageName);
final Intent intent = resolveIntent(i);
if (intent != null) {
gear.setVisibility(View.VISIBLE);
gear.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
startActivity(intent);
}
});
} else {
gear.setVisibility(View.GONE);
}
}
private Intent resolveIntent(Intent i) {
ResolveInfo result = getContext().getPackageManager().resolveActivity(i, 0);
return result != null ? new Intent(i.getAction())
.setClassName(result.activityInfo.packageName, result.activityInfo.name) : null;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, UNINSTALL_UPDATES, 0, R.string.app_factory_reset)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.add(0, UNINSTALL_ALL_USERS_MENU, 1, R.string.uninstall_all_users_text)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
if (mFinishing) {
return;
}
boolean showIt = true;
if (mUpdatedSysApp) {
showIt = false;
} else if (mAppEntry == null) {
showIt = false;
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
showIt = false;
} else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
showIt = false;
} else if (UserHandle.myUserId() != 0) {
showIt = false;
} else if (mUserManager.getUsers().size() < 2) {
showIt = false;
}
menu.findItem(UNINSTALL_ALL_USERS_MENU).setVisible(showIt);
mUpdatedSysApp = (mAppEntry.info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
MenuItem uninstallUpdatesItem = menu.findItem(UNINSTALL_UPDATES);
uninstallUpdatesItem.setVisible(mUpdatedSysApp && !mAppsControlDisallowedBySystem);
if (uninstallUpdatesItem.isVisible()) {
RestrictedLockUtils.setMenuItemAsDisabledByAdmin(getActivity(),
uninstallUpdatesItem, mAppsControlDisallowedAdmin);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case UNINSTALL_ALL_USERS_MENU:
uninstallPkg(mAppEntry.info.packageName, true, false);
return true;
case UNINSTALL_UPDATES:
uninstallPkg(mAppEntry.info.packageName, false, false);
return true;
}
return false;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_UNINSTALL) {
if (mDisableAfterUninstall) {
mDisableAfterUninstall = false;
new DisableChanger(this, mAppEntry.info,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
.execute((Object)null);
}
if (!refreshUi()) {
setIntentAndFinish(true, true);
}
}
if (requestCode == REQUEST_REMOVE_DEVICE_ADMIN) {
if (!refreshUi()) {
setIntentAndFinish(true, true);
}
}
}
// Utility method to set application label and icon.
private void setAppLabelAndIcon(PackageInfo pkgInfo) {
final View appSnippet = mHeader.findViewById(R.id.app_snippet);
mState.ensureIcon(mAppEntry);
setupAppSnippet(appSnippet, mAppEntry.label, mAppEntry.icon,
pkgInfo != null ? pkgInfo.versionName : null);
}
private boolean signaturesMatch(String pkg1, String pkg2) {
if (pkg1 != null && pkg2 != null) {
try {
final int match = mPm.checkSignatures(pkg1, pkg2);
if (match >= PackageManager.SIGNATURE_MATCH) {
return true;
}
} catch (Exception e) {
// e.g. named alternate package not found during lookup;
// this is an expected case sometimes
}
}
return false;
}
@Override
protected boolean refreshUi() {
retrieveAppEntry();
if (mAppEntry == null) {
return false; // onCreate must have failed, make sure to exit
}
if (mPackageInfo == null) {
return false; // onCreate must have failed, make sure to exit
}
// Get list of "home" apps and trace through any meta-data references
List<ResolveInfo> homeActivities = new ArrayList<ResolveInfo>();
mPm.getHomeActivities(homeActivities);
mHomePackages.clear();
for (int i = 0; i< homeActivities.size(); i++) {
ResolveInfo ri = homeActivities.get(i);
final String activityPkg = ri.activityInfo.packageName;
mHomePackages.add(activityPkg);
// Also make sure to include anything proxying for the home app
final Bundle metadata = ri.activityInfo.metaData;
if (metadata != null) {
final String metaPkg = metadata.getString(ActivityManager.META_HOME_ALTERNATE);
if (signaturesMatch(metaPkg, activityPkg)) {
mHomePackages.add(metaPkg);
}
}
}
checkForceStop();
setAppLabelAndIcon(mPackageInfo);
initUninstallButtons();
// Update the preference summaries.
Activity context = getActivity();
mStoragePreference.setSummary(AppStorageSettings.getSummary(mAppEntry, context));
PermissionsSummaryHelper.getPermissionSummary(getContext(),
mPackageName, mPermissionCallback);
mLaunchPreference.setSummary(AppUtils.getLaunchByDefaultSummary(mAppEntry, mUsbManager,
mPm, context));
mNotificationPreference.setSummary(getNotificationSummary(mAppEntry, context,
mBackend));
if (mDataPreference != null) {
mDataPreference.setSummary(getDataSummary());
}
updateBattery();
if (!mInitialized) {
// First time init: are we displaying an uninstalled app?
mInitialized = true;
mShowUninstalled = (mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0;
} else {
// All other times: if the app no longer exists then we want
// to go away.
try {
ApplicationInfo ainfo = context.getPackageManager().getApplicationInfo(
mAppEntry.info.packageName, PackageManager.GET_UNINSTALLED_PACKAGES
| PackageManager.GET_DISABLED_COMPONENTS);
if (!mShowUninstalled) {
// If we did not start out with the app uninstalled, then
// it transitioning to the uninstalled state for the current
// user means we should go away as well.
return (ainfo.flags&ApplicationInfo.FLAG_INSTALLED) != 0;
}
} catch (NameNotFoundException e) {
return false;
}
}
return true;
}
private void updateBattery() {
if (mSipper != null) {
mBatteryPreference.setEnabled(true);
int dischargeAmount = mBatteryHelper.getStats().getDischargeAmount(
BatteryStats.STATS_SINCE_CHARGED);
final int percentOfMax = (int) ((mSipper.totalPowerMah)
/ mBatteryHelper.getTotalPower() * dischargeAmount + .5f);
mBatteryPreference.setSummary(getString(R.string.battery_summary, percentOfMax));
} else {
mBatteryPreference.setEnabled(false);
mBatteryPreference.setSummary(getString(R.string.no_battery_summary));
}
}
private CharSequence getDataSummary() {
if (mChartData != null) {
long totalBytes = mChartData.detail.getTotalBytes();
if (totalBytes == 0) {
return getString(R.string.no_data_usage);
}
Context context = getActivity();
return getString(R.string.data_summary_format,
Formatter.formatFileSize(context, totalBytes),
DateUtils.formatDateTime(context, mChartData.detail.getStart(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_MONTH));
}
return getString(R.string.computing_size);
}
@Override
protected AlertDialog createDialog(int id, int errorCode) {
switch (id) {
case DLG_DISABLE:
return new AlertDialog.Builder(getActivity())
.setMessage(getActivity().getText(R.string.app_disable_dlg_text))
.setPositiveButton(R.string.app_disable_dlg_positive,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Disable the app
new DisableChanger(InstalledAppDetails.this, mAppEntry.info,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER)
.execute((Object)null);
}
})
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case DLG_SPECIAL_DISABLE:
return new AlertDialog.Builder(getActivity())
.setMessage(getActivity().getText(R.string.app_disable_dlg_text))
.setPositiveButton(R.string.app_disable_dlg_positive,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Disable the app and ask for uninstall
uninstallPkg(mAppEntry.info.packageName,
false, true);
}
})
.setNegativeButton(R.string.dlg_cancel, null)
.create();
case DLG_FORCE_STOP:
return new AlertDialog.Builder(getActivity())
.setTitle(getActivity().getText(R.string.force_stop_dlg_title))
.setMessage(getActivity().getText(R.string.force_stop_dlg_text))
.setPositiveButton(R.string.dlg_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// Force stop
forceStopPackage(mAppEntry.info.packageName);
}
})
.setNegativeButton(R.string.dlg_cancel, null)
.create();
}
return null;
}
private void uninstallPkg(String packageName, boolean allUsers, boolean andDisable) {
// Create new intent to launch Uninstaller activity
Uri packageURI = Uri.parse("package:"+packageName);
Intent uninstallIntent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE, packageURI);
uninstallIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, allUsers);
startActivityForResult(uninstallIntent, REQUEST_UNINSTALL);
mDisableAfterUninstall = andDisable;
}
private void forceStopPackage(String pkgName) {
ActivityManager am = (ActivityManager) getActivity().getSystemService(
Context.ACTIVITY_SERVICE);
am.forceStopPackage(pkgName);
int userId = UserHandle.getUserId(mAppEntry.info.uid);
mState.invalidatePackage(pkgName, userId);
ApplicationsState.AppEntry newEnt = mState.getEntry(pkgName, userId);
if (newEnt != null) {
mAppEntry = newEnt;
}
checkForceStop();
}
private void updateForceStopButton(boolean enabled) {
if (mAppsControlDisallowedBySystem) {
mForceStopButton.setEnabled(false);
} else {
mForceStopButton.setEnabled(enabled);
mForceStopButton.setOnClickListener(InstalledAppDetails.this);
}
}
private void checkForceStop() {
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
// User can't force stop device admin.
updateForceStopButton(false);
} else if ((mAppEntry.info.flags&ApplicationInfo.FLAG_STOPPED) == 0) {
// If the app isn't explicitly stopped, then always show the
// force stop button.
updateForceStopButton(true);
} else {
Intent intent = new Intent(Intent.ACTION_QUERY_PACKAGE_RESTART,
Uri.fromParts("package", mAppEntry.info.packageName, null));
intent.putExtra(Intent.EXTRA_PACKAGES, new String[] { mAppEntry.info.packageName });
intent.putExtra(Intent.EXTRA_UID, mAppEntry.info.uid);
intent.putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(mAppEntry.info.uid));
getActivity().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null,
mCheckKillProcessesReceiver, null, Activity.RESULT_CANCELED, null, null);
}
}
private void startManagePermissionsActivity() {
// start new activity to manage app permissions
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName);
intent.putExtra(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS");
}
}
private void startAppInfoFragment(Class<?> fragment, CharSequence title) {
startAppInfoFragment(fragment, title, this, mAppEntry);
}
public static void startAppInfoFragment(Class<?> fragment, CharSequence title,
SettingsPreferenceFragment caller, AppEntry appEntry) {
// start new fragment to display extended information
Bundle args = new Bundle();
args.putString(ARG_PACKAGE_NAME, appEntry.info.packageName);
args.putInt(ARG_PACKAGE_UID, appEntry.info.uid);
args.putBoolean(AppHeader.EXTRA_HIDE_INFO_BUTTON, true);
SettingsActivity sa = (SettingsActivity) caller.getActivity();
sa.startPreferencePanel(fragment.getName(), args, -1, title, caller, SUB_INFO_FRAGMENT);
}
/*
* Method implementing functionality of buttons clicked
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
public void onClick(View v) {
if (mAppEntry == null) {
setIntentAndFinish(true, true);
return;
}
String packageName = mAppEntry.info.packageName;
if (v == mUninstallButton) {
if (mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
Activity activity = getActivity();
Intent uninstallDAIntent = new Intent(activity, DeviceAdminAdd.class);
uninstallDAIntent.putExtra(DeviceAdminAdd.EXTRA_DEVICE_ADMIN_PACKAGE_NAME,
mPackageName);
activity.startActivityForResult(uninstallDAIntent, REQUEST_REMOVE_DEVICE_ADMIN);
return;
}
EnforcedAdmin admin = RestrictedLockUtils.checkIfUninstallBlocked(getActivity(),
packageName, mUserId);
boolean uninstallBlockedBySystem = mAppsControlDisallowedBySystem ||
RestrictedLockUtils.hasBaseUserRestriction(getActivity(), packageName, mUserId);
if (admin != null && !uninstallBlockedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(), admin);
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
if (mAppEntry.info.enabled && !isDisabledUntilUsed()) {
// If the system app has an update and this is the only user on the device,
// then offer to downgrade the app, otherwise only offer to disable the
// app for this user.
if (mUpdatedSysApp && isSingleUser()) {
showDialogInner(DLG_SPECIAL_DISABLE, 0);
} else {
showDialogInner(DLG_DISABLE, 0);
}
} else {
new DisableChanger(this, mAppEntry.info,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
.execute((Object) null);
}
} else if ((mAppEntry.info.flags & ApplicationInfo.FLAG_INSTALLED) == 0) {
uninstallPkg(packageName, true, false);
} else {
uninstallPkg(packageName, false, false);
}
} else if (v == mForceStopButton) {
if (mAppsControlDisallowedAdmin != null && !mAppsControlDisallowedBySystem) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
getActivity(), mAppsControlDisallowedAdmin);
} else {
showDialogInner(DLG_FORCE_STOP, 0);
//forceStopPackage(mAppInfo.packageName);
}
}
}
/** Returns whether there is only one user on this device, not including the system-only user */
private boolean isSingleUser() {
final int userCount = mUserManager.getUserCount();
return userCount == 1
|| (mUserManager.isSplitSystemUser() && userCount == 2);
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == mStoragePreference) {
startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle());
} else if (preference == mNotificationPreference) {
startAppInfoFragment(AppNotificationSettings.class,
getString(R.string.app_notifications_title));
} else if (preference == mPermissionsPreference) {
startManagePermissionsActivity();
} else if (preference == mLaunchPreference) {
startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle());
} else if (preference == mMemoryPreference) {
ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(),
mStatsManager.getMemInfo(), mStats, false);
} else if (preference == mDataPreference) {
startAppInfoFragment(AppDataUsage.class, getString(R.string.app_data_usage));
} else if (preference == mBatteryPreference) {
BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper);
PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(),
mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true, false);
} else {
return false;
}
return true;
}
private void addDynamicPrefs() {
if (Utils.isManagedProfile(UserManager.get(getContext()))) {
return;
}
final PreferenceScreen screen = getPreferenceScreen();
if (DefaultHomePreference.hasHomePreference(mPackageName, getContext())) {
screen.addPreference(new ShortcutPreference(getPrefContext(),
AdvancedAppSettings.class, "default_home", R.string.home_app,
R.string.configure_apps));
}
if (DefaultBrowserPreference.hasBrowserPreference(mPackageName, getContext())) {
screen.addPreference(new ShortcutPreference(getPrefContext(),
AdvancedAppSettings.class, "default_browser", R.string.default_browser_title,
R.string.configure_apps));
}
if (DefaultPhonePreference.hasPhonePreference(mPackageName, getContext())) {
screen.addPreference(new ShortcutPreference(getPrefContext(),
AdvancedAppSettings.class, "default_phone_app", R.string.default_phone_title,
R.string.configure_apps));
}
if (DefaultEmergencyPreference.hasEmergencyPreference(mPackageName, getContext())) {
screen.addPreference(new ShortcutPreference(getPrefContext(),
AdvancedAppSettings.class, "default_emergency_app",
R.string.default_emergency_app, R.string.configure_apps));
}
if (DefaultSmsPreference.hasSmsPreference(mPackageName, getContext())) {
screen.addPreference(new ShortcutPreference(getPrefContext(),
AdvancedAppSettings.class, "default_sms_app", R.string.sms_application_title,
R.string.configure_apps));
}
boolean hasDrawOverOtherApps = hasPermission(permission.SYSTEM_ALERT_WINDOW);
boolean hasWriteSettings = hasPermission(permission.WRITE_SETTINGS);
if (hasDrawOverOtherApps || hasWriteSettings) {
PreferenceCategory category = new PreferenceCategory(getPrefContext());
category.setTitle(R.string.advanced_apps);
screen.addPreference(category);
if (hasDrawOverOtherApps) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.draw_overlay);
pref.setKey("system_alert_window");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startAppInfoFragment(DrawOverlayDetails.class,
getString(R.string.draw_overlay));
return true;
}
});
category.addPreference(pref);
}
if (hasWriteSettings) {
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.write_settings);
pref.setKey("write_settings_apps");
pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startAppInfoFragment(WriteSettingsDetails.class,
getString(R.string.write_settings));
return true;
}
});
category.addPreference(pref);
}
}
final String installerPackageName =
getContext().getPackageManager().getInstallerPackageName(mPackageName);
if (installerPackageName != null) {
final Intent intent = new Intent(Intent.ACTION_SHOW_APP_INFO)
.setPackage(installerPackageName);
final Intent result = resolveIntent(intent);
if (result != null) {
result.putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName);
PreferenceCategory category = new PreferenceCategory(getPrefContext());
category.setTitle(R.string.app_install_details_group_title);
screen.addPreference(category);
Preference pref = new Preference(getPrefContext());
pref.setTitle(R.string.app_install_details_title);
pref.setKey("app_info_store");
pref.setSummary(getString(R.string.app_install_details_summary, mAppEntry.label));
pref.setIntent(result);
category.addPreference(pref);
}
}
}
private boolean hasPermission(String permission) {
if (mPackageInfo == null || mPackageInfo.requestedPermissions == null) {
return false;
}
for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) {
if (mPackageInfo.requestedPermissions[i].equals(permission)) {
return true;
}
}
return false;
}
private void updateDynamicPrefs() {
Preference pref = findPreference("default_home");
if (pref != null) {
pref.setSummary(DefaultHomePreference.isHomeDefault(mPackageName, getContext())
? R.string.yes : R.string.no);
}
pref = findPreference("default_browser");
if (pref != null) {
pref.setSummary(DefaultBrowserPreference.isBrowserDefault(mPackageName, getContext())
? R.string.yes : R.string.no);
}
pref = findPreference("default_phone_app");
if (pref != null) {
pref.setSummary(DefaultPhonePreference.isPhoneDefault(mPackageName, getContext())
? R.string.yes : R.string.no);
}
pref = findPreference("default_emergency_app");
if (pref != null) {
pref.setSummary(DefaultEmergencyPreference.isEmergencyDefault(mPackageName,
getContext()) ? R.string.yes : R.string.no);
}
pref = findPreference("default_sms_app");
if (pref != null) {
pref.setSummary(DefaultSmsPreference.isSmsDefault(mPackageName, getContext())
? R.string.yes : R.string.no);
}
pref = findPreference("system_alert_window");
if (pref != null) {
pref.setSummary(DrawOverlayDetails.getSummary(getContext(), mAppEntry));
}
pref = findPreference("write_settings_apps");
if (pref != null) {
pref.setSummary(WriteSettingsDetails.getSummary(getContext(), mAppEntry));
}
}
public static void setupAppSnippet(View appSnippet, CharSequence label, Drawable icon,
CharSequence versionName) {
LayoutInflater.from(appSnippet.getContext()).inflate(R.layout.widget_text_views,
(ViewGroup) appSnippet.findViewById(android.R.id.widget_frame));
ImageView iconView = (ImageView) appSnippet.findViewById(android.R.id.icon);
iconView.setImageDrawable(icon);
// Set application name.
TextView labelView = (TextView) appSnippet.findViewById(android.R.id.title);
labelView.setText(label);
// Version number of application
TextView appVersion = (TextView) appSnippet.findViewById(R.id.widget_text1);
if (!TextUtils.isEmpty(versionName)) {
appVersion.setSelected(true);
appVersion.setVisibility(View.VISIBLE);
appVersion.setText(appSnippet.getContext().getString(R.string.version_text,
String.valueOf(versionName)));
} else {
appVersion.setVisibility(View.INVISIBLE);
}
}
public static NetworkTemplate getTemplate(Context context) {
if (DataUsageList.hasReadyMobileRadio(context)) {
return NetworkTemplate.buildTemplateMobileWildcard();
}
if (DataUsageSummary.hasWifiRadio(context)) {
return NetworkTemplate.buildTemplateWifiWildcard();
}
return NetworkTemplate.buildTemplateEthernet();
}
public static CharSequence getNotificationSummary(AppEntry appEntry, Context context) {
return getNotificationSummary(appEntry, context, new NotificationBackend());
}
public static CharSequence getNotificationSummary(AppEntry appEntry, Context context,
NotificationBackend backend) {
AppRow appRow = backend.loadAppRow(context, context.getPackageManager(), appEntry.info);
return getNotificationSummary(appRow, context);
}
public static CharSequence getNotificationSummary(AppRow appRow, Context context) {
boolean showSlider = Settings.Secure.getInt(
context.getContentResolver(), NOTIFICATION_TUNER_SETTING, 0) == 1;
List<String> summaryAttributes = new ArrayList<>();
StringBuffer summary = new StringBuffer();
if (showSlider) {
if (appRow.appImportance != Ranking.IMPORTANCE_UNSPECIFIED) {
summaryAttributes.add(context.getString(
R.string.notification_summary_level, appRow.appImportance));
}
} else {
if (appRow.banned) {
summaryAttributes.add(context.getString(R.string.notifications_disabled));
} else if (appRow.appImportance > Ranking.IMPORTANCE_NONE
&& appRow.appImportance < Ranking.IMPORTANCE_DEFAULT) {
summaryAttributes.add(context.getString(R.string.notifications_silenced));
}
}
final boolean lockscreenSecure = new LockPatternUtils(context).isSecure(
UserHandle.myUserId());
if (lockscreenSecure) {
if (appRow.appVisOverride == Notification.VISIBILITY_PRIVATE) {
summaryAttributes.add(context.getString(R.string.notifications_redacted));
} else if (appRow.appVisOverride == Notification.VISIBILITY_SECRET) {
summaryAttributes.add(context.getString(R.string.notifications_hidden));
}
}
if (appRow.appBypassDnd) {
summaryAttributes.add(context.getString(R.string.notifications_priority));
}
final int N = summaryAttributes.size();
for (int i = 0; i < N; i++) {
if (i > 0) {
summary.append(context.getString(R.string.notifications_summary_divider));
}
summary.append(summaryAttributes.get(i));
}
return summary.toString();
}
private class MemoryUpdater extends AsyncTask<Void, Void, ProcStatsPackageEntry> {
@Override
protected ProcStatsPackageEntry doInBackground(Void... params) {
if (getActivity() == null) {
return null;
}
if (mPackageInfo == null) {
return null;
}
if (mStatsManager == null) {
mStatsManager = new ProcStatsData(getActivity(), false);
mStatsManager.setDuration(ProcessStatsBase.sDurations[0]);
}
mStatsManager.refreshStats(true);
for (ProcStatsPackageEntry pkgEntry : mStatsManager.getEntries()) {
for (ProcStatsEntry entry : pkgEntry.mEntries) {
if (entry.mUid == mPackageInfo.applicationInfo.uid) {
pkgEntry.updateMetrics();
return pkgEntry;
}
}
}
return null;
}
@Override
protected void onPostExecute(ProcStatsPackageEntry entry) {
if (getActivity() == null) {
return;
}
if (entry != null) {
mStats = entry;
mMemoryPreference.setEnabled(true);
double amount = Math.max(entry.mRunWeight, entry.mBgWeight)
* mStatsManager.getMemInfo().weightToRam;
mMemoryPreference.setSummary(getString(R.string.memory_use_summary,
Formatter.formatShortFileSize(getContext(), (long) amount)));
} else {
mMemoryPreference.setEnabled(false);
mMemoryPreference.setSummary(getString(R.string.no_memory_use_summary));
}
}
}
private class BatteryUpdater extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
mBatteryHelper.create((Bundle) null);
mBatteryHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED,
mUserManager.getUserProfiles());
List<BatterySipper> usageList = mBatteryHelper.getUsageList();
final int N = usageList.size();
for (int i = 0; i < N; i++) {
BatterySipper sipper = usageList.get(i);
if (sipper.getUid() == mPackageInfo.applicationInfo.uid) {
mSipper = sipper;
break;
}
}
return null;
}
@Override
protected void onPostExecute(Void result) {
if (getActivity() == null) {
return;
}
refreshUi();
}
}
private static class DisableChanger extends AsyncTask<Object, Object, Object> {
final PackageManager mPm;
final WeakReference<InstalledAppDetails> mActivity;
final ApplicationInfo mInfo;
final int mState;
DisableChanger(InstalledAppDetails activity, ApplicationInfo info, int state) {
mPm = activity.mPm;
mActivity = new WeakReference<InstalledAppDetails>(activity);
mInfo = info;
mState = state;
}
@Override
protected Object doInBackground(Object... params) {
mPm.setApplicationEnabledSetting(mInfo.packageName, mState, 0);
return null;
}
}
private final LoaderCallbacks<ChartData> mDataCallbacks = new LoaderCallbacks<ChartData>() {
@Override
public Loader<ChartData> onCreateLoader(int id, Bundle args) {
return new ChartDataLoader(getActivity(), mStatsSession, args);
}
@Override
public void onLoadFinished(Loader<ChartData> loader, ChartData data) {
mChartData = data;
mDataPreference.setSummary(getDataSummary());
}
@Override
public void onLoaderReset(Loader<ChartData> loader) {
// Leave last result.
}
};
private final BroadcastReceiver mCheckKillProcessesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateForceStopButton(getResultCode() != Activity.RESULT_CANCELED);
}
};
private final PermissionsResultCallback mPermissionCallback
= new PermissionsResultCallback() {
@Override
public void onPermissionSummaryResult(int standardGrantedPermissionCount,
int requestedPermissionCount, int additionalGrantedPermissionCount,
List<CharSequence> grantedGroupLabels) {
if (getActivity() == null) {
return;
}
final Resources res = getResources();
CharSequence summary = null;
if (requestedPermissionCount == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_requested);
} else {
final ArrayList<CharSequence> list = new ArrayList<>(grantedGroupLabels);
if (additionalGrantedPermissionCount > 0) {
// N additional permissions.
list.add(res.getQuantityString(
R.plurals.runtime_permissions_additional_count,
additionalGrantedPermissionCount, additionalGrantedPermissionCount));
}
if (list.size() == 0) {
summary = res.getString(
R.string.runtime_permissions_summary_no_permissions_granted);
} else {
summary = ListFormatter.getInstance().format(list);
}
}
mPermissionsPreference.setSummary(summary);
}
};
}