From e1217fdd9dfb9bce505ba3e6c5d3990c30c2e240 Mon Sep 17 00:00:00 2001
From: Jing Ji
Date: Sat, 5 Feb 2022 02:25:03 -0800
Subject: [PATCH] Track the battery usage in different dimensions in
AppBatteryTracker
So it could be configured to catch abusive battery usage by different
combination. It also supports configurable power components.
By default, when background only battery usage reaches a certain
threshold, it'll be put into restricted standby bucket; while after
that, when the sum of its background and foreground service battery
usage reaches to another threshold, the system will prompt the user.
Bug: 200326767
Bug: 203105544
Test: atest FrameworksMockingServicesTests:BackgroundRestrictionTest
Change-Id: I2887201c06cbe7c936eb52c9aa5beb5e326634b7
---
.../am/ActivityManagerShellCommand.java | 20 +-
.../server/am/AppBatteryExemptionTracker.java | 97 +--
.../android/server/am/AppBatteryTracker.java | 589 +++++++++++++++---
.../server/am/AppRestrictionController.java | 7 +-
.../server/am/BackgroundRestrictionTest.java | 25 +-
5 files changed, 586 insertions(+), 152 deletions(-)
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 97727b0875a39..08508b28b9a58 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -31,6 +31,7 @@ import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MOD
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
import android.app.ActivityManager;
@@ -3256,12 +3257,12 @@ final class ActivityManagerShellCommand extends ShellCommand {
return -1;
}
if (arg == null) {
- batteryTracker.mDebugUidPercentages.clear();
+ batteryTracker.clearDebugUidPercentage();
return 0;
}
String[] pairs = arg.split(",");
int[] uids = new int[pairs.length];
- double[] values = new double[pairs.length];
+ double[][] values = new double[pairs.length][];
try {
for (int i = 0; i < pairs.length; i++) {
String[] pair = pairs[i].split("=");
@@ -3270,16 +3271,21 @@ final class ActivityManagerShellCommand extends ShellCommand {
return -1;
}
uids[i] = Integer.parseInt(pair[0]);
- values[i] = Double.parseDouble(pair[1]);
+ final String[] vals = pair[1].split(":");
+ if (vals.length != BATTERY_USAGE_COUNT) {
+ getErrPrintWriter().println("Malformed input");
+ return -1;
+ }
+ values[i] = new double[vals.length];
+ for (int j = 0; j < vals.length; j++) {
+ values[i][j] = Double.parseDouble(vals[j]);
+ }
}
} catch (NumberFormatException e) {
getErrPrintWriter().println("Malformed input");
return -1;
}
- batteryTracker.mDebugUidPercentages.clear();
- for (int i = 0; i < pairs.length; i++) {
- batteryTracker.mDebugUidPercentages.put(uids[i], values[i]);
- }
+ batteryTracker.setDebugUidPercentage(uids, values);
return 0;
}
diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
index 75de3a167a5f1..7b76de2c61668 100644
--- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java
@@ -18,6 +18,7 @@ package com.android.server.am;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.AppBatteryTracker.BATTERY_USAGE_NONE;
import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNAMESPACE_PREFIX;
import static com.android.server.am.BaseAppStateDurationsTracker.EVENT_NUM;
@@ -32,6 +33,8 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy;
import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates;
import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
+import com.android.server.am.AppBatteryTracker.BatteryUsage;
+import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
import com.android.server.am.BaseAppStateDurationsTracker.EventListener;
import com.android.server.am.BaseAppStateTimeEvents.BaseTimeEvent;
import com.android.server.am.BaseAppStateTracker.Injector;
@@ -97,7 +100,8 @@ final class AppBatteryExemptionTracker
if (!mInjector.getPolicy().isEnabled()) {
return;
}
- final double batteryUsage = mAppRestrictionController.getUidBatteryUsage(uid);
+ final ImmutableBatteryUsage batteryUsage = mAppRestrictionController
+ .getUidBatteryUsage(uid);
synchronized (mLock) {
UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME);
if (pkg == null) {
@@ -120,22 +124,23 @@ final class AppBatteryExemptionTracker
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- double getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
if (!mInjector.getPolicy().isEnabled()) {
- return 0.0d;
+ return BATTERY_USAGE_NONE;
}
- Pair result;
+ Pair result;
synchronized (mLock) {
final UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME);
if (pkg == null) {
- return 0.0d;
+ return BATTERY_USAGE_NONE;
}
result = pkg.getBatteryUsageSince(since, now);
}
- if (result.second > 0.0d) {
+ if (!result.second.isEmpty()) {
// We have an open event (just start, no stop), get the battery usage till now.
- final double batteryUsage = mAppRestrictionController.getUidBatteryUsage(uid);
- return result.first + batteryUsage - result.second;
+ final ImmutableBatteryUsage batteryUsage = mAppRestrictionController
+ .getUidBatteryUsage(uid);
+ return result.first.mutate().add(batteryUsage).subtract(result.second).unmutate();
}
return result.first;
}
@@ -156,7 +161,7 @@ final class AppBatteryExemptionTracker
* @param batteryUsage The background current drain since the system boots.
* @param eventType One of EVENT_TYPE_* defined in the class BaseAppStateDurationsTracker.
*/
- void addEvent(boolean start, long now, double batteryUsage, int eventType) {
+ void addEvent(boolean start, long now, ImmutableBatteryUsage batteryUsage, int eventType) {
if (start) {
addEvent(start, new UidStateEventWithBattery(start, now, batteryUsage, null),
eventType);
@@ -169,7 +174,8 @@ final class AppBatteryExemptionTracker
return;
}
addEvent(start, new UidStateEventWithBattery(start, now,
- batteryUsage - last.getBatteryUsage(), last), eventType);
+ batteryUsage.mutate().subtract(last.getBatteryUsage()).unmutate(), last),
+ eventType);
}
}
@@ -183,34 +189,37 @@ final class AppBatteryExemptionTracker
* the second value is the battery usage since the system boots, if there is
* an open event(just start, no stop) at the end of the duration.
*/
- Pair getBatteryUsageSince(long since, long now, int eventType) {
+ Pair getBatteryUsageSince(long since,
+ long now, int eventType) {
return getBatteryUsageSince(since, now, mEvents[eventType]);
}
- private Pair getBatteryUsageSince(long since, long now,
- LinkedList events) {
+ private Pair getBatteryUsageSince(long since,
+ long now, LinkedList events) {
if (events == null || events.size() == 0) {
- return Pair.create(0.0d, 0.0d);
+ return Pair.create(BATTERY_USAGE_NONE, BATTERY_USAGE_NONE);
}
- double batteryUsage = 0.0d;
+ final BatteryUsage batteryUsage = new BatteryUsage();
UidStateEventWithBattery lastEvent = null;
for (UidStateEventWithBattery event : events) {
lastEvent = event;
if (event.getTimestamp() < since || event.isStart()) {
continue;
}
- batteryUsage += event.getBatteryUsage(since, Math.min(now, event.getTimestamp()));
+ batteryUsage.add(event.getBatteryUsage(since, Math.min(now, event.getTimestamp())));
if (now <= event.getTimestamp()) {
break;
}
}
- return Pair.create(batteryUsage, lastEvent.isStart() ? lastEvent.getBatteryUsage() : 0);
+ return Pair.create(batteryUsage.unmutate(), lastEvent.isStart()
+ ? lastEvent.getBatteryUsage() : BATTERY_USAGE_NONE);
}
/**
* @return The aggregated battery usage amongst all the event types we're tracking.
*/
- Pair getBatteryUsageSince(long since, long now) {
+ Pair getBatteryUsageSince(long since,
+ long now) {
LinkedList result = new LinkedList<>();
for (int i = 0; i < mEvents.length; i++) {
result = add(result, mEvents[i]);
@@ -236,7 +245,7 @@ final class AppBatteryExemptionTracker
UidStateEventWithBattery l = itl.next(), r = itr.next();
LinkedList dest = new LinkedList<>();
boolean actl = false, actr = false, overlapping = false;
- double batteryUsage = 0.0d;
+ final BatteryUsage batteryUsage = new BatteryUsage();
long recentActTs = 0, overlappingDuration = 0;
for (long lts = l.getTimestamp(), rts = r.getTimestamp();
lts != Long.MAX_VALUE || rts != Long.MAX_VALUE;) {
@@ -245,8 +254,8 @@ final class AppBatteryExemptionTracker
if (lts == rts) {
earliest = l;
// we'll deal with the double counting problem later.
- batteryUsage += actl ? l.getBatteryUsage() : 0.0d;
- batteryUsage += actr ? r.getBatteryUsage() : 0.0d;
+ if (actl) batteryUsage.add(l.getBatteryUsage());
+ if (actr) batteryUsage.add(r.getBatteryUsage());
overlappingDuration += overlapping && (actl || actr)
? (lts - recentActTs) : 0;
actl = !actl;
@@ -255,13 +264,13 @@ final class AppBatteryExemptionTracker
rts = itr.hasNext() ? (r = itr.next()).getTimestamp() : Long.MAX_VALUE;
} else if (lts < rts) {
earliest = l;
- batteryUsage += actl ? l.getBatteryUsage() : 0.0d;
+ if (actl) batteryUsage.add(l.getBatteryUsage());
overlappingDuration += overlapping && actl ? (lts - recentActTs) : 0;
actl = !actl;
lts = itl.hasNext() ? (l = itl.next()).getTimestamp() : Long.MAX_VALUE;
} else {
earliest = r;
- batteryUsage += actr ? r.getBatteryUsage() : 0.0d;
+ if (actr) batteryUsage.add(r.getBatteryUsage());
overlappingDuration += overlapping && actr ? (rts - recentActTs) : 0;
actr = !actr;
rts = itr.hasNext() ? (r = itr.next()).getTimestamp() : Long.MAX_VALUE;
@@ -281,12 +290,12 @@ final class AppBatteryExemptionTracker
final long durationWithOverlapping = duration + overlappingDuration;
// Get the proportional batteryUsage.
if (durationWithOverlapping != 0) {
- batteryUsage *= duration * 1.0d / durationWithOverlapping;
+ batteryUsage.scale(duration * 1.0d / durationWithOverlapping);
+ event.update(lastEvent, new ImmutableBatteryUsage(batteryUsage));
} else {
- batteryUsage = 0.0d;
+ event.update(lastEvent, BATTERY_USAGE_NONE);
}
- event.update(lastEvent, batteryUsage);
- batteryUsage = 0.0d;
+ batteryUsage.setTo(BATTERY_USAGE_NONE);
overlappingDuration = 0;
}
dest.add(event);
@@ -322,14 +331,15 @@ final class AppBatteryExemptionTracker
* the system boots if the {@link #mIsStart} is true, but will be the delta of the bg
* battery usage since the start event if the {@link #mIsStart} is false.
*/
- private double mBatteryUsage;
+ private @NonNull ImmutableBatteryUsage mBatteryUsage;
/**
* The peer event of this pair (a pair of start/stop events).
*/
private @Nullable UidStateEventWithBattery mPeer;
- UidStateEventWithBattery(boolean isStart, long now, double batteryUsage,
+ UidStateEventWithBattery(boolean isStart, long now,
+ @NonNull ImmutableBatteryUsage batteryUsage,
@Nullable UidStateEventWithBattery peer) {
super(now);
mIsStart = isStart;
@@ -355,15 +365,19 @@ final class AppBatteryExemptionTracker
}
if (mPeer != null) {
// Reduce the bg battery usage proportionally.
- final double batteryUsage = mPeer.getBatteryUsage();
+ final ImmutableBatteryUsage batteryUsage = mPeer.getBatteryUsage();
mPeer.mBatteryUsage = mPeer.getBatteryUsage(timestamp, mPeer.mTimestamp);
// Update the battery data of the start event too.
- mBatteryUsage += batteryUsage - mPeer.mBatteryUsage;
+ mBatteryUsage = mBatteryUsage.mutate()
+ .add(batteryUsage)
+ .subtract(mPeer.mBatteryUsage)
+ .unmutate();
}
mTimestamp = timestamp;
}
- void update(@NonNull UidStateEventWithBattery peer, double batteryUsage) {
+ void update(@NonNull UidStateEventWithBattery peer,
+ @NonNull ImmutableBatteryUsage batteryUsage) {
mPeer = peer;
peer.mPeer = this;
mBatteryUsage = batteryUsage;
@@ -373,18 +387,19 @@ final class AppBatteryExemptionTracker
return mIsStart;
}
- double getBatteryUsage(long start, long end) {
+ @NonNull ImmutableBatteryUsage getBatteryUsage(long start, long end) {
if (mIsStart || start >= mTimestamp || end <= start) {
- return 0.0d;
+ return BATTERY_USAGE_NONE;
}
start = Math.max(start, mPeer.mTimestamp);
end = Math.min(end, mTimestamp);
final long totalDur = mTimestamp - mPeer.mTimestamp;
final long inputDur = end - start;
- return totalDur != 0 ? mBatteryUsage * (1.0d * inputDur) / totalDur : 0.0d;
+ return totalDur != 0 ? (totalDur == inputDur ? mBatteryUsage : mBatteryUsage.mutate()
+ .scale((1.0d * inputDur) / totalDur).unmutate()) : BATTERY_USAGE_NONE;
}
- double getBatteryUsage() {
+ @NonNull ImmutableBatteryUsage getBatteryUsage() {
return mBatteryUsage;
}
@@ -404,14 +419,20 @@ final class AppBatteryExemptionTracker
final UidStateEventWithBattery otherEvent = (UidStateEventWithBattery) other;
return otherEvent.mIsStart == mIsStart
&& otherEvent.mTimestamp == mTimestamp
- && Double.compare(otherEvent.mBatteryUsage, mBatteryUsage) == 0;
+ && mBatteryUsage.equals(otherEvent.mBatteryUsage);
+ }
+
+ @Override
+ public String toString() {
+ return "UidStateEventWithBattery(" + mIsStart + ", " + mTimestamp
+ + ", " + mBatteryUsage + ")";
}
@Override
public int hashCode() {
return (Boolean.hashCode(mIsStart) * 31
+ Long.hashCode(mTimestamp)) * 31
- + Double.hashCode(mBatteryUsage);
+ + mBatteryUsage.hashCode();
}
}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index b8f5c50b2e1b9..99808b6c38a16 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -28,8 +28,10 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACT
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.BatteryConsumer.POWER_COMPONENT_ANY;
import static android.os.BatteryConsumer.PROCESS_STATE_BACKGROUND;
+import static android.os.BatteryConsumer.PROCESS_STATE_COUNT;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND;
import static android.os.BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.os.BatteryConsumer.PROCESS_STATE_UNSPECIFIED;
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.util.TimeUtils.formatTime;
@@ -40,11 +42,13 @@ import static com.android.server.am.BaseAppStateTracker.ONE_DAY;
import static com.android.server.am.BaseAppStateTracker.ONE_MINUTE;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager.RestrictionLevel;
import android.content.Context;
import android.content.pm.ServiceInfo;
import android.os.BatteryConsumer;
+import android.os.BatteryConsumer.Dimensions;
import android.os.BatteryStatsInternal;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
@@ -58,7 +62,6 @@ import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.util.SparseDoubleArray;
import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
@@ -98,12 +101,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
static final long BATTERY_USAGE_STATS_POLLING_MIN_INTERVAL_MS_LONG = 5 * ONE_MINUTE; // 5 mins
static final long BATTERY_USAGE_STATS_POLLING_MIN_INTERVAL_MS_DEBUG = 2_000L; // 2s
- static final BatteryConsumer.Dimensions BATT_DIMEN_FG =
- new BatteryConsumer.Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_FOREGROUND);
- static final BatteryConsumer.Dimensions BATT_DIMEN_BG =
- new BatteryConsumer.Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_BACKGROUND);
- static final BatteryConsumer.Dimensions BATT_DIMEN_FGS =
- new BatteryConsumer.Dimensions(POWER_COMPONENT_ANY, PROCESS_STATE_FOREGROUND_SERVICE);
+ static final ImmutableBatteryUsage BATTERY_USAGE_NONE = new ImmutableBatteryUsage();
private final Runnable mBgBatteryUsageStatsPolling = this::updateBatteryUsageStatsAndCheck;
private final Runnable mBgBatteryUsageStatsCheck = this::checkBatteryUsageStats;
@@ -132,29 +130,30 @@ final class AppBatteryTracker extends BaseAppStateTracker
* the last battery stats reset prior to that (whoever is earlier).
*/
@GuardedBy("mLock")
- private final SparseDoubleArray mUidBatteryUsage = new SparseDoubleArray();
+ private final SparseArray mUidBatteryUsage = new SparseArray<>();
/**
* The battery usage for each UID, in the rolling window of the past.
*/
@GuardedBy("mLock")
- private final SparseDoubleArray mUidBatteryUsageInWindow = new SparseDoubleArray();
+ private final SparseArray mUidBatteryUsageInWindow = new SparseArray<>();
/**
* The uid battery usage stats data from our last query, it consists of the data since
* last battery stats reset.
*/
@GuardedBy("mLock")
- private final SparseDoubleArray mLastUidBatteryUsage = new SparseDoubleArray();
+ private final SparseArray mLastUidBatteryUsage = new SparseArray<>();
// No lock is needed.
- private final SparseDoubleArray mTmpUidBatteryUsage = new SparseDoubleArray();
+ private final SparseArray mTmpUidBatteryUsage = new SparseArray<>();
// No lock is needed.
- private final SparseDoubleArray mTmpUidBatteryUsage2 = new SparseDoubleArray();
+ private final SparseArray mTmpUidBatteryUsage2 = new SparseArray<>();
// No lock is needed.
- private final SparseDoubleArray mTmpUidBatteryUsageInWindow = new SparseDoubleArray();
+ private final SparseArray mTmpUidBatteryUsageInWindow =
+ new SparseArray<>();
// No lock is needed.
private final ArraySet mTmpUserIds = new ArraySet<>();
@@ -166,7 +165,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
private long mLastUidBatteryUsageStartTs;
// For debug only.
- final SparseDoubleArray mDebugUidPercentages = new SparseDoubleArray();
+ private final SparseArray mDebugUidPercentages = new SparseArray<>();
AppBatteryTracker(Context context, AppRestrictionController controller) {
this(context, controller, null, null);
@@ -277,18 +276,24 @@ final class AppBatteryTracker extends BaseAppStateTracker
*
*/
@Override
- public double getUidBatteryUsage(int uid) {
+ @NonNull
+ public ImmutableBatteryUsage getUidBatteryUsage(int uid) {
final long now = mInjector.currentTimeMillis();
final boolean updated = updateBatteryUsageStatsIfNecessary(now, false);
synchronized (mLock) {
if (updated) {
// We just got fresh data, schedule a check right a way.
mBgHandler.removeCallbacks(mBgBatteryUsageStatsPolling);
- if (!mBgHandler.hasCallbacks(mBgBatteryUsageStatsCheck)) {
- mBgHandler.post(mBgBatteryUsageStatsCheck);
- }
+ scheduleBgBatteryUsageStatsCheck();
}
- return mUidBatteryUsage.get(uid, 0.0d);
+ final BatteryUsage usage = mUidBatteryUsage.get(uid);
+ return usage != null ? new ImmutableBatteryUsage(usage) : BATTERY_USAGE_NONE;
+ }
+ }
+
+ private void scheduleBgBatteryUsageStatsCheck() {
+ if (!mBgHandler.hasCallbacks(mBgBatteryUsageStatsCheck)) {
+ mBgHandler.post(mBgBatteryUsageStatsCheck);
}
}
@@ -309,27 +314,32 @@ final class AppBatteryTracker extends BaseAppStateTracker
final long now = SystemClock.elapsedRealtime();
final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
try {
- final SparseDoubleArray uidConsumers = mTmpUidBatteryUsageInWindow;
+ final SparseArray uidConsumers = mTmpUidBatteryUsageInWindow;
synchronized (mLock) {
copyUidBatteryUsage(mUidBatteryUsageInWindow, uidConsumers);
}
final long since = Math.max(0, now - bgPolicy.mBgCurrentDrainWindowMs);
for (int i = 0, size = uidConsumers.size(); i < size; i++) {
final int uid = uidConsumers.keyAt(i);
- final double actualUsage = uidConsumers.valueAt(i);
- final double exemptedUsage = mAppRestrictionController
+ final ImmutableBatteryUsage actualUsage = uidConsumers.valueAt(i);
+ final ImmutableBatteryUsage exemptedUsage = mAppRestrictionController
.getUidBatteryExemptedUsageSince(uid, since, now);
// It's possible the exemptedUsage could be larger than actualUsage,
// as the former one is an approximate value.
- final double bgUsage = Math.max(0.0d, actualUsage - exemptedUsage);
- final double percentage = bgPolicy.getPercentage(uid, bgUsage);
+ final BatteryUsage bgUsage = actualUsage.mutate()
+ .subtract(exemptedUsage)
+ .calcPercentage(uid, bgPolicy);
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
Slog.i(TAG, String.format(
- "UID %d: %.3f mAh (or %4.2f%%) %.3f %.3f over the past %s",
- uid, bgUsage, percentage, exemptedUsage, actualUsage,
+ "UID %d: %s (%s) | %s | %s over the past %s",
+ uid,
+ bgUsage.toString(),
+ bgUsage.percentageToString(),
+ exemptedUsage.toString(),
+ actualUsage.toString(),
TimeUtils.formatDuration(bgPolicy.mBgCurrentDrainWindowMs)));
}
- bgPolicy.handleUidBatteryUsage(uid, percentage);
+ bgPolicy.handleUidBatteryUsage(uid, bgUsage);
}
// For debugging only.
for (int i = 0, size = mDebugUidPercentages.size(); i < size; i++) {
@@ -384,7 +394,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
private void updateBatteryUsageStatsOnce(long now) {
final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
final ArraySet userIds = mTmpUserIds;
- final SparseDoubleArray buf = mTmpUidBatteryUsage;
+ final SparseArray buf = mTmpUidBatteryUsage;
final BatteryStatsInternal batteryStatsInternal = mInjector.getBatteryStatsInternal();
final long windowSize = bgPolicy.mBgCurrentDrainWindowMs;
@@ -453,26 +463,26 @@ final class AppBatteryTracker extends BaseAppStateTracker
for (int i = 0, size = buf.size(); i < size; i++) {
final int uid = buf.keyAt(i);
final int index = mUidBatteryUsage.indexOfKey(uid);
- final double lastUsage = mLastUidBatteryUsage.get(uid, 0.0d);
- final double curUsage = buf.valueAt(i);
- final double before;
+ final BatteryUsage lastUsage = mLastUidBatteryUsage.get(uid, BATTERY_USAGE_NONE);
+ final BatteryUsage curUsage = buf.valueAt(i);
+ final BatteryUsage before;
if (index >= 0) {
before = mUidBatteryUsage.valueAt(index);
- mUidBatteryUsage.setValueAt(index, before - lastUsage + curUsage);
+ before.subtract(lastUsage).add(curUsage);
} else {
- before = 0.0d;
+ before = BATTERY_USAGE_NONE;
mUidBatteryUsage.put(uid, curUsage);
}
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
- final double actualDelta = curUsage - lastUsage;
+ final BatteryUsage actualDelta = new BatteryUsage(curUsage).subtract(lastUsage);
String msg = "Updating mUidBatteryUsage uid=" + uid + ", before=" + before
- + ", after=" + mUidBatteryUsage.get(uid, 0.0d)
+ + ", after=" + mUidBatteryUsage.get(uid, BATTERY_USAGE_NONE)
+ ", delta=" + actualDelta
+ ", last=" + lastUsage
+ ", curStart=" + curStart
+ ", lastLastStart=" + lastUidBatteryUsageStartTs
+ ", thisLastStart=" + mLastUidBatteryUsageStartTs;
- if (actualDelta < 0.0d) {
+ if (!actualDelta.isValid()) {
// Something is wrong, the battery usage shouldn't be negative.
Slog.e(TAG, msg);
} else {
@@ -508,8 +518,8 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
}
- private static BatteryUsageStats updateBatteryUsageStatsOnceInternal(long expectedDuration,
- SparseDoubleArray buf, BatteryUsageStatsQuery.Builder builder,
+ private BatteryUsageStats updateBatteryUsageStatsOnceInternal(long expectedDuration,
+ SparseArray buf, BatteryUsageStatsQuery.Builder builder,
ArraySet userIds, BatteryStatsInternal batteryStatsInternal) {
for (int i = 0, size = userIds.size(); i < size; i++) {
builder.addUser(userIds.valueAt(i));
@@ -527,16 +537,19 @@ final class AppBatteryTracker extends BaseAppStateTracker
final long end = stats.getStatsEndTimestamp();
final double scale = expectedDuration > 0
? (expectedDuration * 1.0d) / (end - start) : 1.0d;
+ final AppBatteryPolicy bgPolicy = mInjector.getPolicy();
for (UidBatteryConsumer uidConsumer : uidConsumers) {
// TODO: b/200326767 - as we are not supporting per proc state attribution yet,
// we couldn't distinguish between a real FGS vs. a bound FGS proc state.
final int uid = uidConsumer.getUid();
- final double bgUsage = getBgUsage(uidConsumer) * scale;
+ final BatteryUsage bgUsage = new BatteryUsage(uidConsumer, bgPolicy)
+ .scale(scale);
int index = buf.indexOfKey(uid);
if (index < 0) {
buf.put(uid, bgUsage);
} else {
- buf.setValueAt(index, buf.valueAt(index) + bgUsage);
+ final BatteryUsage before = buf.valueAt(index);
+ before.add(bgUsage);
}
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
Slog.i(TAG, "updateBatteryUsageStatsOnceInternal uid=" + uid
@@ -549,32 +562,19 @@ final class AppBatteryTracker extends BaseAppStateTracker
return stats;
}
- private static void copyUidBatteryUsage(SparseDoubleArray source, SparseDoubleArray dest) {
+ private static void copyUidBatteryUsage(SparseArray extends BatteryUsage> source,
+ SparseArray dest) {
dest.clear();
for (int i = source.size() - 1; i >= 0; i--) {
- dest.put(source.keyAt(i), source.valueAt(i));
+ dest.put(source.keyAt(i), new ImmutableBatteryUsage(source.valueAt(i)));
}
}
- private static void copyUidBatteryUsage(SparseDoubleArray source, SparseDoubleArray dest,
- double scale) {
+ private static void copyUidBatteryUsage(SparseArray extends BatteryUsage> source,
+ SparseArray dest, double scale) {
dest.clear();
for (int i = source.size() - 1; i >= 0; i--) {
- dest.put(source.keyAt(i), source.valueAt(i) * scale);
- }
- }
-
- private static double getBgUsage(final UidBatteryConsumer uidConsumer) {
- return getConsumedPowerNoThrow(uidConsumer, BATT_DIMEN_BG)
- + getConsumedPowerNoThrow(uidConsumer, BATT_DIMEN_FGS);
- }
-
- private static double getConsumedPowerNoThrow(final UidBatteryConsumer uidConsumer,
- final BatteryConsumer.Dimensions dimens) {
- try {
- return uidConsumer.getConsumedPower(dimens);
- } catch (IllegalArgumentException e) {
- return 0.0d;
+ dest.put(source.keyAt(i), new ImmutableBatteryUsage(source.valueAt(i), scale));
}
}
@@ -602,6 +602,19 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
}
+ void setDebugUidPercentage(int[] uids, double[][] percentages) {
+ mDebugUidPercentages.clear();
+ for (int i = 0; i < uids.length; i++) {
+ mDebugUidPercentages.put(uids[i], new BatteryUsage().setPercentage(percentages[i]));
+ }
+ scheduleBgBatteryUsageStatsCheck();
+ }
+
+ void clearDebugUidPercentage() {
+ mDebugUidPercentages.clear();
+ scheduleBgBatteryUsageStatsCheck();
+ }
+
@VisibleForTesting
void reset() {
synchronized (mLock) {
@@ -620,7 +633,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
pw.println("APP BATTERY STATE TRACKER:");
updateBatteryUsageStatsIfNecessary(mInjector.currentTimeMillis(), true);
synchronized (mLock) {
- final SparseDoubleArray uidConsumers = mUidBatteryUsageInWindow;
+ final SparseArray uidConsumers = mUidBatteryUsageInWindow;
pw.print(" " + prefix);
pw.print(" Last battery usage start=");
TimeUtils.dumpTime(pw, mLastUidBatteryUsageStartTs);
@@ -638,25 +651,284 @@ final class AppBatteryTracker extends BaseAppStateTracker
} else {
for (int i = 0, size = uidConsumers.size(); i < size; i++) {
final int uid = uidConsumers.keyAt(i);
- final double bgUsage = uidConsumers.valueAt(i);
- final double exemptedUsage = mAppRestrictionController
- .getUidBatteryExemptedUsageSince(uid, since, now);
- final double reportedUsage = Math.max(0.0d, bgUsage - exemptedUsage);
- pw.format("%s%s: [%s] %.3f mAh (%4.2f%%) | %.3f mAh (%4.2f%%) | "
- + "%.3f mAh (%4.2f%%) | %.3f mAh\n",
+ final BatteryUsage bgUsage = uidConsumers.valueAt(i)
+ .calcPercentage(uid, bgPolicy);
+ final BatteryUsage exemptedUsage = mAppRestrictionController
+ .getUidBatteryExemptedUsageSince(uid, since, now)
+ .calcPercentage(uid, bgPolicy);
+ final BatteryUsage reportedUsage = new BatteryUsage(bgUsage)
+ .subtract(exemptedUsage)
+ .calcPercentage(uid, bgPolicy);
+ pw.format("%s%s: [%s] %s (%s) | %s (%s) | %s (%s) | %s\n",
newPrefix, UserHandle.formatUid(uid),
PowerExemptionManager.reasonCodeToString(bgPolicy.shouldExemptUid(uid)),
- bgUsage , bgPolicy.getPercentage(uid, bgUsage),
- exemptedUsage, bgPolicy.getPercentage(-1, exemptedUsage),
- reportedUsage, bgPolicy.getPercentage(-1, reportedUsage),
- mUidBatteryUsage.get(uid, 0.0d));
+ bgUsage.toString(),
+ bgUsage.percentageToString(),
+ exemptedUsage.toString(),
+ exemptedUsage.percentageToString(),
+ reportedUsage.toString(),
+ reportedUsage.percentageToString(),
+ mUidBatteryUsage.get(uid, BATTERY_USAGE_NONE).toString());
}
}
}
super.dump(pw, prefix);
}
+ static class BatteryUsage {
+ static final int BATTERY_USAGE_INDEX_UNSPECIFIED = PROCESS_STATE_UNSPECIFIED;
+ static final int BATTERY_USAGE_INDEX_FOREGROUND = PROCESS_STATE_FOREGROUND;
+ static final int BATTERY_USAGE_INDEX_BACKGROUND = PROCESS_STATE_BACKGROUND;
+ static final int BATTERY_USAGE_INDEX_FOREGROUND_SERVICE = PROCESS_STATE_FOREGROUND_SERVICE;
+ static final int BATTERY_USAGE_COUNT = PROCESS_STATE_COUNT;
+
+ static final Dimensions[] BATT_DIMENS = new Dimensions[] {
+ new Dimensions(AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS,
+ PROCESS_STATE_UNSPECIFIED),
+ new Dimensions(AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS,
+ PROCESS_STATE_FOREGROUND),
+ new Dimensions(AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS,
+ PROCESS_STATE_BACKGROUND),
+ new Dimensions(AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS,
+ PROCESS_STATE_FOREGROUND_SERVICE),
+ };
+
+ @NonNull double[] mUsage;
+ @Nullable double[] mPercentage;
+
+ BatteryUsage() {
+ this(0.0d, 0.0d, 0.0d, 0.0d);
+ }
+
+ BatteryUsage(double unspecifiedUsage, double fgUsage, double bgUsage, double fgsUsage) {
+ mUsage = new double[] {unspecifiedUsage, fgUsage, bgUsage, fgsUsage};
+ }
+
+ BatteryUsage(@NonNull double[] usage) {
+ mUsage = usage;
+ }
+
+ BatteryUsage(@NonNull BatteryUsage other, double scale) {
+ this(other);
+ scaleInternal(scale);
+ }
+
+ BatteryUsage(@NonNull BatteryUsage other) {
+ mUsage = new double[other.mUsage.length];
+ setToInternal(other);
+ }
+
+ BatteryUsage(@NonNull UidBatteryConsumer consumer, @NonNull AppBatteryPolicy policy) {
+ final Dimensions[] dims = policy.mBatteryDimensions;
+ mUsage = new double[] {
+ getConsumedPowerNoThrow(consumer, dims[BATTERY_USAGE_INDEX_UNSPECIFIED]),
+ getConsumedPowerNoThrow(consumer, dims[BATTERY_USAGE_INDEX_FOREGROUND]),
+ getConsumedPowerNoThrow(consumer, dims[BATTERY_USAGE_INDEX_BACKGROUND]),
+ getConsumedPowerNoThrow(consumer, dims[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE])
+ };
+ }
+
+ BatteryUsage setTo(@NonNull BatteryUsage other) {
+ return setToInternal(other);
+ }
+
+ private BatteryUsage setToInternal(@NonNull BatteryUsage other) {
+ for (int i = 0; i < other.mUsage.length; i++) {
+ mUsage[i] = other.mUsage[i];
+ }
+ return this;
+ }
+
+ BatteryUsage add(@NonNull BatteryUsage other) {
+ for (int i = 0; i < other.mUsage.length; i++) {
+ mUsage[i] += other.mUsage[i];
+ }
+ return this;
+ }
+
+ BatteryUsage subtract(@NonNull BatteryUsage other) {
+ for (int i = 0; i < other.mUsage.length; i++) {
+ mUsage[i] = Math.max(0.0d, mUsage[i] - other.mUsage[i]);
+ }
+ return this;
+ }
+
+ BatteryUsage scale(double scale) {
+ return scaleInternal(scale);
+ }
+
+ private BatteryUsage scaleInternal(double scale) {
+ for (int i = 0; i < mUsage.length; i++) {
+ mUsage[i] *= scale;
+ }
+ return this;
+ }
+
+ ImmutableBatteryUsage unmutate() {
+ return new ImmutableBatteryUsage(this);
+ }
+
+ BatteryUsage calcPercentage(int uid, @NonNull AppBatteryPolicy policy) {
+ if (mPercentage == null || mPercentage.length != mUsage.length) {
+ mPercentage = new double[mUsage.length];
+ }
+ policy.calcPercentage(uid, mUsage, mPercentage);
+ return this;
+ }
+
+ BatteryUsage setPercentage(@NonNull double[] percentage) {
+ mPercentage = percentage;
+ return this;
+ }
+
+ double[] getPercentage() {
+ return mPercentage;
+ }
+
+ String percentageToString() {
+ return formatBatteryUsagePercentage(mPercentage);
+ }
+
+ @Override
+ public String toString() {
+ return formatBatteryUsage(mUsage);
+ }
+
+ boolean isValid() {
+ for (int i = 0; i < mUsage.length; i++) {
+ if (mUsage[i] < 0.0d) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ boolean isEmpty() {
+ for (int i = 0; i < mUsage.length; i++) {
+ if (mUsage[i] > 0.0d) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null) {
+ return false;
+ }
+ final BatteryUsage otherUsage = (BatteryUsage) other;
+ for (int i = 0; i < mUsage.length; i++) {
+ if (Double.compare(mUsage[i], otherUsage.mUsage[i]) != 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 0;
+ for (int i = 0; i < mUsage.length; i++) {
+ hashCode = Double.hashCode(mUsage[i]) + hashCode * 31;
+ }
+ return hashCode;
+ }
+
+ private static String formatBatteryUsage(double[] usage) {
+ return String.format("%.3f %.3f %.3f %.3f mAh",
+ usage[BATTERY_USAGE_INDEX_UNSPECIFIED],
+ usage[BATTERY_USAGE_INDEX_FOREGROUND],
+ usage[BATTERY_USAGE_INDEX_BACKGROUND],
+ usage[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
+ }
+
+ static String formatBatteryUsagePercentage(double[] percentage) {
+ return String.format("%4.2f%% %4.2f%% %4.2f%% %4.2f%%",
+ percentage[BATTERY_USAGE_INDEX_UNSPECIFIED],
+ percentage[BATTERY_USAGE_INDEX_FOREGROUND],
+ percentage[BATTERY_USAGE_INDEX_BACKGROUND],
+ percentage[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]);
+ }
+
+ private static double getConsumedPowerNoThrow(final UidBatteryConsumer uidConsumer,
+ final Dimensions dimens) {
+ try {
+ return uidConsumer.getConsumedPower(dimens);
+ } catch (IllegalArgumentException e) {
+ return 0.0d;
+ }
+ }
+ }
+
+ static final class ImmutableBatteryUsage extends BatteryUsage {
+ ImmutableBatteryUsage() {
+ super();
+ }
+
+ ImmutableBatteryUsage(double unspecifiedUsage, double fgUsage, double bgUsage,
+ double fgsUsage) {
+ super(unspecifiedUsage, fgUsage, bgUsage, fgsUsage);
+ }
+
+ ImmutableBatteryUsage(@NonNull double[] usage) {
+ super(usage);
+ }
+
+ ImmutableBatteryUsage(@NonNull BatteryUsage other, double scale) {
+ super(other, scale);
+ }
+
+ ImmutableBatteryUsage(@NonNull BatteryUsage other) {
+ super(other);
+ }
+
+ ImmutableBatteryUsage(@NonNull UidBatteryConsumer consumer,
+ @NonNull AppBatteryPolicy policy) {
+ super(consumer, policy);
+ }
+
+ @Override
+ BatteryUsage setTo(@NonNull BatteryUsage other) {
+ throw new RuntimeException("Readonly");
+ }
+
+ @Override
+ BatteryUsage add(@NonNull BatteryUsage other) {
+ throw new RuntimeException("Readonly");
+ }
+
+ @Override
+ BatteryUsage subtract(@NonNull BatteryUsage other) {
+ throw new RuntimeException("Readonly");
+ }
+
+ @Override
+ BatteryUsage scale(double scale) {
+ throw new RuntimeException("Readonly");
+ }
+
+ @Override
+ BatteryUsage setPercentage(@NonNull double[] percentage) {
+ throw new RuntimeException("Readonly");
+ }
+
+ BatteryUsage mutate() {
+ return new BatteryUsage(this);
+ }
+ }
+
static final class AppBatteryPolicy extends BaseAppStatePolicy {
+ /**
+ * The type of battery usage we could choose to apply the policy on.
+ *
+ * Must be in sync with android.os.BatteryConsumer.PROCESS_STATE_*.
+ */
+ static final int BATTERY_USAGE_TYPE_UNSPECIFIED = 1;
+ static final int BATTERY_USAGE_TYPE_FOREGROUND = 1 << 1;
+ static final int BATTERY_USAGE_TYPE_BACKGROUND = 1 << 2;
+ static final int BATTERY_USAGE_TYPE_FOREGROUND_SERVICE = 1 << 3;
+
/**
* Whether or not we should enable the monitoring on background current drains.
*/
@@ -728,6 +1000,30 @@ final class AppBatteryTracker extends BaseAppStateTracker
DEVICE_CONFIG_SUBNAMESPACE_PREFIX
+ "current_drain_event_duration_based_threshold_enabled";
+ /**
+ * The types of battery drain we're checking on each app; if the sum of the battery drain
+ * exceeds the threshold, it'll be moved to restricted standby bucket; the type here
+ * must be one of, or combination of {@link #BATTERY_USAGE_TYPE_BACKGROUND} and
+ * {@link #BATTERY_USAGE_TYPE_FOREGROUND_SERVICE}.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_types_to_restricted_bucket";
+
+ /**
+ * The types of battery drain we're checking on each app; if the sum of the battery drain
+ * exceeds the threshold, it'll be moved to background restricted level; the type here
+ * must be one of, or combination of {@link #BATTERY_USAGE_TYPE_BACKGROUND} and
+ * {@link #BATTERY_USAGE_TYPE_FOREGROUND_SERVICE}.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_types_to_bg_restricted";
+
+ /**
+ * The power usage components we're monitoring.
+ */
+ static final String KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS =
+ DEVICE_CONFIG_SUBNAMESPACE_PREFIX + "current_drain_power_components";
+
/**
* Default value to {@link #mTrackerEnabled}.
*/
@@ -782,6 +1078,24 @@ final class AppBatteryTracker extends BaseAppStateTracker
static final boolean DEFAULT_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED =
false;
+ /**
+ * Default value to {@link #mBgCurrentDrainRestrictedBucketTypes}.
+ */
+ static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET =
+ BATTERY_USAGE_TYPE_BACKGROUND;
+
+ /**
+ * Default value to {@link #mBgCurrentDrainBgRestrictedTypes}.
+ */
+ static final int DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED =
+ BATTERY_USAGE_TYPE_BACKGROUND | BATTERY_USAGE_TYPE_FOREGROUND_SERVICE;
+
+ /**
+ * Default value to {@link #mBgCurrentDrainPowerComponents}.
+ **/
+ @BatteryConsumer.PowerComponent
+ static final int DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS = POWER_COMPONENT_ANY;
+
/**
* The index to {@link #mBgCurrentDrainRestrictedBucketThreshold}
* and {@link #mBgCurrentDrainBgRestrictedThreshold}.
@@ -829,6 +1143,24 @@ final class AppBatteryTracker extends BaseAppStateTracker
*/
volatile boolean mBgCurrentDrainEventDurationBasedThresholdEnabled;
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET.
+ */
+ volatile int mBgCurrentDrainRestrictedBucketTypes;
+
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED.
+ */
+ volatile int mBgCurrentDrainBgRestrictedTypes;
+
+ /**
+ * @see #KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS.
+ */
+ @BatteryConsumer.PowerComponent
+ volatile int mBgCurrentDrainPowerComponents;
+
+ volatile Dimensions[] mBatteryDimensions;
+
/**
* The capacity of the battery when fully charged in mAh.
*/
@@ -862,6 +1194,9 @@ final class AppBatteryTracker extends BaseAppStateTracker
case KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_BG_RESTRICTED:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_RESTRICTED_BUCKET:
case KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED:
+ case KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET:
+ case KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED:
+ case KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS:
updateCurrentDrainThreshold();
break;
case KEY_BG_CURRENT_DRAIN_WINDOW:
@@ -912,14 +1247,33 @@ final class AppBatteryTracker extends BaseAppStateTracker
DeviceConfig.getFloat(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_HIGH_THRESHOLD_TO_BG_RESTRICTED,
DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_HIGH_THRESHOLD);
+ mBgCurrentDrainRestrictedBucketTypes =
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET,
+ DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET);
+ mBgCurrentDrainBgRestrictedTypes =
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED,
+ DEFAULT_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED);
+ mBgCurrentDrainPowerComponents =
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS,
+ DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS);
+ if (mBgCurrentDrainPowerComponents == DEFAULT_BG_CURRENT_DRAIN_POWER_COMPONENTS) {
+ mBatteryDimensions = BatteryUsage.BATT_DIMENS;
+ } else {
+ mBatteryDimensions = new Dimensions[BatteryUsage.BATTERY_USAGE_COUNT];
+ for (int i = 0; i < BatteryUsage.BATTERY_USAGE_COUNT; i++) {
+ mBatteryDimensions[i] = new Dimensions(mBgCurrentDrainPowerComponents, i);
+ }
+ }
}
private void updateCurrentDrainWindow() {
mBgCurrentDrainWindowMs = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_BG_CURRENT_DRAIN_WINDOW,
- mBgCurrentDrainWindowMs != DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS
- ? mBgCurrentDrainWindowMs : DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
+ DEFAULT_BG_CURRENT_DRAIN_WINDOW_MS);
}
private void updateCurrentDrainMediaPlaybackMinDuration() {
@@ -970,18 +1324,58 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
}
- double getBgUsage(final UidBatteryConsumer uidConsumer) {
- return getConsumedPowerNoThrow(uidConsumer, BATT_DIMEN_BG)
- + getConsumedPowerNoThrow(uidConsumer, BATT_DIMEN_FGS);
+ double[] calcPercentage(final int uid, final double[] usage, double[] percentage) {
+ final BatteryUsage debugUsage = uid > 0 ? mTracker.mDebugUidPercentages.get(uid) : null;
+ final double[] forced = debugUsage != null ? debugUsage.getPercentage() : null;
+ for (int i = 0; i < usage.length; i++) {
+ percentage[i] = forced != null ? forced[i] : usage[i] / mBatteryFullChargeMah * 100;
+ }
+ return percentage;
}
- double getPercentage(final int uid, final double usage) {
- final double actualPercentage = usage / mBatteryFullChargeMah * 100;
- return DEBUG_BACKGROUND_BATTERY_TRACKER
- ? mTracker.mDebugUidPercentages.get(uid, actualPercentage) : actualPercentage;
+ private double sumPercentageOfTypes(double[] percentage, int types) {
+ double result = 0.0d;
+ for (int type = Integer.highestOneBit(types); type != 0;
+ type = Integer.highestOneBit(types)) {
+ final int index = Integer.numberOfTrailingZeros(type);
+ result += percentage[index];
+ types &= ~type;
+ }
+ return result;
}
- void handleUidBatteryUsage(final int uid, final double percentage) {
+ private static String batteryUsageTypesToString(int types) {
+ final StringBuilder sb = new StringBuilder("[");
+ boolean needDelimiter = false;
+ for (int type = Integer.highestOneBit(types); type != 0;
+ type = Integer.highestOneBit(types)) {
+ if (needDelimiter) {
+ sb.append('|');
+ }
+ needDelimiter = true;
+ switch (type) {
+ case BATTERY_USAGE_TYPE_UNSPECIFIED:
+ sb.append("UNSPECIFIED");
+ break;
+ case BATTERY_USAGE_TYPE_FOREGROUND:
+ sb.append("FOREGROUND");
+ break;
+ case BATTERY_USAGE_TYPE_BACKGROUND:
+ sb.append("BACKGROUND");
+ break;
+ case BATTERY_USAGE_TYPE_FOREGROUND_SERVICE:
+ sb.append("FOREGROUND_SERVICE");
+ break;
+ default:
+ return "[UNKNOWN(" + Integer.toHexString(types) + ")]";
+ }
+ types &= ~type;
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ void handleUidBatteryUsage(final int uid, final BatteryUsage usage) {
final @ReasonCode int reason = shouldExemptUid(uid);
if (reason != REASON_DENIED) {
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
@@ -992,6 +1386,10 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
boolean notifyController = false;
boolean excessive = false;
+ final double rbPercentage = sumPercentageOfTypes(usage.getPercentage(),
+ mBgCurrentDrainRestrictedBucketTypes);
+ final double brPercentage = sumPercentageOfTypes(usage.getPercentage(),
+ mBgCurrentDrainBgRestrictedTypes);
synchronized (mLock) {
final int curLevel = mTracker.mAppRestrictionController.getRestrictionLevel(uid);
if (curLevel >= RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
@@ -1003,7 +1401,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
mBgCurrentDrainWindowMs);
final int index = mHighBgBatteryPackages.indexOfKey(uid);
if (index < 0) {
- if (percentage >= mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
+ if (rbPercentage >= mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
// New findings to us, track it and let the controller know.
final long[] ts = new long[TIME_STAMP_INDEX_LAST];
ts[TIME_STAMP_INDEX_RESTRICTED_BUCKET] = now;
@@ -1012,13 +1410,13 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
} else {
final long[] ts = mHighBgBatteryPackages.valueAt(index);
- if (percentage < mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
+ if (rbPercentage < mBgCurrentDrainRestrictedBucketThreshold[thresholdIndex]) {
// it's actually back to normal, but we don't untrack it until
// explicit user interactions.
notifyController = true;
} else {
excessive = true;
- if (percentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) {
+ if (brPercentage >= mBgCurrentDrainBgRestrictedThreshold[thresholdIndex]) {
// If we're in the restricted standby bucket but still seeing high
// current drains, tell the controller again.
if (curLevel == RESTRICTION_LEVEL_RESTRICTED_BUCKET
@@ -1037,7 +1435,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
if (excessive) {
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
Slog.i(TAG, "Excessive background current drain " + uid
- + String.format(" %.2f%%", percentage) + " over "
+ + usage + " (" + usage.percentageToString() + " ) over "
+ TimeUtils.formatDuration(mBgCurrentDrainWindowMs));
}
if (notifyController) {
@@ -1048,7 +1446,7 @@ final class AppBatteryTracker extends BaseAppStateTracker
} else {
if (DEBUG_BACKGROUND_BATTERY_TRACKER) {
Slog.i(TAG, "Background current drain backs to normal " + uid
- + String.format(" %.2f%%", percentage) + " over "
+ + usage + " (" + usage.percentageToString() + " ) over "
+ TimeUtils.formatDuration(mBgCurrentDrainWindowMs));
}
// For now, we're not lifting the restrictions if the bg current drain backs to
@@ -1120,15 +1518,6 @@ final class AppBatteryTracker extends BaseAppStateTracker
}
}
- private double getConsumedPowerNoThrow(final UidBatteryConsumer uidConsumer,
- final BatteryConsumer.Dimensions dimens) {
- try {
- return uidConsumer.getConsumedPower(dimens);
- } catch (IllegalArgumentException e) {
- return 0.0d;
- }
- }
-
@VisibleForTesting
void reset() {
mHighBgBatteryPackages.clear();
@@ -1179,6 +1568,18 @@ final class AppBatteryTracker extends BaseAppStateTracker
pw.print(KEY_BG_CURRENT_DRAIN_EVENT_DURATION_BASED_THRESHOLD_ENABLED);
pw.print('=');
pw.println(mBgCurrentDrainEventDurationBasedThresholdEnabled);
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_TYPES_TO_RESTRICTED_BUCKET);
+ pw.print('=');
+ pw.println(batteryUsageTypesToString(mBgCurrentDrainRestrictedBucketTypes));
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_TYPES_TO_BG_RESTRICTED);
+ pw.print('=');
+ pw.println(batteryUsageTypesToString(mBgCurrentDrainBgRestrictedTypes));
+ pw.print(prefix);
+ pw.print(KEY_BG_CURRENT_DRAIN_POWER_COMPONENTS);
+ pw.print('=');
+ pw.println(mBgCurrentDrainPowerComponents);
pw.print(prefix);
pw.println("Excessive current drain detected:");
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 8cff13e88fd28..a3aa129a31ea8 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -142,6 +142,7 @@ import com.android.internal.util.function.TriConsumer;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
+import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
@@ -1076,7 +1077,7 @@ public final class AppRestrictionController {
* @return The to-be-exempted battery usage of the given UID in the given duration; it could
* be considered as "exempted" due to various use cases, i.e. media playback.
*/
- double getUidBatteryExemptedUsageSince(int uid, long since, long now) {
+ ImmutableBatteryUsage getUidBatteryExemptedUsageSince(int uid, long since, long now) {
return mInjector.getAppBatteryExemptionTracker()
.getUidBatteryExemptedUsageSince(uid, since, now);
}
@@ -1084,7 +1085,7 @@ public final class AppRestrictionController {
/**
* @return The total battery usage of the given UID since the system boots.
*/
- double getUidBatteryUsage(int uid) {
+ @NonNull ImmutableBatteryUsage getUidBatteryUsage(int uid) {
return mInjector.getUidBatteryUsageProvider().getUidBatteryUsage(uid);
}
@@ -1092,7 +1093,7 @@ public final class AppRestrictionController {
/**
* @return The total battery usage of the given UID since the system boots.
*/
- double getUidBatteryUsage(int uid);
+ @NonNull ImmutableBatteryUsage getUidBatteryUsage(int uid);
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index a112bafbe4f94..fb8749e025950 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -46,9 +46,10 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
-import static com.android.server.am.AppBatteryTracker.BATT_DIMEN_BG;
-import static com.android.server.am.AppBatteryTracker.BATT_DIMEN_FG;
-import static com.android.server.am.AppBatteryTracker.BATT_DIMEN_FGS;
+import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_BACKGROUND;
+import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND;
+import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_INDEX_FOREGROUND_SERVICE;
+import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATT_DIMENS;
import static com.android.server.am.AppRestrictionController.STOCK_PM_FLAGS;
import static org.junit.Assert.assertEquals;
@@ -113,6 +114,7 @@ import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolic
import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates;
import com.android.server.am.AppBatteryExemptionTracker.UidStateEventWithBattery;
import com.android.server.am.AppBatteryTracker.AppBatteryPolicy;
+import com.android.server.am.AppBatteryTracker.ImmutableBatteryUsage;
import com.android.server.am.AppBindServiceEventsTracker.AppBindServiceEventsPolicy;
import com.android.server.am.AppBroadcastEventsTracker.AppBroadcastEventsPolicy;
import com.android.server.am.AppFGSTracker.AppFGSPolicy;
@@ -566,7 +568,7 @@ public final class BackgroundRestrictionTest {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+ AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
@@ -1294,7 +1296,7 @@ public final class BackgroundRestrictionTest {
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
AppBatteryPolicy.KEY_BG_CURRENT_DRAIN_THRESHOLD_TO_RESTRICTED_BUCKET,
DeviceConfig::getFloat,
- AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_BG_RESTRICTED_THRESHOLD);
+ AppBatteryPolicy.DEFAULT_BG_CURRENT_DRAIN_RESTRICTED_BUCKET_THRESHOLD);
bgCurrentDrainRestrictedBucketThreshold.set(restrictBucketThreshold);
bgCurrentDrainBgRestrictedThreshold = new DeviceConfigSession<>(
@@ -1932,9 +1934,12 @@ public final class BackgroundRestrictionTest {
private UidBatteryConsumer mockUidBatteryConsumer(int uid, double bg, double fgs, double fg) {
UidBatteryConsumer uidConsumer = mock(UidBatteryConsumer.class);
doReturn(uid).when(uidConsumer).getUid();
- doReturn(bg).when(uidConsumer).getConsumedPower(eq(BATT_DIMEN_BG));
- doReturn(fgs).when(uidConsumer).getConsumedPower(eq(BATT_DIMEN_FGS));
- doReturn(fg).when(uidConsumer).getConsumedPower(eq(BATT_DIMEN_FG));
+ doReturn(bg).when(uidConsumer).getConsumedPower(
+ eq(BATT_DIMENS[BATTERY_USAGE_INDEX_BACKGROUND]));
+ doReturn(fgs).when(uidConsumer).getConsumedPower(
+ eq(BATT_DIMENS[BATTERY_USAGE_INDEX_FOREGROUND_SERVICE]));
+ doReturn(fg).when(uidConsumer).getConsumedPower(
+ eq(BATT_DIMENS[BATTERY_USAGE_INDEX_FOREGROUND]));
return uidConsumer;
}
@@ -2234,8 +2239,8 @@ public final class BackgroundRestrictionTest {
boolean[] isStart, long[] timestamps, double[] batteryUsage) {
final LinkedList result = new LinkedList<>();
for (int i = 0; i < isStart.length; i++) {
- result.add(new UidStateEventWithBattery(
- isStart[i], timestamps[i], batteryUsage[i], null));
+ result.add(new UidStateEventWithBattery(isStart[i], timestamps[i],
+ new ImmutableBatteryUsage(0.0d, 0.0d, batteryUsage[i], 0.0d), null));
}
return result;
}