Convert WakelockPowerCalculator to work with BatteryUsageStats
Test: atest FrameworksCoreTests:com.android.internal.os.WakelockPowerCalculatorTest Bug: 158137862 Change-Id: I485bdb1f7dc6622fdf30ddae6d6872f2455f36ad
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ import org.junit.runners.Suite;
|
||||
SystemServicePowerCalculatorTest.class,
|
||||
UserPowerCalculatorTest.class,
|
||||
VideoPowerCalculatorTest.class,
|
||||
WakelockPowerCalculatorTest.class,
|
||||
|
||||
com.android.internal.power.MeasuredEnergyStatsTest.class
|
||||
})
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user