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