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;
}