Merge "Convert SensorPowerCalculator to work with BatteryUsageStats"

This commit is contained in:
Dmitri Plotnikov
2021-01-22 19:10:33 +00:00
committed by Android (Google) Code Review
8 changed files with 339 additions and 57 deletions

View File

@@ -45,6 +45,8 @@ public abstract class BatteryConsumer {
POWER_COMPONENT_FLASHLIGHT,
POWER_COMPONENT_MOBILE_RADIO,
POWER_COMPONENT_SYSTEM_SERVICES,
POWER_COMPONENT_SENSORS,
POWER_COMPONENT_GNSS,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface PowerComponent {
@@ -59,8 +61,10 @@ public abstract class BatteryConsumer {
public static final int POWER_COMPONENT_FLASHLIGHT = 6;
public static final int POWER_COMPONENT_SYSTEM_SERVICES = 7;
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_COUNT = 9;
public static final int POWER_COMPONENT_COUNT = 11;
public static final int FIRST_CUSTOM_POWER_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_POWER_COMPONENT_ID = 9999;
@@ -90,6 +94,8 @@ public abstract class BatteryConsumer {
TIME_COMPONENT_CAMERA,
TIME_COMPONENT_FLASHLIGHT,
TIME_COMPONENT_MOBILE_RADIO,
TIME_COMPONENT_SENSORS,
TIME_COMPONENT_GNSS,
})
@Retention(RetentionPolicy.SOURCE)
public static @interface TimeComponent {
@@ -104,8 +110,10 @@ public abstract class BatteryConsumer {
public static final int TIME_COMPONENT_VIDEO = 6;
public static final int TIME_COMPONENT_FLASHLIGHT = 7;
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_COUNT = 9;
public static final int TIME_COMPONENT_COUNT = 11;
public static final int FIRST_CUSTOM_TIME_COMPONENT_ID = 1000;
public static final int LAST_CUSTOM_TIME_COMPONENT_ID = 9999;

View File

@@ -339,8 +339,9 @@ public class BatteryStatsHelper {
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
mPowerCalculators.add(new SensorPowerCalculator(mPowerProfile,
mPowerCalculators.add(new SensorPowerCalculator(
mContext.getSystemService(SensorManager.class)));
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
mPowerCalculators.add(new MediaPowerCalculator(mPowerProfile));

View File

@@ -62,8 +62,9 @@ public class BatteryUsageStatsProvider {
}
mPowerCalculators.add(new WifiPowerCalculator(mPowerProfile));
mPowerCalculators.add(new BluetoothPowerCalculator(mPowerProfile));
mPowerCalculators.add(new SensorPowerCalculator(mPowerProfile,
mPowerCalculators.add(new SensorPowerCalculator(
mContext.getSystemService(SensorManager.class)));
mPowerCalculators.add(new GnssPowerCalculator(mPowerProfile));
mPowerCalculators.add(new CameraPowerCalculator(mPowerProfile));
mPowerCalculators.add(new FlashlightPowerCalculator(mPowerProfile));
mPowerCalculators.add(new AudioPowerCalculator(mPowerProfile));

View File

@@ -0,0 +1,124 @@
/*
* Copyright (C) 2015 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 android.location.GnssSignalQuality;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.util.SparseArray;
import java.util.List;
/**
* Estimates the amount of power consumed by the GNSS (e.g. GPS).
*/
public class GnssPowerCalculator extends PowerCalculator {
private final double mAveragePowerGnssOn;
private final double[] mAveragePowerPerSignalQuality;
public GnssPowerCalculator(PowerProfile profile) {
mAveragePowerGnssOn = profile.getAveragePowerOrDefault(PowerProfile.POWER_GPS_ON, -1);
mAveragePowerPerSignalQuality =
new double[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
mAveragePowerPerSignalQuality[i] = profile.getAveragePower(
PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i);
}
}
@Override
public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
SparseArray<UserHandle> asUsers) {
final double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs,
BatteryStats.STATS_SINCE_CHARGED);
final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
builder.getUidBatteryConsumerBuilders();
for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i);
calculateApp(app, app.getBatteryStatsUid(), rawRealtimeUs, rawUptimeUs, query,
averageGnssPowerMa);
}
}
private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query,
double averageGnssPowerMa) {
final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
double powerMah = computePower(durationMs, averageGnssPowerMa);
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS, powerMah);
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
double averageGnssPowerMa = getAverageGnssPower(batteryStats, rawRealtimeUs, statsType);
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper app = sippers.get(i);
if (app.drainType == BatterySipper.DrainType.APP) {
calculateApp(app, app.uidObj, rawRealtimeUs, statsType, averageGnssPowerMa);
}
}
}
protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
int statsType, double averageGnssPowerMa) {
final long durationMs = computeDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
double powerMah = computePower(durationMs, averageGnssPowerMa);
app.gpsTimeMs = durationMs;
app.gpsPowerMah = powerMah;
}
private long computeDuration(BatteryStats.Uid u, long rawRealtimeUs, int statsType) {
final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
final BatteryStats.Uid.Sensor sensor = sensorStats.get(BatteryStats.Uid.Sensor.GPS);
if (sensor == null) {
return 0;
}
final BatteryStats.Timer timer = sensor.getSensorTime();
return timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
}
private double computePower(long sensorTime, double averageGnssPowerMa) {
return (sensorTime * averageGnssPowerMa) / (1000 * 60 * 60);
}
private double getAverageGnssPower(BatteryStats stats, long rawRealtimeUs, int statsType) {
double averagePower = mAveragePowerGnssOn;
if (averagePower != -1) {
return averagePower;
}
averagePower = 0;
long totalTime = 0;
double totalPower = 0;
for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType);
totalTime += timePerLevel;
totalPower += mAveragePowerPerSignalQuality[i] * timePerLevel;
}
if (totalTime != 0) {
averagePower = totalPower / totalTime;
}
return averagePower;
}
}

