diff options
4 files changed, 110 insertions, 31 deletions
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index b50a08dfa57a..547d406751f9 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1142,6 +1142,16 @@ public abstract class BatteryStats { @BatteryConsumer.ProcessState int processState); + + /** + * Returns the battery consumption (in microcoulombs) of UID's camera usage, derived from + * on-device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getCameraEnergyConsumptionUC(); + /** * Returns the battery consumption (in microcoulombs) used by this uid for each * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 93c1d7fb659a..d622fd717611 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -8442,6 +8442,12 @@ public class BatteryStatsImpl extends BatteryStats { processState); } + @GuardedBy("mBsi") + @Override + public long getCameraEnergyConsumptionUC() { + return getEnergyConsumptionUC(EnergyConsumerStats.POWER_BUCKET_CAMERA); + } + /** * Gets the minimum of the uid's foreground activity time and its PROCESS_STATE_TOP time * since last marked. Also sets the mark time for both these timers. @@ -8492,6 +8498,20 @@ public class BatteryStatsImpl extends BatteryStats { return gnssTimeUs; } + /** + * Gets the uid's time spent using the camera since last marked. Also sets the mark time for + * the camera timer. + */ + private long markCameraTimeUs(long elapsedRealtimeMs) { + final StopwatchTimer timer = mCameraTurnedOnTimer; + if (timer == null) { + return 0; + } + final long cameraTimeUs = timer.getTimeSinceMarkLocked(elapsedRealtimeMs * 1000); + timer.setMark(elapsedRealtimeMs); + return cameraTimeUs; + } + public StopwatchTimer createAudioTurnedOnTimerLocked() { if (mAudioTurnedOnTimer == null) { mAudioTurnedOnTimer = new StopwatchTimer(mBsi.mClock, Uid.this, AUDIO_TURNED_ON, @@ -12934,12 +12954,33 @@ public class BatteryStatsImpl extends BatteryStats { return; } - // TODO(b/258319905): Handle mIgnoreNextExternalStats + if (mIgnoreNextExternalStats) { + // Although under ordinary resets we won't get here, and typically a new sync will + // happen right after the reset, strictly speaking we need to set all mark times to now. + final int uidStatsSize = mUidStats.size(); + for (int i = 0; i < uidStatsSize; i++) { + final Uid uid = mUidStats.valueAt(i); + uid.markCameraTimeUs(elapsedRealtimeMs); + } + return; + } mGlobalEnergyConsumerStats.updateStandardBucket( EnergyConsumerStats.POWER_BUCKET_CAMERA, chargeUC); - // TODO(b/258319905): Update per-UID stats. + // Collect the per uid time since mark so that we can normalize power. + final SparseDoubleArray cameraTimeUsArray = new SparseDoubleArray(); + + // Note: Iterating over all UIDs may be suboptimal. + final int uidStatsSize = mUidStats.size(); + for (int i = 0; i < uidStatsSize; i++) { + final Uid uid = mUidStats.valueAt(i); + final long cameraTimeUs = uid.markCameraTimeUs(elapsedRealtimeMs); + if (cameraTimeUs == 0) continue; + cameraTimeUsArray.put(uid.getUid(), (double) cameraTimeUs); + } + distributeEnergyToUidsLocked(EnergyConsumerStats.POWER_BUCKET_CAMERA, chargeUC, + cameraTimeUsArray, 0, elapsedRealtimeMs); } /** diff --git a/services/core/java/com/android/server/power/stats/CameraPowerCalculator.java b/services/core/java/com/android/server/power/stats/CameraPowerCalculator.java index b17aea443050..89991bf8e4e1 100644 --- a/services/core/java/com/android/server/power/stats/CameraPowerCalculator.java +++ b/services/core/java/com/android/server/power/stats/CameraPowerCalculator.java @@ -73,11 +73,19 @@ public class CameraPowerCalculator extends PowerCalculator { @Override protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) { - final long durationMs = + long consumptionUc = app.getBatteryStatsUid().getCameraEnergyConsumptionUC(); + int powerModel = getPowerModel(consumptionUc, query); + long durationMs = mPowerEstimator.calculateDuration(u.getCameraTurnedOnTimer(), rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED); - final double powerMah = mPowerEstimator.calculatePower(durationMs); + double powerMah; + if (powerModel == BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION) { + powerMah = uCtoMah(consumptionUc); + } else { + powerMah = mPowerEstimator.calculatePower(durationMs); + } + app.setUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA, durationMs) - .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah); + .setConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA, powerMah, powerModel); } } diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java index 2a8bf421a8cc..5fce32f0598a 100644 --- a/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java +++ b/services/tests/servicestests/src/com/android/server/power/stats/CameraPowerCalculatorTest.java @@ -36,7 +36,8 @@ import org.junit.runner.RunWith; public class CameraPowerCalculatorTest { private static final double PRECISION = 0.00001; - private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; + private static final int APP1_UID = Process.FIRST_APPLICATION_UID + 42; + private static final int APP2_UID = Process.FIRST_APPLICATION_UID + 43; @Rule public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule() @@ -47,8 +48,10 @@ public class CameraPowerCalculatorTest { public void testTimerBasedModel() { BatteryStatsImpl stats = mStatsRule.getBatteryStats(); synchronized (stats) { // To keep the GuardedBy check happy - stats.noteCameraOnLocked(APP_UID, 1000, 1000); - stats.noteCameraOffLocked(APP_UID, 2000, 2000); + stats.noteCameraOnLocked(APP1_UID, 1000, 1000); + stats.noteCameraOffLocked(APP1_UID, 2000, 2000); + stats.noteCameraOnLocked(APP2_UID, 3000, 3000); + stats.noteCameraOffLocked(APP2_UID, 5000, 5000); } CameraPowerCalculator calculator = @@ -56,29 +59,37 @@ public class CameraPowerCalculatorTest { mStatsRule.apply(BatteryUsageStatsRule.POWER_PROFILE_MODEL_ONLY, calculator); - UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) + UidBatteryConsumer app1Consumer = mStatsRule.getUidBatteryConsumer(APP1_UID); + assertThat(app1Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(1000); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) + assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isWithin(PRECISION).of(0.1); - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) + assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); + + UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); + assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isEqualTo(2000); + assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isWithin(PRECISION).of(0.2); + assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(1000); + .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isWithin(PRECISION).of(0.1); + .isWithin(PRECISION).of(0.3); assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(1000); + .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isWithin(PRECISION).of(0.1); + .isWithin(PRECISION).of(0.3); assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); } @@ -87,9 +98,12 @@ public class CameraPowerCalculatorTest { public void testEnergyConsumptionBasedModel() { BatteryStatsImpl stats = mStatsRule.getBatteryStats(); synchronized (stats) { // To keep the GuardedBy check happy - stats.noteCameraOnLocked(APP_UID, 1000, 1000); - stats.noteCameraOffLocked(APP_UID, 2000, 2000); - stats.updateCameraEnergyConsumerStatsLocked(720_000, 6000); // 0.72C == 0.2mAh + stats.noteCameraOnLocked(APP1_UID, 1000, 1000); + stats.noteCameraOffLocked(APP1_UID, 2000, 2000); + stats.updateCameraEnergyConsumerStatsLocked(720_000, 2100); // 0.72C == 0.2mAh + stats.noteCameraOnLocked(APP2_UID, 3000, 3000); + stats.noteCameraOffLocked(APP2_UID, 5000, 5000); + stats.updateCameraEnergyConsumerStatsLocked(1_080_000, 5100); // 0.3mAh } CameraPowerCalculator calculator = @@ -97,31 +111,37 @@ public class CameraPowerCalculatorTest { mStatsRule.apply(calculator); - UidBatteryConsumer consumer = mStatsRule.getUidBatteryConsumer(APP_UID); - assertThat(consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) + UidBatteryConsumer app1Consumer = mStatsRule.getUidBatteryConsumer(APP1_UID); + assertThat(app1Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(1000); - assertThat(consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isWithin(PRECISION).of(0.1); - // Per-UID energy consumer model is not implemented yet, so the result will be based on the - // power profile - assertThat(consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(BatteryConsumer.POWER_MODEL_POWER_PROFILE); + assertThat(app1Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isWithin(PRECISION).of(0.2); + assertThat(app1Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); + + UidBatteryConsumer app2Consumer = mStatsRule.getUidBatteryConsumer(APP2_UID); + assertThat(app2Consumer.getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isEqualTo(2000); + assertThat(app2Consumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isWithin(PRECISION).of(0.3); + assertThat(app2Consumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) + .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer deviceBatteryConsumer = mStatsRule.getDeviceBatteryConsumer(); assertThat(deviceBatteryConsumer .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(1000); + .isEqualTo(3000); assertThat(deviceBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isWithin(PRECISION).of(0.2); + .isWithin(PRECISION).of(0.5); assertThat(deviceBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); final BatteryConsumer appsBatteryConsumer = mStatsRule.getAppsBatteryConsumer(); assertThat(appsBatteryConsumer .getUsageDurationMillis(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isEqualTo(1000); + .isEqualTo(3000); assertThat(appsBatteryConsumer.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CAMERA)) - .isWithin(PRECISION).of(0.2); + .isWithin(PRECISION).of(0.5); assertThat(appsBatteryConsumer.getPowerModel(BatteryConsumer.POWER_COMPONENT_CAMERA)) .isEqualTo(BatteryConsumer.POWER_MODEL_ENERGY_CONSUMPTION); } |