Convert WakelockPowerCalculator to work with BatteryUsageStats

Test: atest FrameworksCoreTests:com.android.internal.os.WakelockPowerCalculatorTest

Bug: 158137862
Change-Id: I485bdb1f7dc6622fdf30ddae6d6872f2455f36ad
This commit is contained in:
Dmitri Plotnikov
2021-02-04 20:00:03 -08:00
parent 4c4fc216da
commit 248ade5460
5 changed files with 178 additions and 36 deletions

View File

@@ -47,6 +47,7 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_SYSTEM_SERVICES,
POWER_COMPONENT_SENSORS,
POWER_COMPONENT_GNSS,
POWER_COMPONENT_WAKELOCK,
POWER_COMPONENT_SCREEN,
})
@Retention(RetentionPolicy.SOURCE)
@@ -64,6 +65,7 @@ public abstract class BatteryConsumer {
public static final int POWER_COMPONENT_MOBILE_RADIO = 8;
public static final int POWER_COMPONENT_SENSORS = 9;
public static final int POWER_COMPONENT_GNSS = 10;
public static final int POWER_COMPONENT_WAKELOCK = 12;
public static final int POWER_COMPONENT_SCREEN = 13;
public static final int POWER_COMPONENT_COUNT = 14;
@@ -87,6 +89,7 @@ public abstract class BatteryConsumer {
TIME_COMPONENT_MOBILE_RADIO,
TIME_COMPONENT_SENSORS,
TIME_COMPONENT_GNSS,
TIME_COMPONENT_WAKELOCK,
TIME_COMPONENT_SCREEN,
})
@Retention(RetentionPolicy.SOURCE)
@@ -104,6 +107,7 @@ public abstract class BatteryConsumer {
public static final int TIME_COMPONENT_MOBILE_RADIO = 8;
public static final int TIME_COMPONENT_SENSORS = 9;
public static final int TIME_COMPONENT_GNSS = 10;
public static final int TIME_COMPONENT_WAKELOCK = 12;
public static final int TIME_COMPONENT_SCREEN = 13;
public static final int TIME_COMPONENT_COUNT = 14;

View File

@@ -15,7 +15,12 @@
*/
package com.android.internal.os;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
@@ -26,39 +31,93 @@ import java.util.List;
public class WakelockPowerCalculator extends PowerCalculator {
private static final String TAG = "WakelockPowerCalculator";
private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
private final double mPowerWakelock;
private long mTotalAppWakelockTimeMs = 0;
private final UsageBasedPowerEstimator mPowerEstimator;
private static class PowerAndDuration {
public long durationMs;
public double powerMah;
}
public WakelockPowerCalculator(PowerProfile profile) {
mPowerWakelock = profile.getAveragePower(PowerProfile.POWER_CPU_IDLE);
mPowerEstimator = new UsageBasedPowerEstimator(
profile.getAveragePower(PowerProfile.POWER_CPU_IDLE));
}
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
final PowerAndDuration result = new PowerAndDuration();
UidBatteryConsumer.Builder osBatteryConsumer = null;
double osPowerMah = 0;
long osDurationMs = 0;
long totalAppDurationMs = 0;
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(result, app.getBatteryStatsUid(), rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK, result.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
totalAppDurationMs += result.durationMs;
if (app.getUid() == Process.ROOT_UID) {
osBatteryConsumer = app;
osDurationMs = result.durationMs;
osPowerMah = result.powerMah;
}
}
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
if (osBatteryConsumer != null) {
calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs,
BatteryStats.STATS_SINCE_CHARGED, osPowerMah, osDurationMs, totalAppDurationMs);
osBatteryConsumer.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK,
result.durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK, result.powerMah);
}
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
final PowerAndDuration result = new PowerAndDuration();
BatterySipper osSipper = null;
double osPowerMah = 0;
long osDurationMs = 0;
long totalAppDurationMs = 0;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
calculateApp(result, app.uidObj, rawRealtimeUs, statsType);
app.wakeLockTimeMs = result.durationMs;
app.wakeLockPowerMah = result.powerMah;
totalAppDurationMs += result.durationMs;
if (app.getUid() == Process.ROOT_UID) {
osSipper = app;
osPowerMah = result.powerMah;
osDurationMs = result.durationMs;
}
}
}
// The device has probably been awake for longer than the screen on
// time and application wake lock time would account for. Assign
// this remainder to the OS, if possible.
BatterySipper osSipper = null;
for (int i = sippers.size() - 1; i >= 0; i--) {
BatterySipper app = sippers.get(i);
if (app.getUid() == 0) {
osSipper = app;
break;
}
}
if (osSipper != null) {
calculateRemaining(osSipper, batteryStats, rawRealtimeUs, rawUptimeUs, statsType);
calculateRemaining(result, batteryStats, rawRealtimeUs, rawUptimeUs, statsType,
osPowerMah, osDurationMs, totalAppDurationMs);
osSipper.wakeLockTimeMs = result.durationMs;
osSipper.wakeLockPowerMah = result.powerMah;
osSipper.sumPower();
}
}
@Override
protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
private void calculateApp(PowerAndDuration result, BatteryStats.Uid u, long rawRealtimeUs,
int statsType) {
long wakeLockTimeUs = 0;
final ArrayMap<String, ? extends BatteryStats.Uid.Wakelock> wakelockStats =
u.getWakelockStats();
@@ -73,34 +132,29 @@ public class WakelockPowerCalculator extends PowerCalculator {
wakeLockTimeUs += timer.getTotalTimeLocked(rawRealtimeUs, statsType);
}
}
app.wakeLockTimeMs = wakeLockTimeUs / 1000; // convert to millis
mTotalAppWakelockTimeMs += app.wakeLockTimeMs;
result.durationMs = wakeLockTimeUs / 1000; // convert to millis
// Add cost of holding a wake lock.
app.wakeLockPowerMah = (app.wakeLockTimeMs * mPowerWakelock) / (1000 * 60 * 60);
if (DEBUG && app.wakeLockPowerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": wake " + app.wakeLockTimeMs
+ " power=" + formatCharge(app.wakeLockPowerMah));
result.powerMah = mPowerEstimator.calculatePower(result.durationMs);
if (DEBUG && result.powerMah != 0) {
Log.d(TAG, "UID " + u.getUid() + ": wake " + result.durationMs
+ " power=" + formatCharge(result.powerMah));
}
}
private void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000;
wakeTimeMillis -= mTotalAppWakelockTimeMs
+ (stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000);
private void calculateRemaining(PowerAndDuration result, BatteryStats stats, long rawRealtimeUs,
long rawUptimeUs, int statsType, double osPowerMah, long osDurationMs,
long totalAppDurationMs) {
final long wakeTimeMillis = stats.getBatteryUptime(rawUptimeUs) / 1000
- stats.getScreenOnTime(rawRealtimeUs, statsType) / 1000
- totalAppDurationMs;
if (wakeTimeMillis > 0) {
final double power = (wakeTimeMillis * mPowerWakelock) / (1000 * 60 * 60);
final double power = mPowerEstimator.calculatePower(wakeTimeMillis);
if (DEBUG) {
Log.d(TAG, "OS wakeLockTime " + wakeTimeMillis + " power " + formatCharge(power));
}
app.wakeLockTimeMs += wakeTimeMillis;
app.wakeLockPowerMah += power;
result.durationMs = osDurationMs + wakeTimeMillis;
result.powerMah = osPowerMah + power;
}
}
@Override
public void reset() {
mTotalAppWakelockTimeMs = 0;
}
}