View File

@@ -17,81 +17,79 @@ package com.android.internal.os;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.location.GnssSignalQuality;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.UserHandle;
import android.os.BatteryUsageStatsQuery;
import android.os.UidBatteryConsumer;
import android.util.SparseArray;
import java.util.List;
public class SensorPowerCalculator extends PowerCalculator {
private final PowerProfile mPowerProfile;
private final List<Sensor> mSensors;
private double mGpsPower;
private final SparseArray<Sensor> mSensors;
public SensorPowerCalculator(PowerProfile profile, SensorManager sensorManager) {
mPowerProfile = profile;
mSensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
public SensorPowerCalculator(SensorManager sensorManager) {
List<Sensor> sensors = sensorManager.getSensorList(Sensor.TYPE_ALL);
mSensors = new SparseArray<>(sensors.size());
for (int i = 0; i < sensors.size(); i++) {
Sensor sensor = sensors.get(i);
mSensors.put(sensor.getHandle(), sensor);
}
}
@Override
public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
long rawRealtimeUs, long rawUptimeUs, int statsType, SparseArray<UserHandle> asUsers) {
mGpsPower = getAverageGpsPower(batteryStats, rawRealtimeUs, statsType);
super.calculate(sippers, batteryStats, rawRealtimeUs, rawUptimeUs, statsType, asUsers);
protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
app.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SENSORS,
calculateDuration(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED))
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS,
calculatePowerMah(u, rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED));
}
@Override
protected void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
long rawUptimeUs, int statsType) {
// Process Sensor usage
app.sensorPowerMah = calculatePowerMah(u, rawRealtimeUs, statsType);
}
private long calculateDuration(BatteryStats.Uid u, long rawRealtimeUs, int statsType) {
long durationMs = 0;
final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
final int NSE = sensorStats.size();
for (int ise = 0; ise < NSE; ise++) {
final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
final int sensorHandle = sensorStats.keyAt(ise);
if (sensorHandle == BatteryStats.Uid.Sensor.GPS) {
continue;
}
final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
final BatteryStats.Timer timer = sensor.getSensorTime();
durationMs += timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
}
return durationMs;
}
private double calculatePowerMah(BatteryStats.Uid u, long rawRealtimeUs, int statsType) {
double powerMah = 0;
final SparseArray<? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats();
final int count = sensorStats.size();
for (int ise = 0; ise < count; ise++) {
final int sensorHandle = sensorStats.keyAt(ise);
// TODO(b/178127364): remove BatteryStats.Uid.Sensor.GPS and references to it.
if (sensorHandle == BatteryStats.Uid.Sensor.GPS) {
continue;
}
final BatteryStats.Uid.Sensor sensor = sensorStats.valueAt(ise);
final BatteryStats.Timer timer = sensor.getSensorTime();
final long sensorTime = timer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
switch (sensorHandle) {
case BatteryStats.Uid.Sensor.GPS:
app.gpsTimeMs = sensorTime;
app.gpsPowerMah = (app.gpsTimeMs * mGpsPower) / (1000 * 60 * 60);
break;
default:
final int sensorsCount = mSensors.size();
for (int i = 0; i < sensorsCount; i++) {
final Sensor s = mSensors.get(i);
if (s.getHandle() == sensorHandle) {
app.sensorPowerMah += (sensorTime * s.getPower()) / (1000 * 60 * 60);
break;
}
}
break;
if (sensorTime != 0) {
Sensor s = mSensors.get(sensorHandle);
if (s != null) {
powerMah += (sensorTime * s.getPower()) / (1000 * 60 * 60);
}
}
}
}
private double getAverageGpsPower(BatteryStats stats, long rawRealtimeUs,
int statsType) {
double averagePower =
mPowerProfile.getAveragePowerOrDefault(PowerProfile.POWER_GPS_ON, -1);
if (averagePower != -1) {
return averagePower;
}
averagePower = 0;
long totalTime = 0;
double totalPower = 0;
for (int i = 0; i < GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS; i++) {
long timePerLevel = stats.getGpsSignalQualityTime(i, rawRealtimeUs, statsType);
totalTime += timePerLevel;
totalPower +=
mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED, i)
* timePerLevel;
}
if (totalTime != 0) {
averagePower = totalPower / totalTime;
}
return averagePower;
return powerMah;
}
}

