Clean up BatteryAppListPreferenceController
Bug: 256123455 Bug: 258576047 Fix: 258576047 Test: presubmit Change-Id: I8c3d05f76e7a6995fccc34b2cc60ee126bb3d350
This commit is contained in:
@@ -132,7 +132,7 @@ public class AppBatteryPreferenceController extends BasePreferenceController
|
|||||||
if (isBatteryStatsAvailable()) {
|
if (isBatteryStatsAvailable()) {
|
||||||
final UserManager userManager =
|
final UserManager userManager =
|
||||||
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||||
final BatteryEntry entry = new BatteryEntry(mContext, /* handler */null, userManager,
|
final BatteryEntry entry = new BatteryEntry(mContext, userManager,
|
||||||
mUidBatteryConsumer, /* isHidden */ false,
|
mUidBatteryConsumer, /* isHidden */ false,
|
||||||
mUidBatteryConsumer.getUid(), /* packages */ null, mPackageName);
|
mUidBatteryConsumer.getUid(), /* packages */ null, mPackageName);
|
||||||
Log.i(TAG, "Battery consumer available, launch : "
|
Log.i(TAG, "Battery consumer available, launch : "
|
||||||
|
@@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.os.BatteryConsumer;
|
|
||||||
import android.os.BatteryStats;
|
import android.os.BatteryStats;
|
||||||
import android.os.BatteryStatsManager;
|
import android.os.BatteryStatsManager;
|
||||||
import android.os.BatteryUsageStats;
|
import android.os.BatteryUsageStats;
|
||||||
@@ -200,24 +199,6 @@ public class BatteryUtils {
|
|||||||
: uid < 0 || isHiddenSystemModule(packages);
|
: uid < 0 || isHiddenSystemModule(packages);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the specified device power component should be excluded from the summary
|
|
||||||
* battery consumption list.
|
|
||||||
*/
|
|
||||||
public boolean shouldHideDevicePowerComponent(BatteryConsumer consumer,
|
|
||||||
@BatteryConsumer.PowerComponent int powerComponentId) {
|
|
||||||
switch (powerComponentId) {
|
|
||||||
case BatteryConsumer.POWER_COMPONENT_IDLE:
|
|
||||||
case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
|
|
||||||
case BatteryConsumer.POWER_COMPONENT_SCREEN:
|
|
||||||
case BatteryConsumer.POWER_COMPONENT_BLUETOOTH:
|
|
||||||
case BatteryConsumer.POWER_COMPONENT_WIFI:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if one the specified packages belongs to a hidden system module.
|
* Returns true if one the specified packages belongs to a hidden system module.
|
||||||
*/
|
*/
|
||||||
|
@@ -1,549 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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.batteryusage;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.AggregateBatteryConsumer;
|
|
||||||
import android.os.BatteryConsumer;
|
|
||||||
import android.os.BatteryUsageStats;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.os.UidBatteryConsumer;
|
|
||||||
import android.os.UserBatteryConsumer;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
import android.util.ArrayMap;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.SparseArray;
|
|
||||||
|
|
||||||
import androidx.annotation.VisibleForTesting;
|
|
||||||
import androidx.preference.Preference;
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
import androidx.preference.PreferenceScreen;
|
|
||||||
|
|
||||||
import com.android.internal.os.PowerProfile;
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.core.PreferenceControllerMixin;
|
|
||||||
import com.android.settings.fuelgauge.AdvancedPowerUsageDetail;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settingslib.core.AbstractPreferenceController;
|
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
|
||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnDestroy;
|
|
||||||
import com.android.settingslib.core.lifecycle.events.OnPause;
|
|
||||||
import com.android.settingslib.utils.StringUtil;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controller that update the battery header view
|
|
||||||
*/
|
|
||||||
public class BatteryAppListPreferenceController extends AbstractPreferenceController
|
|
||||||
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy {
|
|
||||||
private static final String TAG = "BatteryAppListPreferenceController";
|
|
||||||
@VisibleForTesting
|
|
||||||
static final boolean USE_FAKE_DATA = false;
|
|
||||||
private static final int MAX_ITEMS_TO_LIST = USE_FAKE_DATA ? 30 : 20;
|
|
||||||
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
|
|
||||||
private static final String MEDIASERVER_PACKAGE_NAME = "mediaserver";
|
|
||||||
private static final String NOT_AVAILABLE = "not_available";
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
PreferenceGroup mAppListGroup;
|
|
||||||
private BatteryUsageStats mBatteryUsageStats;
|
|
||||||
private ArrayMap<String, Preference> mPreferenceCache;
|
|
||||||
@VisibleForTesting
|
|
||||||
BatteryUtils mBatteryUtils;
|
|
||||||
private final UserManager mUserManager;
|
|
||||||
private final PackageManager mPackageManager;
|
|
||||||
private final SettingsActivity mActivity;
|
|
||||||
private final InstrumentedPreferenceFragment mFragment;
|
|
||||||
private final String mPreferenceKey;
|
|
||||||
|
|
||||||
private Context mPrefContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Battery attribution list configuration.
|
|
||||||
*/
|
|
||||||
public interface Config {
|
|
||||||
/**
|
|
||||||
* Returns true if the attribution list should be shown.
|
|
||||||
*/
|
|
||||||
boolean shouldShowBatteryAttributionList(Context context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
static Config sConfig = new Config() {
|
|
||||||
@Override
|
|
||||||
public boolean shouldShowBatteryAttributionList(Context context) {
|
|
||||||
if (USE_FAKE_DATA) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerProfile powerProfile = new PowerProfile(context);
|
|
||||||
// Cheap hack to try to figure out if the power_profile.xml was populated.
|
|
||||||
final double averagePowerForOrdinal = powerProfile.getAveragePowerForOrdinal(
|
|
||||||
PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
|
|
||||||
final boolean shouldShowBatteryAttributionList =
|
|
||||||
averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
|
|
||||||
if (!shouldShowBatteryAttributionList) {
|
|
||||||
Log.w(TAG, "shouldShowBatteryAttributionList(): " + averagePowerForOrdinal);
|
|
||||||
}
|
|
||||||
return shouldShowBatteryAttributionList;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(Message msg) {
|
|
||||||
switch (msg.what) {
|
|
||||||
case BatteryEntry.MSG_UPDATE_NAME_ICON:
|
|
||||||
BatteryEntry entry = (BatteryEntry) msg.obj;
|
|
||||||
PowerGaugePreference pgp = mAppListGroup.findPreference(entry.getKey());
|
|
||||||
if (pgp != null) {
|
|
||||||
final int userId = UserHandle.getUserId(entry.getUid());
|
|
||||||
final UserHandle userHandle = new UserHandle(userId);
|
|
||||||
pgp.setIcon(mUserManager.getBadgedIconForUser(entry.getIcon(), userHandle));
|
|
||||||
pgp.setTitle(entry.mName);
|
|
||||||
if (entry.isAppEntry()) {
|
|
||||||
pgp.setContentDescription(entry.mName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BatteryEntry.MSG_REPORT_FULLY_DRAWN:
|
|
||||||
Activity activity = mActivity;
|
|
||||||
if (activity != null) {
|
|
||||||
activity.reportFullyDrawn();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
super.handleMessage(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public BatteryAppListPreferenceController(Context context, String preferenceKey,
|
|
||||||
Lifecycle lifecycle, SettingsActivity activity,
|
|
||||||
InstrumentedPreferenceFragment fragment) {
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
if (lifecycle != null) {
|
|
||||||
lifecycle.addObserver(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreferenceKey = preferenceKey;
|
|
||||||
mBatteryUtils = BatteryUtils.getInstance(context);
|
|
||||||
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
|
|
||||||
mPackageManager = context.getPackageManager();
|
|
||||||
mActivity = activity;
|
|
||||||
mFragment = fragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
BatteryEntry.stopRequestQueue();
|
|
||||||
mHandler.removeMessages(BatteryEntry.MSG_UPDATE_NAME_ICON);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
if (mActivity.isChangingConfigurations()) {
|
|
||||||
BatteryEntry.clearUidCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void displayPreference(PreferenceScreen screen) {
|
|
||||||
super.displayPreference(screen);
|
|
||||||
mPrefContext = screen.getContext();
|
|
||||||
mAppListGroup = screen.findPreference(mPreferenceKey);
|
|
||||||
mAppListGroup.setTitle(mPrefContext.getString(R.string.power_usage_list_summary));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPreferenceKey() {
|
|
||||||
return mPreferenceKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean handlePreferenceTreeClick(Preference preference) {
|
|
||||||
if (preference instanceof PowerGaugePreference) {
|
|
||||||
PowerGaugePreference pgp = (PowerGaugePreference) preference;
|
|
||||||
BatteryEntry entry = pgp.getInfo();
|
|
||||||
AdvancedPowerUsageDetail.startBatteryDetailPage(mActivity,
|
|
||||||
mFragment, entry, pgp.getPercent(), /*isValidToShowSummary=*/ true);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Refreshes the list of battery consumers using the supplied BatteryUsageStats.
|
|
||||||
*/
|
|
||||||
public void refreshAppListGroup(BatteryUsageStats batteryUsageStats, boolean showAllApps) {
|
|
||||||
if (!isAvailable()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
|
|
||||||
mAppListGroup.setTitle(R.string.power_usage_list_summary);
|
|
||||||
|
|
||||||
boolean addedSome = false;
|
|
||||||
|
|
||||||
cacheRemoveAllPrefs(mAppListGroup);
|
|
||||||
mAppListGroup.setOrderingAsAdded(false);
|
|
||||||
|
|
||||||
if (sConfig.shouldShowBatteryAttributionList(mContext)) {
|
|
||||||
final int dischargePercentage = getDischargePercentage(batteryUsageStats);
|
|
||||||
final List<BatteryEntry> usageList =
|
|
||||||
getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ true);
|
|
||||||
final double totalPower = batteryUsageStats.getConsumedPower();
|
|
||||||
final int numSippers = usageList.size();
|
|
||||||
for (int i = 0; i < numSippers; i++) {
|
|
||||||
final BatteryEntry entry = usageList.get(i);
|
|
||||||
|
|
||||||
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
|
|
||||||
entry.getConsumedPower(), totalPower, dischargePercentage);
|
|
||||||
|
|
||||||
if (((int) (percentOfTotal + .5)) < 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int uid = entry.getUid();
|
|
||||||
final UserHandle userHandle = new UserHandle(UserHandle.getUserId(uid));
|
|
||||||
final Drawable badgedIcon = mUserManager.getBadgedIconForUser(entry.getIcon(),
|
|
||||||
userHandle);
|
|
||||||
final CharSequence contentDescription = mUserManager.getBadgedLabelForUser(
|
|
||||||
entry.getLabel(), userHandle);
|
|
||||||
|
|
||||||
final String key = entry.getKey();
|
|
||||||
PowerGaugePreference pref = (PowerGaugePreference) getCachedPreference(key);
|
|
||||||
if (pref == null) {
|
|
||||||
pref = new PowerGaugePreference(mPrefContext, badgedIcon,
|
|
||||||
contentDescription, entry);
|
|
||||||
pref.setKey(key);
|
|
||||||
}
|
|
||||||
entry.mPercent = percentOfTotal;
|
|
||||||
pref.setTitle(entry.getLabel());
|
|
||||||
pref.setOrder(i + 1);
|
|
||||||
pref.setPercent(percentOfTotal);
|
|
||||||
pref.shouldShowAnomalyIcon(false);
|
|
||||||
pref.setEnabled(uid != BatteryUtils.UID_TETHERING
|
|
||||||
&& uid != BatteryUtils.UID_REMOVED_APPS);
|
|
||||||
setUsageSummary(pref, entry);
|
|
||||||
addedSome = true;
|
|
||||||
mAppListGroup.addPreference(pref);
|
|
||||||
if (mAppListGroup.getPreferenceCount() - getCachedCount()
|
|
||||||
> (MAX_ITEMS_TO_LIST + 1)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!addedSome) {
|
|
||||||
addNotAvailableMessage();
|
|
||||||
}
|
|
||||||
removeCachedPrefs(mAppListGroup);
|
|
||||||
|
|
||||||
BatteryEntry.startRequestQueue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the BatteryEntry list by using the supplied BatteryUsageStats.
|
|
||||||
*/
|
|
||||||
public List<BatteryEntry> getBatteryEntryList(
|
|
||||||
BatteryUsageStats batteryUsageStats, boolean showAllApps) {
|
|
||||||
mBatteryUsageStats = USE_FAKE_DATA ? getFakeStats() : batteryUsageStats;
|
|
||||||
if (!sConfig.shouldShowBatteryAttributionList(mContext)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final int dischargePercentage = getDischargePercentage(batteryUsageStats);
|
|
||||||
final List<BatteryEntry> usageList =
|
|
||||||
getCoalescedUsageList(showAllApps, /*loadDataInBackground=*/ false);
|
|
||||||
final double totalPower = batteryUsageStats.getConsumedPower();
|
|
||||||
for (int i = 0; i < usageList.size(); i++) {
|
|
||||||
final BatteryEntry entry = usageList.get(i);
|
|
||||||
final double percentOfTotal = mBatteryUtils.calculateBatteryPercent(
|
|
||||||
entry.getConsumedPower(), totalPower, dischargePercentage);
|
|
||||||
entry.mPercent = percentOfTotal;
|
|
||||||
}
|
|
||||||
return usageList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getDischargePercentage(BatteryUsageStats batteryUsageStats) {
|
|
||||||
int dischargePercentage = batteryUsageStats.getDischargePercentage();
|
|
||||||
if (dischargePercentage < 0) {
|
|
||||||
dischargePercentage = 0;
|
|
||||||
}
|
|
||||||
return dischargePercentage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
|
|
||||||
* exists for all users of the same app. We detect this case and merge the power use
|
|
||||||
* for dex2oat to the device OWNER's use of the app.
|
|
||||||
*
|
|
||||||
* @return A sorted list of apps using power.
|
|
||||||
*/
|
|
||||||
private List<BatteryEntry> getCoalescedUsageList(
|
|
||||||
boolean showAllApps, boolean loadDataInBackground) {
|
|
||||||
final SparseArray<BatteryEntry> batteryEntryList = new SparseArray<>();
|
|
||||||
|
|
||||||
final ArrayList<BatteryEntry> results = new ArrayList<>();
|
|
||||||
final List<UidBatteryConsumer> uidBatteryConsumers =
|
|
||||||
mBatteryUsageStats.getUidBatteryConsumers();
|
|
||||||
|
|
||||||
// Sort to have all apps with "real" UIDs first, followed by apps that are supposed
|
|
||||||
// to be combined with the real ones.
|
|
||||||
uidBatteryConsumers.sort(Comparator.comparingInt(
|
|
||||||
consumer -> consumer.getUid() == getRealUid(consumer) ? 0 : 1));
|
|
||||||
|
|
||||||
for (int i = 0, size = uidBatteryConsumers.size(); i < size; i++) {
|
|
||||||
final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
|
|
||||||
final int uid = getRealUid(consumer);
|
|
||||||
|
|
||||||
final String[] packages = mPackageManager.getPackagesForUid(uid);
|
|
||||||
if (mBatteryUtils.shouldHideUidBatteryConsumerUnconditionally(consumer, packages)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final boolean isHidden = mBatteryUtils.shouldHideUidBatteryConsumer(consumer, packages);
|
|
||||||
if (isHidden && !showAllApps) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int index = batteryEntryList.indexOfKey(uid);
|
|
||||||
if (index < 0) {
|
|
||||||
// New entry.
|
|
||||||
batteryEntryList.put(uid, new BatteryEntry(mContext, mHandler, mUserManager,
|
|
||||||
consumer, isHidden, uid, packages, null, loadDataInBackground));
|
|
||||||
} else {
|
|
||||||
// Combine BatterySippers if we already have one with this UID.
|
|
||||||
final BatteryEntry existingSipper = batteryEntryList.valueAt(index);
|
|
||||||
existingSipper.add(consumer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final BatteryConsumer deviceConsumer = mBatteryUsageStats.getAggregateBatteryConsumer(
|
|
||||||
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
|
|
||||||
final BatteryConsumer appsConsumer = mBatteryUsageStats.getAggregateBatteryConsumer(
|
|
||||||
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
|
|
||||||
|
|
||||||
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
|
|
||||||
componentId++) {
|
|
||||||
if (!showAllApps
|
|
||||||
&& mBatteryUtils.shouldHideDevicePowerComponent(deviceConsumer, componentId)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
results.add(new BatteryEntry(mContext, componentId,
|
|
||||||
deviceConsumer.getConsumedPower(componentId),
|
|
||||||
appsConsumer.getConsumedPower(componentId),
|
|
||||||
deviceConsumer.getUsageDurationMillis(componentId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
|
|
||||||
componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
|
|
||||||
+ deviceConsumer.getCustomPowerComponentCount();
|
|
||||||
componentId++) {
|
|
||||||
if (!showAllApps) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
results.add(new BatteryEntry(mContext, componentId,
|
|
||||||
deviceConsumer.getCustomPowerComponentName(componentId),
|
|
||||||
deviceConsumer.getConsumedPowerForCustomComponent(componentId),
|
|
||||||
appsConsumer.getConsumedPowerForCustomComponent(componentId)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showAllApps) {
|
|
||||||
final List<UserBatteryConsumer> userBatteryConsumers =
|
|
||||||
mBatteryUsageStats.getUserBatteryConsumers();
|
|
||||||
for (int i = 0, size = userBatteryConsumers.size(); i < size; i++) {
|
|
||||||
final UserBatteryConsumer consumer = userBatteryConsumers.get(i);
|
|
||||||
results.add(new BatteryEntry(mContext, mHandler, mUserManager,
|
|
||||||
consumer, /* isHidden */ true, Process.INVALID_UID, null, null,
|
|
||||||
loadDataInBackground));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int numUidSippers = batteryEntryList.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < numUidSippers; i++) {
|
|
||||||
results.add(batteryEntryList.valueAt(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// The sort order must have changed, so re-sort based on total power use.
|
|
||||||
results.sort(BatteryEntry.COMPARATOR);
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getRealUid(UidBatteryConsumer consumer) {
|
|
||||||
int realUid = consumer.getUid();
|
|
||||||
|
|
||||||
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
|
|
||||||
// actual app UID.
|
|
||||||
if (isSharedGid(consumer.getUid())) {
|
|
||||||
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
|
|
||||||
UserHandle.getAppIdFromSharedAppGid(consumer.getUid()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
|
|
||||||
if (isSystemUid(realUid)
|
|
||||||
&& !MEDIASERVER_PACKAGE_NAME.equals(consumer.getPackageWithHighestDrain())) {
|
|
||||||
// Use the system UID for all UIDs running in their own sandbox that
|
|
||||||
// are not apps. We exclude mediaserver because we already are expected to
|
|
||||||
// report that as a separate item.
|
|
||||||
realUid = Process.SYSTEM_UID;
|
|
||||||
}
|
|
||||||
return realUid;
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
void setUsageSummary(Preference preference, BatteryEntry entry) {
|
|
||||||
if (BatteryEntry.isSystemUid(entry.getUid())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Only show summary when usage time is longer than one minute
|
|
||||||
final long usageTimeMs = entry.getTimeInForegroundMs();
|
|
||||||
if (usageTimeMs >= DateUtils.MINUTE_IN_MILLIS) {
|
|
||||||
final CharSequence timeSequence =
|
|
||||||
StringUtil.formatElapsedTime(mContext, usageTimeMs, false, false);
|
|
||||||
preference.setSummary(
|
|
||||||
entry.isHidden()
|
|
||||||
? timeSequence
|
|
||||||
: TextUtils.expandTemplate(mContext.getText(R.string.battery_used_for),
|
|
||||||
timeSequence));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cacheRemoveAllPrefs(PreferenceGroup group) {
|
|
||||||
mPreferenceCache = new ArrayMap<>();
|
|
||||||
final int n = group.getPreferenceCount();
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
Preference p = group.getPreference(i);
|
|
||||||
if (TextUtils.isEmpty(p.getKey())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mPreferenceCache.put(p.getKey(), p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSharedGid(int uid) {
|
|
||||||
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSystemUid(int uid) {
|
|
||||||
final int appUid = UserHandle.getAppId(uid);
|
|
||||||
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
private BatteryUsageStats getFakeStats() {
|
|
||||||
BatteryUsageStats.Builder builder = new BatteryUsageStats.Builder(new String[0])
|
|
||||||
.setDischargePercentage(100);
|
|
||||||
|
|
||||||
float use = 500;
|
|
||||||
final AggregateBatteryConsumer.Builder appsBatteryConsumerBuilder =
|
|
||||||
builder.getAggregateBatteryConsumerBuilder(
|
|
||||||
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
|
|
||||||
final AggregateBatteryConsumer.Builder deviceBatteryConsumerBuilder =
|
|
||||||
builder.getAggregateBatteryConsumerBuilder(
|
|
||||||
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
|
|
||||||
for (@BatteryConsumer.PowerComponent int componentId : new int[]{
|
|
||||||
BatteryConsumer.POWER_COMPONENT_AMBIENT_DISPLAY,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_BLUETOOTH,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_CAMERA,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_FLASHLIGHT,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_IDLE,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_MEMORY,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_PHONE,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_SCREEN,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_WIFI,
|
|
||||||
}) {
|
|
||||||
appsBatteryConsumerBuilder.setConsumedPower(componentId, use);
|
|
||||||
deviceBatteryConsumerBuilder.setConsumedPower(componentId, use * 2);
|
|
||||||
use += 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
use = 450;
|
|
||||||
for (int i = 0; i < 100; i++) {
|
|
||||||
builder.getOrCreateUidBatteryConsumerBuilder(Process.FIRST_APPLICATION_UID + i)
|
|
||||||
.setTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND, 10000 + i * 1000)
|
|
||||||
.setTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND, 20000 + i * 2000)
|
|
||||||
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, use);
|
|
||||||
use += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate dex2oat process.
|
|
||||||
builder.getOrCreateUidBatteryConsumerBuilder(Process.FIRST_APPLICATION_UID)
|
|
||||||
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
||||||
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000.0)
|
|
||||||
.setPackageWithHighestDrain("dex2oat");
|
|
||||||
|
|
||||||
builder.getOrCreateUidBatteryConsumerBuilder(Process.FIRST_APPLICATION_UID + 1)
|
|
||||||
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
||||||
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000.0)
|
|
||||||
.setPackageWithHighestDrain("dex2oat");
|
|
||||||
|
|
||||||
builder.getOrCreateUidBatteryConsumerBuilder(UserHandle.getSharedAppGid(Process.LOG_UID))
|
|
||||||
.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CPU, 100000)
|
|
||||||
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 900.0);
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Preference getCachedPreference(String key) {
|
|
||||||
return mPreferenceCache != null ? mPreferenceCache.remove(key) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeCachedPrefs(PreferenceGroup group) {
|
|
||||||
for (Preference p : mPreferenceCache.values()) {
|
|
||||||
group.removePreference(p);
|
|
||||||
}
|
|
||||||
mPreferenceCache = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCachedCount() {
|
|
||||||
return mPreferenceCache != null ? mPreferenceCache.size() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addNotAvailableMessage() {
|
|
||||||
Preference notAvailable = getCachedPreference(NOT_AVAILABLE);
|
|
||||||
if (notAvailable == null) {
|
|
||||||
notAvailable = new Preference(mPrefContext);
|
|
||||||
notAvailable.setKey(NOT_AVAILABLE);
|
|
||||||
notAvailable.setTitle(R.string.power_usage_not_available);
|
|
||||||
notAvailable.setSelectable(false);
|
|
||||||
mAppListGroup.addPreference(notAvailable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -337,10 +337,8 @@ public class BatteryDiffEntry {
|
|||||||
mAppIcon = nameAndIcon.mIcon;
|
mAppIcon = nameAndIcon.mIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
final BatteryEntry.NameAndIcon nameAndIcon =
|
final BatteryEntry.NameAndIcon nameAndIcon = BatteryEntry.loadNameAndIcon(
|
||||||
BatteryEntry.loadNameAndIcon(
|
mContext, uid, /*batteryEntry=*/ null, packageName, mAppLabel, mAppIcon);
|
||||||
mContext, uid, /*handler=*/ null, /*batteryEntry=*/ null,
|
|
||||||
packageName, mAppLabel, mAppIcon);
|
|
||||||
// Clears BatteryEntry internal cache since we will have another one.
|
// Clears BatteryEntry internal cache since we will have another one.
|
||||||
BatteryEntry.clearUidCache();
|
BatteryEntry.clearUidCache();
|
||||||
if (nameAndIcon != null) {
|
if (nameAndIcon != null) {
|
||||||
|
@@ -26,7 +26,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.BatteryConsumer;
|
import android.os.BatteryConsumer;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UidBatteryConsumer;
|
import android.os.UidBatteryConsumer;
|
||||||
@@ -80,78 +79,9 @@ public class BatteryEntry {
|
|||||||
static final HashMap<String, UidToDetail> sUidCache = new HashMap<>();
|
static final HashMap<String, UidToDetail> sUidCache = new HashMap<>();
|
||||||
|
|
||||||
static final ArrayList<BatteryEntry> sRequestQueue = new ArrayList<BatteryEntry>();
|
static final ArrayList<BatteryEntry> sRequestQueue = new ArrayList<BatteryEntry>();
|
||||||
static Handler sHandler;
|
|
||||||
|
|
||||||
static Locale sCurrentLocale = null;
|
static Locale sCurrentLocale = null;
|
||||||
|
|
||||||
private static class NameAndIconLoader extends Thread {
|
|
||||||
private boolean mAbort = false;
|
|
||||||
|
|
||||||
NameAndIconLoader() {
|
|
||||||
super("BatteryUsage Icon Loader");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void abort() {
|
|
||||||
mAbort = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
while (true) {
|
|
||||||
BatteryEntry be;
|
|
||||||
synchronized (sRequestQueue) {
|
|
||||||
if (sRequestQueue.isEmpty() || mAbort) {
|
|
||||||
if (sHandler != null) {
|
|
||||||
sHandler.sendEmptyMessage(MSG_REPORT_FULLY_DRAWN);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
be = sRequestQueue.remove(0);
|
|
||||||
}
|
|
||||||
final NameAndIcon nameAndIcon =
|
|
||||||
BatteryEntry.loadNameAndIcon(
|
|
||||||
be.mContext, be.getUid(), sHandler, be,
|
|
||||||
be.mDefaultPackageName, be.mName, be.mIcon);
|
|
||||||
if (nameAndIcon != null) {
|
|
||||||
be.mIcon = nameAndIcon.mIcon;
|
|
||||||
be.mName = nameAndIcon.mName;
|
|
||||||
be.mDefaultPackageName = nameAndIcon.mPackageName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static NameAndIconLoader sRequestThread;
|
|
||||||
|
|
||||||
/** Starts the request queue. */
|
|
||||||
public static void startRequestQueue() {
|
|
||||||
if (sHandler != null) {
|
|
||||||
synchronized (sRequestQueue) {
|
|
||||||
if (!sRequestQueue.isEmpty()) {
|
|
||||||
if (sRequestThread != null) {
|
|
||||||
sRequestThread.abort();
|
|
||||||
}
|
|
||||||
sRequestThread = new NameAndIconLoader();
|
|
||||||
sRequestThread.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
sRequestThread.start();
|
|
||||||
sRequestQueue.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Stops the request queue. */
|
|
||||||
public static void stopRequestQueue() {
|
|
||||||
synchronized (sRequestQueue) {
|
|
||||||
if (sRequestThread != null) {
|
|
||||||
sRequestThread.abort();
|
|
||||||
sRequestThread = null;
|
|
||||||
sRequestQueue.clear();
|
|
||||||
sHandler = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clears the UID cache. */
|
/** Clears the UID cache. */
|
||||||
public static void clearUidCache() {
|
public static void clearUidCache() {
|
||||||
sUidCache.clear();
|
sUidCache.clear();
|
||||||
@@ -185,16 +115,14 @@ public class BatteryEntry {
|
|||||||
Drawable mIcon;
|
Drawable mIcon;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatteryEntry(Context context, Handler handler, UserManager um,
|
public BatteryEntry(Context context, UserManager um, BatteryConsumer batteryConsumer,
|
||||||
BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages,
|
boolean isHidden, int uid, String[] packages, String packageName) {
|
||||||
String packageName) {
|
this(context, um, batteryConsumer, isHidden, uid, packages, packageName, true);
|
||||||
this(context, handler, um, batteryConsumer, isHidden, uid, packages, packageName, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatteryEntry(Context context, Handler handler, UserManager um,
|
public BatteryEntry(Context context, UserManager um, BatteryConsumer batteryConsumer,
|
||||||
BatteryConsumer batteryConsumer, boolean isHidden, int uid, String[] packages,
|
boolean isHidden, int uid, String[] packages, String packageName,
|
||||||
String packageName, boolean loadDataInBackground) {
|
boolean loadDataInBackground) {
|
||||||
sHandler = handler;
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mBatteryConsumer = batteryConsumer;
|
mBatteryConsumer = batteryConsumer;
|
||||||
mIsHidden = isHidden;
|
mIsHidden = isHidden;
|
||||||
@@ -332,20 +260,12 @@ public class BatteryEntry {
|
|||||||
} else {
|
} else {
|
||||||
mIcon = mContext.getPackageManager().getDefaultActivityIcon();
|
mIcon = mContext.getPackageManager().getDefaultActivityIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Avoids post the loading icon and label in the background request.
|
|
||||||
if (sHandler != null && loadDataInBackground) {
|
|
||||||
synchronized (sRequestQueue) {
|
|
||||||
sRequestQueue.add(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Loads the app label and icon image and stores into the cache. */
|
/** Loads the app label and icon image and stores into the cache. */
|
||||||
public static NameAndIcon loadNameAndIcon(
|
public static NameAndIcon loadNameAndIcon(
|
||||||
Context context,
|
Context context,
|
||||||
int uid,
|
int uid,
|
||||||
Handler handler,
|
|
||||||
BatteryEntry batteryEntry,
|
BatteryEntry batteryEntry,
|
||||||
String defaultPackageName,
|
String defaultPackageName,
|
||||||
String name,
|
String name,
|
||||||
@@ -432,9 +352,6 @@ public class BatteryEntry {
|
|||||||
utd.mPackageName = defaultPackageName;
|
utd.mPackageName = defaultPackageName;
|
||||||
|
|
||||||
sUidCache.put(uidString, utd);
|
sUidCache.put(uidString, utd);
|
||||||
if (handler != null) {
|
|
||||||
handler.sendMessage(handler.obtainMessage(MSG_UPDATE_NAME_ICON, batteryEntry));
|
|
||||||
}
|
|
||||||
return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0);
|
return new NameAndIcon(name, defaultPackageName, icon, /*iconId=*/ 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,13 +24,15 @@ import android.util.Log;
|
|||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
/** Load battery usage data in the background. */
|
/** Load battery usage data in the background. */
|
||||||
public final class BatteryUsageDataLoader {
|
public final class BatteryUsageDataLoader {
|
||||||
private static final String TAG = "BatteryUsageDataLoader";
|
private static final String TAG = "BatteryUsageDataLoader";
|
||||||
|
|
||||||
|
// For testing only.
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static BatteryAppListPreferenceController sController;
|
static Supplier<List<BatteryEntry>> sFakeBatteryEntryListSupplier;
|
||||||
|
|
||||||
private BatteryUsageDataLoader() {
|
private BatteryUsageDataLoader() {
|
||||||
}
|
}
|
||||||
@@ -47,10 +49,9 @@ public final class BatteryUsageDataLoader {
|
|||||||
final long start = System.currentTimeMillis();
|
final long start = System.currentTimeMillis();
|
||||||
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
|
final BatteryUsageStats batteryUsageStats = DataProcessor.getBatteryUsageStats(context);
|
||||||
final List<BatteryEntry> batteryEntryList =
|
final List<BatteryEntry> batteryEntryList =
|
||||||
DataProcessor.generateBatteryEntryListFromBatteryUsageStats(
|
sFakeBatteryEntryListSupplier != null ? sFakeBatteryEntryListSupplier.get()
|
||||||
context,
|
: DataProcessor.generateBatteryEntryListFromBatteryUsageStats(context,
|
||||||
batteryUsageStats,
|
batteryUsageStats);
|
||||||
sController);
|
|
||||||
if (batteryEntryList == null || batteryEntryList.isEmpty()) {
|
if (batteryEntryList == null || batteryEntryList.isEmpty()) {
|
||||||
Log.w(TAG, "getBatteryEntryList() returns null or empty content");
|
Log.w(TAG, "getBatteryEntryList() returns null or empty content");
|
||||||
}
|
}
|
||||||
|
@@ -21,22 +21,29 @@ import static com.android.settings.fuelgauge.batteryusage.ConvertUtils.utcToLoca
|
|||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
import android.os.BatteryConsumer;
|
||||||
import android.os.BatteryStatsManager;
|
import android.os.BatteryStatsManager;
|
||||||
import android.os.BatteryUsageStats;
|
import android.os.BatteryUsageStats;
|
||||||
import android.os.BatteryUsageStatsQuery;
|
import android.os.BatteryUsageStatsQuery;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UidBatteryConsumer;
|
||||||
|
import android.os.UserBatteryConsumer;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.format.DateUtils;
|
import android.text.format.DateUtils;
|
||||||
import android.util.ArraySet;
|
import android.util.ArraySet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.internal.os.PowerProfile;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
import com.android.settings.fuelgauge.BatteryUtils;
|
||||||
import com.android.settings.overlay.FeatureFactory;
|
import com.android.settings.overlay.FeatureFactory;
|
||||||
@@ -48,6 +55,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -62,12 +70,14 @@ import java.util.stream.Collectors;
|
|||||||
public final class DataProcessor {
|
public final class DataProcessor {
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
private static final String TAG = "DataProcessor";
|
private static final String TAG = "DataProcessor";
|
||||||
|
private static final int MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP = 10;
|
||||||
private static final int MIN_DAILY_DATA_SIZE = 2;
|
private static final int MIN_DAILY_DATA_SIZE = 2;
|
||||||
private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
|
private static final int MIN_TIMESTAMP_DATA_SIZE = 2;
|
||||||
private static final int MAX_DIFF_SECONDS_OF_UPPER_TIMESTAMP = 5;
|
private static final int MAX_DIFF_SECONDS_OF_UPPER_TIMESTAMP = 5;
|
||||||
// Maximum total time value for each hourly slot cumulative data at most 2 hours.
|
// Maximum total time value for each hourly slot cumulative data at most 2 hours.
|
||||||
private static final float TOTAL_HOURLY_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
|
private static final float TOTAL_HOURLY_TIME_THRESHOLD = DateUtils.HOUR_IN_MILLIS * 2;
|
||||||
private static final long MIN_TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
|
private static final long MIN_TIME_SLOT = DateUtils.HOUR_IN_MILLIS * 2;
|
||||||
|
private static final String MEDIASERVER_PACKAGE_NAME = "mediaserver";
|
||||||
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>();
|
private static final Map<String, BatteryHistEntry> EMPTY_BATTERY_MAP = new HashMap<>();
|
||||||
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
|
private static final BatteryHistEntry EMPTY_BATTERY_HIST_ENTRY =
|
||||||
new BatteryHistEntry(new ContentValues());
|
new BatteryHistEntry(new ContentValues());
|
||||||
@@ -188,24 +198,26 @@ public final class DataProcessor {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public static List<BatteryEntry> generateBatteryEntryListFromBatteryUsageStats(
|
public static List<BatteryEntry> generateBatteryEntryListFromBatteryUsageStats(
|
||||||
final Context context,
|
final Context context,
|
||||||
@Nullable final BatteryUsageStats batteryUsageStats,
|
@Nullable final BatteryUsageStats batteryUsageStats) {
|
||||||
@Nullable BatteryAppListPreferenceController batteryAppListPreferenceController) {
|
|
||||||
if (batteryUsageStats == null) {
|
if (batteryUsageStats == null) {
|
||||||
Log.w(TAG, "batteryUsageStats is null content");
|
Log.w(TAG, "batteryUsageStats is null content");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Loads the battery consuming data.
|
if (!shouldShowBatteryAttributionList(context)) {
|
||||||
final BatteryAppListPreferenceController controller =
|
return null;
|
||||||
batteryAppListPreferenceController == null
|
}
|
||||||
? new BatteryAppListPreferenceController(
|
final BatteryUtils batteryUtils = BatteryUtils.getInstance(context);
|
||||||
context,
|
final int dischargePercentage = Math.max(0, batteryUsageStats.getDischargePercentage());
|
||||||
/*preferenceKey=*/ null,
|
final List<BatteryEntry> usageList = getCoalescedUsageList(
|
||||||
/*lifecycle=*/ null,
|
context, batteryUtils, batteryUsageStats, /*loadDataInBackground=*/ false);
|
||||||
/*activity*=*/ null,
|
final double totalPower = batteryUsageStats.getConsumedPower();
|
||||||
/*fragment=*/ null)
|
for (int i = 0; i < usageList.size(); i++) {
|
||||||
: batteryAppListPreferenceController;
|
final BatteryEntry entry = usageList.get(i);
|
||||||
|
final double percentOfTotal = batteryUtils.calculateBatteryPercent(
|
||||||
return controller.getBatteryEntryList(batteryUsageStats, /*showAllApps=*/ true);
|
entry.getConsumedPower(), totalPower, dischargePercentage);
|
||||||
|
entry.mPercent = percentOfTotal;
|
||||||
|
}
|
||||||
|
return usageList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -534,10 +546,7 @@ public final class DataProcessor {
|
|||||||
try {
|
try {
|
||||||
final BatteryUsageStats batteryUsageStats = getBatteryUsageStats(context);
|
final BatteryUsageStats batteryUsageStats = getBatteryUsageStats(context);
|
||||||
final List<BatteryEntry> batteryEntryList =
|
final List<BatteryEntry> batteryEntryList =
|
||||||
generateBatteryEntryListFromBatteryUsageStats(
|
generateBatteryEntryListFromBatteryUsageStats(context, batteryUsageStats);
|
||||||
context,
|
|
||||||
batteryUsageStats,
|
|
||||||
/*batteryAppListPreferenceController=*/ null);
|
|
||||||
batteryDiffData = generateBatteryDiffData(context, batteryEntryList, batteryUsageStats);
|
batteryDiffData = generateBatteryDiffData(context, batteryEntryList, batteryUsageStats);
|
||||||
closeBatteryUsageStats(batteryUsageStats);
|
closeBatteryUsageStats(batteryUsageStats);
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
@@ -1100,6 +1109,136 @@ public final class DataProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean shouldShowBatteryAttributionList(final Context context) {
|
||||||
|
final PowerProfile powerProfile = new PowerProfile(context);
|
||||||
|
// Cheap hack to try to figure out if the power_profile.xml was populated.
|
||||||
|
final double averagePowerForOrdinal = powerProfile.getAveragePowerForOrdinal(
|
||||||
|
PowerProfile.POWER_GROUP_DISPLAY_SCREEN_FULL, 0);
|
||||||
|
final boolean shouldShowBatteryAttributionList =
|
||||||
|
averagePowerForOrdinal >= MIN_AVERAGE_POWER_THRESHOLD_MILLI_AMP;
|
||||||
|
if (!shouldShowBatteryAttributionList) {
|
||||||
|
Log.w(TAG, "shouldShowBatteryAttributionList(): " + averagePowerForOrdinal);
|
||||||
|
}
|
||||||
|
return shouldShowBatteryAttributionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We want to coalesce some UIDs. For example, dex2oat runs under a shared gid that
|
||||||
|
* exists for all users of the same app. We detect this case and merge the power use
|
||||||
|
* for dex2oat to the device OWNER's use of the app.
|
||||||
|
*
|
||||||
|
* @return A sorted list of apps using power.
|
||||||
|
*/
|
||||||
|
private static List<BatteryEntry> getCoalescedUsageList(final Context context,
|
||||||
|
final BatteryUtils batteryUtils,
|
||||||
|
final BatteryUsageStats batteryUsageStats,
|
||||||
|
final boolean loadDataInBackground) {
|
||||||
|
final PackageManager packageManager = context.getPackageManager();
|
||||||
|
final UserManager userManager = context.getSystemService(UserManager.class);
|
||||||
|
final SparseArray<BatteryEntry> batteryEntryList = new SparseArray<>();
|
||||||
|
final ArrayList<BatteryEntry> results = new ArrayList<>();
|
||||||
|
final List<UidBatteryConsumer> uidBatteryConsumers =
|
||||||
|
batteryUsageStats.getUidBatteryConsumers();
|
||||||
|
|
||||||
|
// Sort to have all apps with "real" UIDs first, followed by apps that are supposed
|
||||||
|
// to be combined with the real ones.
|
||||||
|
uidBatteryConsumers.sort(Comparator.comparingInt(
|
||||||
|
consumer -> consumer.getUid() == getRealUid(consumer) ? 0 : 1));
|
||||||
|
|
||||||
|
for (int i = 0, size = uidBatteryConsumers.size(); i < size; i++) {
|
||||||
|
final UidBatteryConsumer consumer = uidBatteryConsumers.get(i);
|
||||||
|
final int uid = getRealUid(consumer);
|
||||||
|
|
||||||
|
final String[] packages = packageManager.getPackagesForUid(uid);
|
||||||
|
if (batteryUtils.shouldHideUidBatteryConsumerUnconditionally(consumer, packages)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean isHidden = batteryUtils.shouldHideUidBatteryConsumer(consumer, packages);
|
||||||
|
final int index = batteryEntryList.indexOfKey(uid);
|
||||||
|
if (index < 0) {
|
||||||
|
// New entry.
|
||||||
|
batteryEntryList.put(uid, new BatteryEntry(context, userManager, consumer,
|
||||||
|
isHidden, uid, packages, null, loadDataInBackground));
|
||||||
|
} else {
|
||||||
|
// Combine BatterySippers if we already have one with this UID.
|
||||||
|
final BatteryEntry existingSipper = batteryEntryList.valueAt(index);
|
||||||
|
existingSipper.add(consumer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final BatteryConsumer deviceConsumer = batteryUsageStats.getAggregateBatteryConsumer(
|
||||||
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
|
||||||
|
final BatteryConsumer appsConsumer = batteryUsageStats.getAggregateBatteryConsumer(
|
||||||
|
BatteryUsageStats.AGGREGATE_BATTERY_CONSUMER_SCOPE_ALL_APPS);
|
||||||
|
|
||||||
|
for (int componentId = 0; componentId < BatteryConsumer.POWER_COMPONENT_COUNT;
|
||||||
|
componentId++) {
|
||||||
|
results.add(new BatteryEntry(context, componentId,
|
||||||
|
deviceConsumer.getConsumedPower(componentId),
|
||||||
|
appsConsumer.getConsumedPower(componentId),
|
||||||
|
deviceConsumer.getUsageDurationMillis(componentId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int componentId = BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID;
|
||||||
|
componentId < BatteryConsumer.FIRST_CUSTOM_POWER_COMPONENT_ID
|
||||||
|
+ deviceConsumer.getCustomPowerComponentCount();
|
||||||
|
componentId++) {
|
||||||
|
results.add(new BatteryEntry(context, componentId,
|
||||||
|
deviceConsumer.getCustomPowerComponentName(componentId),
|
||||||
|
deviceConsumer.getConsumedPowerForCustomComponent(componentId),
|
||||||
|
appsConsumer.getConsumedPowerForCustomComponent(componentId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
final List<UserBatteryConsumer> userBatteryConsumers =
|
||||||
|
batteryUsageStats.getUserBatteryConsumers();
|
||||||
|
for (int i = 0, size = userBatteryConsumers.size(); i < size; i++) {
|
||||||
|
final UserBatteryConsumer consumer = userBatteryConsumers.get(i);
|
||||||
|
results.add(new BatteryEntry(context, userManager, consumer, /* isHidden */ true,
|
||||||
|
Process.INVALID_UID, null, null, loadDataInBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
final int numUidSippers = batteryEntryList.size();
|
||||||
|
|
||||||
|
for (int i = 0; i < numUidSippers; i++) {
|
||||||
|
results.add(batteryEntryList.valueAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The sort order must have changed, so re-sort based on total power use.
|
||||||
|
results.sort(BatteryEntry.COMPARATOR);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int getRealUid(final UidBatteryConsumer consumer) {
|
||||||
|
int realUid = consumer.getUid();
|
||||||
|
|
||||||
|
// Check if this UID is a shared GID. If so, we combine it with the OWNER's
|
||||||
|
// actual app UID.
|
||||||
|
if (isSharedGid(consumer.getUid())) {
|
||||||
|
realUid = UserHandle.getUid(UserHandle.USER_SYSTEM,
|
||||||
|
UserHandle.getAppIdFromSharedAppGid(consumer.getUid()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this UID is a system UID (mediaserver, logd, nfc, drm, etc).
|
||||||
|
if (isSystemUid(realUid)
|
||||||
|
&& !MEDIASERVER_PACKAGE_NAME.equals(consumer.getPackageWithHighestDrain())) {
|
||||||
|
// Use the system UID for all UIDs running in their own sandbox that
|
||||||
|
// are not apps. We exclude mediaserver because we already are expected to
|
||||||
|
// report that as a separate item.
|
||||||
|
realUid = Process.SYSTEM_UID;
|
||||||
|
}
|
||||||
|
return realUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSharedGid(final int uid) {
|
||||||
|
return UserHandle.getAppIdFromSharedAppGid(uid) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSystemUid(final int uid) {
|
||||||
|
final int appUid = UserHandle.getAppId(uid);
|
||||||
|
return appUid >= Process.SYSTEM_UID && appUid < Process.FIRST_APPLICATION_UID;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isUsageMapValid(
|
private static boolean isUsageMapValid(
|
||||||
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap,
|
final Map<Integer, Map<Integer, BatteryDiffData>> batteryUsageMap,
|
||||||
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
|
final List<BatteryLevelData.PeriodBatteryLevelData> hourlyBatteryLevelsPerDay) {
|
||||||
|
@@ -216,41 +216,6 @@ public class BatteryUtilsTest {
|
|||||||
assertThat(time).isEqualTo(0);
|
assertThat(time).isEqualTo(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_TypeIdle_ReturnTrue() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_IDLE)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_TypeMobileRadio_ReturnTrue() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_TypeScreen_ReturnTrue() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_SCREEN)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_TypeBluetooth_ReturnTrue() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_BLUETOOTH)).isTrue(); }
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_TypeWifi_ReturnTrue() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_WIFI)).isTrue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testShouldHideSystemConsumer_OtherType_ReturnFalse() {
|
|
||||||
assertThat(mBatteryUtils.shouldHideDevicePowerComponent(mAggregateBatteryConsumer,
|
|
||||||
BatteryConsumer.POWER_COMPONENT_FLASHLIGHT)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCalculateBatteryPercent() {
|
public void testCalculateBatteryPercent() {
|
||||||
assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE,
|
assertThat(mBatteryUtils.calculateBatteryPercent(BATTERY_SYSTEM_USAGE, TOTAL_BATTERY_USAGE,
|
||||||
|
@@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022 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.batteryusage;
|
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
|
||||||
|
|
||||||
import static org.mockito.Mockito.anyInt;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.os.UserManager;
|
|
||||||
import android.text.format.DateUtils;
|
|
||||||
|
|
||||||
import androidx.preference.PreferenceGroup;
|
|
||||||
|
|
||||||
import com.android.settings.R;
|
|
||||||
import com.android.settings.SettingsActivity;
|
|
||||||
import com.android.settings.core.InstrumentedPreferenceFragment;
|
|
||||||
import com.android.settings.fuelgauge.BatteryUtils;
|
|
||||||
import com.android.settings.testutils.FakeFeatureFactory;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.robolectric.RobolectricTestRunner;
|
|
||||||
import org.robolectric.RuntimeEnvironment;
|
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
|
||||||
public class BatteryAppListPreferenceControllerTest {
|
|
||||||
|
|
||||||
private static final String KEY_APP_LIST = "app_list";
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private SettingsActivity mSettingsActivity;
|
|
||||||
@Mock
|
|
||||||
private PreferenceGroup mAppListGroup;
|
|
||||||
@Mock
|
|
||||||
private InstrumentedPreferenceFragment mFragment;
|
|
||||||
@Mock
|
|
||||||
private BatteryUtils mBatteryUtils;
|
|
||||||
@Mock
|
|
||||||
private PackageManager mPackageManager;
|
|
||||||
@Mock
|
|
||||||
private UserManager mUserManager;
|
|
||||||
@Mock
|
|
||||||
private BatteryEntry mBatteryEntry;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private PowerGaugePreference mPreference;
|
|
||||||
private BatteryAppListPreferenceController mPreferenceController;
|
|
||||||
private FakeFeatureFactory mFeatureFactory;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
|
|
||||||
mFeatureFactory = FakeFeatureFactory.setupForTest();
|
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
|
||||||
final Resources resources = spy(mContext.getResources());
|
|
||||||
when(mContext.getResources()).thenReturn(resources);
|
|
||||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
|
||||||
when(mContext.getApplicationContext()).thenReturn(mContext);
|
|
||||||
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
|
|
||||||
when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
|
|
||||||
|
|
||||||
mPreference = new PowerGaugePreference(mContext);
|
|
||||||
|
|
||||||
mPreferenceController = new BatteryAppListPreferenceController(mContext, KEY_APP_LIST, null,
|
|
||||||
mSettingsActivity, mFragment);
|
|
||||||
mPreferenceController.mBatteryUtils = mBatteryUtils;
|
|
||||||
mPreferenceController.mAppListGroup = mAppListGroup;
|
|
||||||
|
|
||||||
BatteryAppListPreferenceController.sConfig =
|
|
||||||
new BatteryAppListPreferenceController.Config() {
|
|
||||||
@Override
|
|
||||||
public boolean shouldShowBatteryAttributionList(Context context) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetUsageSummary_timeLessThanOneMinute_doNotSetSummary() {
|
|
||||||
when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(59 * DateUtils.SECOND_IN_MILLIS);
|
|
||||||
|
|
||||||
mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
|
|
||||||
assertThat(mPreference.getSummary()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetUsageSummary_systemProcessUid_doNotSetSummary() {
|
|
||||||
when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(DateUtils.MINUTE_IN_MILLIS);
|
|
||||||
when(mBatteryEntry.getUid()).thenReturn(Process.SYSTEM_UID);
|
|
||||||
|
|
||||||
mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
|
|
||||||
assertThat(mPreference.getSummary()).isNull();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetUsageSummary_timeMoreThanOneMinute_normalApp_setScreenSummary() {
|
|
||||||
when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(2 * DateUtils.MINUTE_IN_MILLIS);
|
|
||||||
doReturn(mContext.getText(R.string.battery_used_for)).when(mFragment).getText(
|
|
||||||
R.string.battery_used_for);
|
|
||||||
doReturn(mContext).when(mFragment).getContext();
|
|
||||||
|
|
||||||
mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
|
|
||||||
|
|
||||||
assertThat(mPreference.getSummary().toString()).isEqualTo("Used for 2 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetUsageSummary_timeMoreThanOneMinute_hiddenApp_setUsedSummary() {
|
|
||||||
when(mBatteryEntry.getTimeInForegroundMs()).thenReturn(2 * DateUtils.MINUTE_IN_MILLIS);
|
|
||||||
when(mBatteryEntry.isHidden()).thenReturn(true);
|
|
||||||
|
|
||||||
doReturn(mContext).when(mFragment).getContext();
|
|
||||||
|
|
||||||
mPreferenceController.setUsageSummary(mPreference, mBatteryEntry);
|
|
||||||
|
|
||||||
assertThat(mPreference.getSummary().toString()).isEqualTo("2 min");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNeverUseFakeData() {
|
|
||||||
assertThat(BatteryAppListPreferenceController.USE_FAKE_DATA).isFalse();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -30,7 +30,6 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.os.BatteryConsumer;
|
import android.os.BatteryConsumer;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.UidBatteryConsumer;
|
import android.os.UidBatteryConsumer;
|
||||||
import android.os.UserBatteryConsumer;
|
import android.os.UserBatteryConsumer;
|
||||||
@@ -70,8 +69,6 @@ public class BatteryEntryTest {
|
|||||||
private Context mMockContext;
|
private Context mMockContext;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
@Mock
|
@Mock
|
||||||
private Handler mMockHandler;
|
|
||||||
@Mock
|
|
||||||
private PackageManager mMockPackageManager;
|
private PackageManager mMockPackageManager;
|
||||||
@Mock
|
@Mock
|
||||||
private UserManager mMockUserManager;
|
private UserManager mMockUserManager;
|
||||||
@@ -102,7 +99,7 @@ public class BatteryEntryTest {
|
|||||||
UidBatteryConsumer consumer = mock(UidBatteryConsumer.class);
|
UidBatteryConsumer consumer = mock(UidBatteryConsumer.class);
|
||||||
when(consumer.getUid()).thenReturn(APP_UID);
|
when(consumer.getUid()).thenReturn(APP_UID);
|
||||||
when(consumer.getPackageWithHighestDrain()).thenReturn(highDrainPackage);
|
when(consumer.getPackageWithHighestDrain()).thenReturn(highDrainPackage);
|
||||||
return new BatteryEntry(mMockContext, mMockHandler, mMockUserManager,
|
return new BatteryEntry(mMockContext, mMockUserManager,
|
||||||
consumer, false, APP_UID, packages, packageName);
|
consumer, false, APP_UID, packages, packageName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +114,7 @@ public class BatteryEntryTest {
|
|||||||
private BatteryEntry createUserBatteryConsumer(int userId) {
|
private BatteryEntry createUserBatteryConsumer(int userId) {
|
||||||
UserBatteryConsumer consumer = mock(UserBatteryConsumer.class);
|
UserBatteryConsumer consumer = mock(UserBatteryConsumer.class);
|
||||||
when(consumer.getUserId()).thenReturn(userId);
|
when(consumer.getUserId()).thenReturn(userId);
|
||||||
return new BatteryEntry(mMockContext, mMockHandler, mMockUserManager,
|
return new BatteryEntry(mMockContext, mMockUserManager,
|
||||||
consumer, false, 0, null, null);
|
consumer, false, 0, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +179,7 @@ public class BatteryEntryTest {
|
|||||||
when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
|
when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_FOREGROUND))
|
||||||
.thenReturn(100L);
|
.thenReturn(100L);
|
||||||
|
|
||||||
final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mMockHandler,
|
final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application,
|
||||||
mMockUserManager, mUidBatteryConsumer, false, 0, null, null);
|
mMockUserManager, mUidBatteryConsumer, false, 0, null, null);
|
||||||
|
|
||||||
assertThat(entry.getTimeInForegroundMs()).isEqualTo(100L);
|
assertThat(entry.getTimeInForegroundMs()).isEqualTo(100L);
|
||||||
@@ -201,7 +198,7 @@ public class BatteryEntryTest {
|
|||||||
when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
|
when(mUidBatteryConsumer.getTimeInStateMs(UidBatteryConsumer.STATE_BACKGROUND))
|
||||||
.thenReturn(100L);
|
.thenReturn(100L);
|
||||||
|
|
||||||
final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application, mMockHandler,
|
final BatteryEntry entry = new BatteryEntry(RuntimeEnvironment.application,
|
||||||
mMockUserManager, mUidBatteryConsumer, false, 0, null, null);
|
mMockUserManager, mUidBatteryConsumer, false, 0, null, null);
|
||||||
|
|
||||||
assertThat(entry.getTimeInBackgroundMs()).isEqualTo(100L);
|
assertThat(entry.getTimeInBackgroundMs()).isEqualTo(100L);
|
||||||
@@ -217,8 +214,6 @@ public class BatteryEntryTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUidCache_switchLocale_shouldCleanCache() {
|
public void testUidCache_switchLocale_shouldCleanCache() {
|
||||||
BatteryEntry.stopRequestQueue();
|
|
||||||
|
|
||||||
Locale.setDefault(new Locale("en_US"));
|
Locale.setDefault(new Locale("en_US"));
|
||||||
BatteryEntry.sUidCache.put(Integer.toString(APP_UID), null);
|
BatteryEntry.sUidCache.put(Integer.toString(APP_UID), null);
|
||||||
assertThat(BatteryEntry.sUidCache).isNotEmpty();
|
assertThat(BatteryEntry.sUidCache).isNotEmpty();
|
||||||
|
@@ -20,7 +20,6 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -59,8 +58,6 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private BatteryUsageStats mBatteryUsageStats;
|
private BatteryUsageStats mBatteryUsageStats;
|
||||||
@Mock
|
@Mock
|
||||||
private BatteryAppListPreferenceController mMockBatteryAppListController;
|
|
||||||
@Mock
|
|
||||||
private BatteryEntry mMockBatteryEntry;
|
private BatteryEntry mMockBatteryEntry;
|
||||||
@Captor
|
@Captor
|
||||||
private ArgumentCaptor<BatteryUsageStatsQuery> mStatsQueryCaptor;
|
private ArgumentCaptor<BatteryUsageStatsQuery> mStatsQueryCaptor;
|
||||||
@@ -69,7 +66,6 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = spy(RuntimeEnvironment.application);
|
mContext = spy(RuntimeEnvironment.application);
|
||||||
BatteryUsageDataLoader.sController = mMockBatteryAppListController;
|
|
||||||
doReturn(mContext).when(mContext).getApplicationContext();
|
doReturn(mContext).when(mContext).getApplicationContext();
|
||||||
doReturn(mBatteryStatsManager).when(mContext).getSystemService(
|
doReturn(mBatteryStatsManager).when(mContext).getSystemService(
|
||||||
Context.BATTERY_STATS_SERVICE);
|
Context.BATTERY_STATS_SERVICE);
|
||||||
@@ -84,8 +80,7 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
batteryEntryList.add(mMockBatteryEntry);
|
batteryEntryList.add(mMockBatteryEntry);
|
||||||
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
||||||
.thenReturn(mBatteryUsageStats);
|
.thenReturn(mBatteryUsageStats);
|
||||||
when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
|
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> batteryEntryList;
|
||||||
.thenReturn(batteryEntryList);
|
|
||||||
|
|
||||||
BatteryUsageDataLoader.loadUsageData(mContext);
|
BatteryUsageDataLoader.loadUsageData(mContext);
|
||||||
|
|
||||||
@@ -93,24 +88,6 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
assertThat(queryFlags
|
assertThat(queryFlags
|
||||||
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY)
|
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY)
|
||||||
.isNotEqualTo(0);
|
.isNotEqualTo(0);
|
||||||
verify(mMockBatteryAppListController)
|
|
||||||
.getBatteryEntryList(mBatteryUsageStats, /*showAllApps=*/ true);
|
|
||||||
verify(mMockContentResolver).insert(any(), any());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void loadUsageData_nullBatteryUsageStats_notLoadBatteryEntryData() {
|
|
||||||
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
|
||||||
.thenReturn(null);
|
|
||||||
|
|
||||||
BatteryUsageDataLoader.loadUsageData(mContext);
|
|
||||||
|
|
||||||
final int queryFlags = mStatsQueryCaptor.getValue().getFlags();
|
|
||||||
assertThat(queryFlags
|
|
||||||
& BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY)
|
|
||||||
.isNotEqualTo(0);
|
|
||||||
verify(mMockBatteryAppListController, never())
|
|
||||||
.getBatteryEntryList(mBatteryUsageStats, /*showAllApps=*/ true);
|
|
||||||
verify(mMockContentResolver).insert(any(), any());
|
verify(mMockContentResolver).insert(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +95,7 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
public void loadUsageData_nullBatteryEntryList_insertFakeDataIntoProvider() {
|
public void loadUsageData_nullBatteryEntryList_insertFakeDataIntoProvider() {
|
||||||
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
||||||
.thenReturn(mBatteryUsageStats);
|
.thenReturn(mBatteryUsageStats);
|
||||||
when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
|
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> null;
|
||||||
.thenReturn(null);
|
|
||||||
|
|
||||||
BatteryUsageDataLoader.loadUsageData(mContext);
|
BatteryUsageDataLoader.loadUsageData(mContext);
|
||||||
|
|
||||||
@@ -130,8 +106,7 @@ public final class BatteryUsageDataLoaderTest {
|
|||||||
public void loadUsageData_emptyBatteryEntryList_insertFakeDataIntoProvider() {
|
public void loadUsageData_emptyBatteryEntryList_insertFakeDataIntoProvider() {
|
||||||
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
when(mBatteryStatsManager.getBatteryUsageStats(mStatsQueryCaptor.capture()))
|
||||||
.thenReturn(mBatteryUsageStats);
|
.thenReturn(mBatteryUsageStats);
|
||||||
when(mMockBatteryAppListController.getBatteryEntryList(mBatteryUsageStats, true))
|
BatteryUsageDataLoader.sFakeBatteryEntryListSupplier = () -> new ArrayList<BatteryEntry>();
|
||||||
.thenReturn(new ArrayList<BatteryEntry>());
|
|
||||||
|
|
||||||
BatteryUsageDataLoader.loadUsageData(mContext);
|
BatteryUsageDataLoader.loadUsageData(mContext);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user