View File

@@ -68,6 +68,7 @@ import org.junit.runners.Suite;
SystemServicePowerCalculatorTest.class,
UserPowerCalculatorTest.class,
VideoPowerCalculatorTest.class,
WakelockPowerCalculatorTest.class,
com.android.internal.power.MeasuredEnergyStatsTest.class
})

View File

@@ -49,6 +49,7 @@ public class BatteryUsageStatsRule implements TestRule {
};
private BatteryUsageStats mBatteryUsageStats;
private boolean mScreenOn;
public BatteryUsageStatsRule() {
Context context = InstrumentationRegistry.getContext();
@@ -97,6 +98,11 @@ public class BatteryUsageStatsRule implements TestRule {
return this;
}
public BatteryUsageStatsRule startWithScreenOn(boolean screenOn) {
mScreenOn = screenOn;
return this;
}
public void setNetworkStats(NetworkStats networkStats) {
mBatteryStats.setNetworkStats(networkStats);
}
@@ -115,6 +121,7 @@ public class BatteryUsageStatsRule implements TestRule {
private void noteOnBattery() {
mBatteryStats.setOnBatteryInternal(true);
mBatteryStats.getOnBatteryTimeBase().setRunning(true, 0, 0);
mBatteryStats.getOnBatteryScreenOffTimeBase().setRunning(!mScreenOn, 0, 0);
}
public PowerProfile getPowerProfile() {

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import static com.google.common.truth.Truth.assertThat;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.Process;
import android.os.UidBatteryConsumer;
import android.os.WorkSource;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class WakelockPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
private static final int APP_PID = 3145;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_CPU_IDLE, 360.0);
@Test
public void testTimerBasedModel() {
mStatsRule.getUidStats(Process.ROOT_UID);
BatteryStatsImpl batteryStats = mStatsRule.getBatteryStats();
batteryStats.noteStartWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "",
BatteryStats.WAKE_TYPE_PARTIAL, true, 1000, 1000);
batteryStats.noteStopWakeFromSourceLocked(new WorkSource(APP_UID), APP_PID, "awake", "",
BatteryStats.WAKE_TYPE_PARTIAL, 2000, 2000);
mStatsRule.setTime(10_000_000, 6_000_000);
WakelockPowerCalculator calculator =
new WakelockPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK))
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
.isWithin(PRECISION).of(0.1);
UidBatteryConsumer osConsumer = mStatsRule.getUidBatteryConsumer(Process.ROOT_UID);
assertThat(osConsumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_WAKELOCK))
.isEqualTo(5000);
assertThat(osConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_WAKELOCK))
.isWithin(PRECISION).of(0.5);
}
}