View File

@@ -46,6 +46,7 @@ import org.junit.runners.Suite;
BstatsCpuTimesValidationTest.class,
CameraPowerCalculatorTest.class,
FlashlightPowerCalculatorTest.class,
GnssPowerCalculatorTest.class,
IdlePowerCalculatorTest.class,
KernelCpuProcStringReaderTest.class,
KernelCpuUidActiveTimeReaderTest.class,
@@ -62,6 +63,7 @@ import org.junit.runners.Suite;
PowerCalculatorTest.class,
PowerProfileTest.class,
ScreenPowerCalculatorTest.class,
SensorPowerCalculatorTest.class,
SystemServicePowerCalculatorTest.class,
VideoPowerCalculatorTest.class,

View File

@@ -0,0 +1,62 @@
/*
* 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.Process;
import android.os.UidBatteryConsumer;
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 GnssPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
.setAveragePower(PowerProfile.POWER_GPS_ON, 360.0)
.setAveragePower(PowerProfile.POWER_GPS_SIGNAL_QUALITY_BASED,
new double[] {720.0, 1440.0, 1800.0});
@Test
public void testTimerBasedModel() {
BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
uidStats.noteStartGps(1000);
uidStats.noteStopGps(2000);
GnssPowerCalculator calculator =
new GnssPowerCalculator(mStatsRule.getPowerProfile());
mStatsRule.apply(calculator);
UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_GNSS))
.isEqualTo(1000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_GNSS))
.isWithin(PRECISION).of(0.1);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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 static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.input.InputSensorInfo;
import android.os.BatteryConsumer;
import android.os.Process;
import android.os.UidBatteryConsumer;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SensorPowerCalculatorTest {
private static final double PRECISION = 0.00001;
private static final int SENSOR_HANDLE_1 = 1;
private static final int SENSOR_HANDLE_2 = 2;
private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
@Rule
public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule();
@Test
public void testTimerBasedModel() {
Sensor sensor1 = createSensor(SENSOR_HANDLE_1, Sensor.TYPE_AMBIENT_TEMPERATURE, 360);
Sensor sensor2 = createSensor(SENSOR_HANDLE_2, Sensor.TYPE_STEP_COUNTER, 720);
SensorManager sensorManager = mock(SensorManager.class);
when(sensorManager.getSensorList(Sensor.TYPE_ALL))
.thenReturn(List.of(sensor1, sensor2));
BatteryStatsImpl.Uid uidStats = mStatsRule.getUidStats(APP_UID);
uidStats.noteStartSensor(SENSOR_HANDLE_1, 1000);
uidStats.noteStopSensor(SENSOR_HANDLE_1, 2000);
uidStats.noteStartSensor(SENSOR_HANDLE_2, 3000);
uidStats.noteStopSensor(SENSOR_HANDLE_2, 5000);
SensorPowerCalculator calculator = new SensorPowerCalculator(sensorManager);
mStatsRule.apply(calculator);
UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID);
assertThat(consumer.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_SENSORS))
.isEqualTo(3000);
assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_SENSORS))
.isWithin(PRECISION).of(0.5);
}
private Sensor createSensor(int handle, int type, double power) {
return new Sensor(new InputSensorInfo("name", "vendor", 0 /* version */,
handle, type, 100.0f /*maxRange */, 0.02f /* resolution */,
(float) power, 1000 /* minDelay */, 0 /* fifoReservedEventCount */,
0 /* fifoMaxEventCount */, "" /* stringType */, "" /* requiredPermission */,
0 /* maxDelay */, 0 /* flags */, 0 /* id */));
}
}