Reusing the 'Background activity' switch found in App Info. The switch now needs to be shown for all apps and will toggle another app op RUN_ANY_IN_BACKGROUND which controls whether jobs or alarms are run for background apps. Also fixed handling of multiple packages with shared uid. The controller was picking the first package for uid but the order of packages can change on a reboot which would cause wrong app ops settings across packages of the same uid. Test: make -j32 RunSettingsRoboTests Bug: 65176793 Change-Id: I2a9b96bc02730776172c3ae317cb7f7f890bec30
378 lines
16 KiB
Java
378 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2017 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.fuelgauge;
|
|
|
|
import android.annotation.UserIdInt;
|
|
import android.app.Activity;
|
|
import android.app.ActivityManager;
|
|
import android.app.LoaderManager;
|
|
import android.app.admin.DevicePolicyManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.Loader;
|
|
import android.content.pm.PackageManager;
|
|
import android.os.BatteryStats;
|
|
import android.os.Bundle;
|
|
import android.os.UserHandle;
|
|
import android.os.UserManager;
|
|
import android.support.annotation.VisibleForTesting;
|
|
import android.support.v14.preference.PreferenceFragment;
|
|
import android.support.v7.preference.Preference;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
|
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
|
import com.android.internal.os.BatterySipper;
|
|
import com.android.internal.os.BatteryStatsHelper;
|
|
import com.android.internal.util.ArrayUtils;
|
|
import com.android.settings.R;
|
|
import com.android.settings.SettingsActivity;
|
|
import com.android.settings.Utils;
|
|
import com.android.settings.applications.LayoutPreference;
|
|
import com.android.settings.dashboard.DashboardFragment;
|
|
import com.android.settings.enterprise.DevicePolicyManagerWrapper;
|
|
import com.android.settings.enterprise.DevicePolicyManagerWrapperImpl;
|
|
import com.android.settings.fuelgauge.anomaly.Anomaly;
|
|
import com.android.settings.fuelgauge.anomaly.AnomalyDialogFragment;
|
|
import com.android.settings.fuelgauge.anomaly.AnomalyLoader;
|
|
import com.android.settings.fuelgauge.anomaly.AnomalySummaryPreferenceController;
|
|
import com.android.settings.widget.EntityHeaderController;
|
|
import com.android.settingslib.applications.AppUtils;
|
|
import com.android.settingslib.applications.ApplicationsState;
|
|
import com.android.settingslib.core.AbstractPreferenceController;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* Power usage detail fragment for each app, this fragment contains
|
|
*
|
|
* 1. Detail battery usage information for app(i.e. usage time, usage amount)
|
|
* 2. Battery related controls for app(i.e uninstall, force stop)
|
|
*/
|
|
public class AdvancedPowerUsageDetail extends DashboardFragment implements
|
|
ButtonActionDialogFragment.AppButtonsDialogListener,
|
|
AnomalyDialogFragment.AnomalyDialogListener,
|
|
LoaderManager.LoaderCallbacks<List<Anomaly>>,
|
|
BackgroundActivityPreferenceController.WarningConfirmationListener {
|
|
|
|
public static final String TAG = "AdvancedPowerUsageDetail";
|
|
public static final String EXTRA_UID = "extra_uid";
|
|
public static final String EXTRA_PACKAGE_NAME = "extra_package_name";
|
|
public static final String EXTRA_FOREGROUND_TIME = "extra_foreground_time";
|
|
public static final String EXTRA_BACKGROUND_TIME = "extra_background_time";
|
|
public static final String EXTRA_LABEL = "extra_label";
|
|
public static final String EXTRA_ICON_ID = "extra_icon_id";
|
|
public static final String EXTRA_POWER_USAGE_PERCENT = "extra_power_usage_percent";
|
|
public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount";
|
|
public static final String EXTRA_ANOMALY_LIST = "extra_anomaly_list";
|
|
|
|
private static final String KEY_PREF_FOREGROUND = "app_usage_foreground";
|
|
private static final String KEY_PREF_BACKGROUND = "app_usage_background";
|
|
private static final String KEY_PREF_POWER_USAGE = "app_power_usage";
|
|
private static final String KEY_PREF_HEADER = "header_view";
|
|
|
|
private static final int REQUEST_UNINSTALL = 0;
|
|
private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1;
|
|
|
|
private static final int ANOMALY_LOADER = 0;
|
|
|
|
@VisibleForTesting
|
|
LayoutPreference mHeaderPreference;
|
|
@VisibleForTesting
|
|
ApplicationsState mState;
|
|
@VisibleForTesting
|
|
ApplicationsState.AppEntry mAppEntry;
|
|
@VisibleForTesting
|
|
BatteryUtils mBatteryUtils;
|
|
|
|
@VisibleForTesting
|
|
Preference mForegroundPreference;
|
|
@VisibleForTesting
|
|
Preference mBackgroundPreference;
|
|
@VisibleForTesting
|
|
Preference mPowerUsagePreference;
|
|
@VisibleForTesting
|
|
AnomalySummaryPreferenceController mAnomalySummaryPreferenceController;
|
|
private AppButtonsPreferenceController mAppButtonsPreferenceController;
|
|
private BackgroundActivityPreferenceController mBackgroundActivityPreferenceController;
|
|
|
|
private DevicePolicyManagerWrapper mDpm;
|
|
private UserManager mUserManager;
|
|
private PackageManager mPackageManager;
|
|
private List<Anomaly> mAnomalies;
|
|
private String mPackageName;
|
|
|
|
@VisibleForTesting
|
|
static void startBatteryDetailPage(SettingsActivity caller, BatteryUtils batteryUtils,
|
|
PreferenceFragment fragment, BatteryStatsHelper helper, int which, BatteryEntry entry,
|
|
String usagePercent, List<Anomaly> anomalies) {
|
|
// Initialize mStats if necessary.
|
|
helper.getStats();
|
|
|
|
final Bundle args = new Bundle();
|
|
final BatterySipper sipper = entry.sipper;
|
|
final BatteryStats.Uid uid = sipper.uidObj;
|
|
final boolean isTypeApp = sipper.drainType == BatterySipper.DrainType.APP;
|
|
|
|
final long foregroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
|
|
BatteryUtils.StatusType.FOREGROUND, uid, which) : sipper.usageTimeMs;
|
|
final long backgroundTimeMs = isTypeApp ? batteryUtils.getProcessTimeMs(
|
|
BatteryUtils.StatusType.BACKGROUND, uid, which) : 0;
|
|
|
|
if (ArrayUtils.isEmpty(sipper.mPackages)) {
|
|
// populate data for system app
|
|
args.putString(EXTRA_LABEL, entry.getLabel());
|
|
args.putInt(EXTRA_ICON_ID, entry.iconId);
|
|
args.putString(EXTRA_PACKAGE_NAME, null);
|
|
} else {
|
|
// populate data for normal app
|
|
args.putString(EXTRA_PACKAGE_NAME, entry.defaultPackageName != null
|
|
? entry.defaultPackageName
|
|
: sipper.mPackages[0]);
|
|
}
|
|
|
|
args.putInt(EXTRA_UID, sipper.getUid());
|
|
args.putLong(EXTRA_BACKGROUND_TIME, backgroundTimeMs);
|
|
args.putLong(EXTRA_FOREGROUND_TIME, foregroundTimeMs);
|
|
args.putString(EXTRA_POWER_USAGE_PERCENT, usagePercent);
|
|
args.putInt(EXTRA_POWER_USAGE_AMOUNT, (int) sipper.totalPowerMah);
|
|
args.putParcelableList(EXTRA_ANOMALY_LIST, anomalies);
|
|
|
|
caller.startPreferencePanelAsUser(fragment, AdvancedPowerUsageDetail.class.getName(), args,
|
|
R.string.battery_details_title, null,
|
|
new UserHandle(getUserIdToLaunchAdvancePowerUsageDetail(sipper)));
|
|
}
|
|
|
|
private static @UserIdInt int getUserIdToLaunchAdvancePowerUsageDetail(BatterySipper bs) {
|
|
if (bs.drainType == BatterySipper.DrainType.USER) {
|
|
return ActivityManager.getCurrentUser();
|
|
}
|
|
return UserHandle.getUserId(bs.getUid());
|
|
}
|
|
|
|
public static void startBatteryDetailPage(SettingsActivity caller, PreferenceFragment fragment,
|
|
BatteryStatsHelper helper, int which, BatteryEntry entry, String usagePercent,
|
|
List<Anomaly> anomalies) {
|
|
startBatteryDetailPage(caller, BatteryUtils.getInstance(caller), fragment, helper, which,
|
|
entry, usagePercent, anomalies);
|
|
}
|
|
|
|
public static void startBatteryDetailPage(SettingsActivity caller, PreferenceFragment fragment,
|
|
String packageName) {
|
|
final Bundle args = new Bundle(3);
|
|
final PackageManager packageManager = caller.getPackageManager();
|
|
args.putString(EXTRA_PACKAGE_NAME, packageName);
|
|
args.putString(EXTRA_POWER_USAGE_PERCENT, Utils.formatPercentage(0));
|
|
try {
|
|
args.putInt(EXTRA_UID, packageManager.getPackageUid(packageName, 0 /* no flag */));
|
|
} catch (PackageManager.NameNotFoundException e) {
|
|
Log.e(TAG, "Cannot find package: " + packageName, e);
|
|
}
|
|
|
|
caller.startPreferencePanelAsUser(fragment, AdvancedPowerUsageDetail.class.getName(), args,
|
|
R.string.battery_details_title, null, new UserHandle(UserHandle.myUserId()));
|
|
}
|
|
|
|
@Override
|
|
public void onAttach(Activity activity) {
|
|
super.onAttach(activity);
|
|
|
|
mState = ApplicationsState.getInstance(getActivity().getApplication());
|
|
mDpm = new DevicePolicyManagerWrapperImpl(
|
|
(DevicePolicyManager) activity.getSystemService(Context.DEVICE_POLICY_SERVICE));
|
|
mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
|
|
mPackageManager = activity.getPackageManager();
|
|
mBatteryUtils = BatteryUtils.getInstance(getContext());
|
|
}
|
|
|
|
@Override
|
|
public void onCreate(Bundle icicle) {
|
|
super.onCreate(icicle);
|
|
|
|
mPackageName = getArguments().getString(EXTRA_PACKAGE_NAME);
|
|
mAnomalySummaryPreferenceController = new AnomalySummaryPreferenceController(
|
|
(SettingsActivity) getActivity(), this, MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL);
|
|
mForegroundPreference = findPreference(KEY_PREF_FOREGROUND);
|
|
mBackgroundPreference = findPreference(KEY_PREF_BACKGROUND);
|
|
mPowerUsagePreference = findPreference(KEY_PREF_POWER_USAGE);
|
|
mHeaderPreference = (LayoutPreference) findPreference(KEY_PREF_HEADER);
|
|
|
|
if (mPackageName != null) {
|
|
mAppEntry = mState.getEntry(mPackageName, UserHandle.myUserId());
|
|
initAnomalyInfo();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onResume() {
|
|
super.onResume();
|
|
|
|
initHeader();
|
|
initPreference();
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void initAnomalyInfo() {
|
|
mAnomalies = getArguments().getParcelableArrayList(EXTRA_ANOMALY_LIST);
|
|
if (mAnomalies == null) {
|
|
getLoaderManager().initLoader(ANOMALY_LOADER, Bundle.EMPTY, this);
|
|
} else if (mAnomalies != null) {
|
|
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(mAnomalies);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void initHeader() {
|
|
final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header);
|
|
final Activity context = getActivity();
|
|
final Bundle bundle = getArguments();
|
|
EntityHeaderController controller = EntityHeaderController
|
|
.newInstance(context, this, appSnippet)
|
|
.setRecyclerView(getListView(), getLifecycle())
|
|
.setButtonActions(EntityHeaderController.ActionType.ACTION_NONE,
|
|
EntityHeaderController.ActionType.ACTION_NONE);
|
|
|
|
if (mAppEntry == null) {
|
|
controller.setLabel(bundle.getString(EXTRA_LABEL));
|
|
|
|
final int iconId = bundle.getInt(EXTRA_ICON_ID, 0);
|
|
if (iconId == 0) {
|
|
controller.setIcon(context.getPackageManager().getDefaultActivityIcon());
|
|
} else {
|
|
controller.setIcon(context.getDrawable(bundle.getInt(EXTRA_ICON_ID)));
|
|
}
|
|
} else {
|
|
mState.ensureIcon(mAppEntry);
|
|
controller.setLabel(mAppEntry);
|
|
controller.setIcon(mAppEntry);
|
|
boolean isInstantApp = AppUtils.isInstant(mAppEntry.info);
|
|
CharSequence summary = isInstantApp
|
|
? null : getString(Utils.getInstallationStatus(mAppEntry.info));
|
|
controller.setIsInstantApp(AppUtils.isInstant(mAppEntry.info));
|
|
controller.setSummary(summary);
|
|
}
|
|
|
|
controller.done(context, true /* rebindActions */);
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void initPreference() {
|
|
final Bundle bundle = getArguments();
|
|
final Context context = getContext();
|
|
|
|
final long foregroundTimeMs = bundle.getLong(EXTRA_FOREGROUND_TIME);
|
|
final long backgroundTimeMs = bundle.getLong(EXTRA_BACKGROUND_TIME);
|
|
final String usagePercent = bundle.getString(EXTRA_POWER_USAGE_PERCENT);
|
|
final int powerMah = bundle.getInt(EXTRA_POWER_USAGE_AMOUNT);
|
|
mForegroundPreference.setSummary(
|
|
TextUtils.expandTemplate(getText(R.string.battery_used_for),
|
|
Utils.formatElapsedTime(context, foregroundTimeMs, false)));
|
|
mBackgroundPreference.setSummary(
|
|
TextUtils.expandTemplate(getText(R.string.battery_active_for),
|
|
Utils.formatElapsedTime(context, backgroundTimeMs, false)));
|
|
mPowerUsagePreference.setSummary(
|
|
getString(R.string.battery_detail_power_percentage, usagePercent, powerMah));
|
|
}
|
|
|
|
@Override
|
|
public boolean onPreferenceTreeClick(Preference preference) {
|
|
if (TextUtils.equals(preference.getKey(), AnomalySummaryPreferenceController.ANOMALY_KEY)) {
|
|
mAnomalySummaryPreferenceController.onPreferenceTreeClick(preference);
|
|
return true;
|
|
}
|
|
return super.onPreferenceTreeClick(preference);
|
|
}
|
|
|
|
@Override
|
|
public int getMetricsCategory() {
|
|
return MetricsEvent.FUELGAUGE_POWER_USAGE_DETAIL;
|
|
}
|
|
|
|
@Override
|
|
protected String getLogTag() {
|
|
return TAG;
|
|
}
|
|
|
|
@Override
|
|
protected int getPreferenceScreenResId() {
|
|
return R.xml.power_usage_detail;
|
|
}
|
|
|
|
@Override
|
|
protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
|
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
|
final Bundle bundle = getArguments();
|
|
final int uid = bundle.getInt(EXTRA_UID, 0);
|
|
final String packageName = bundle.getString(EXTRA_PACKAGE_NAME);
|
|
|
|
mBackgroundActivityPreferenceController = new BackgroundActivityPreferenceController(
|
|
context, this, uid, packageName);
|
|
controllers.add(mBackgroundActivityPreferenceController);
|
|
controllers.add(new BatteryOptimizationPreferenceController(
|
|
(SettingsActivity) getActivity(), this, packageName));
|
|
mAppButtonsPreferenceController = new AppButtonsPreferenceController(
|
|
(SettingsActivity) getActivity(), this, getLifecycle(), packageName, mState, mDpm,
|
|
mUserManager, mPackageManager, REQUEST_UNINSTALL, REQUEST_REMOVE_DEVICE_ADMIN);
|
|
controllers.add(mAppButtonsPreferenceController);
|
|
|
|
return controllers;
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
|
super.onActivityResult(requestCode, resultCode, data);
|
|
if (mAppButtonsPreferenceController != null) {
|
|
mAppButtonsPreferenceController.handleActivityResult(requestCode, resultCode, data);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleDialogClick(int id) {
|
|
if (mAppButtonsPreferenceController != null) {
|
|
mAppButtonsPreferenceController.handleDialogClick(id);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onAnomalyHandled(Anomaly anomaly) {
|
|
mAnomalySummaryPreferenceController.hideHighUsagePreference();
|
|
}
|
|
|
|
@Override
|
|
public Loader<List<Anomaly>> onCreateLoader(int id, Bundle args) {
|
|
return new AnomalyLoader(getContext(), mPackageName);
|
|
}
|
|
|
|
@Override
|
|
public void onLoadFinished(Loader<List<Anomaly>> loader, List<Anomaly> data) {
|
|
mAnomalySummaryPreferenceController.updateAnomalySummaryPreference(data);
|
|
}
|
|
|
|
@Override
|
|
public void onLoaderReset(Loader<List<Anomaly>> loader) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void onLimitBackgroundActivity() {
|
|
mBackgroundActivityPreferenceController.setUnchecked(
|
|
findPreference(mBackgroundActivityPreferenceController.getPreferenceKey()));
|
|
}
|
|
}
|