Power profile audio & video support
Add power consumed by audio and video hardware to power profile.
Attribute these power to specific app.
Bug: 70531798
Test: PowerProfileTest
Test: BatteryStatsHelperTest
Test: PowerCalculatorTest
Change-Id: I4811efa14ce97983258cd59bfd328181fe95baf4
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 5b6291e..5abc6d4 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -73,14 +73,16 @@
public double usagePowerMah;
// Subsystem usage times.
- public long cpuTimeMs;
- public long gpsTimeMs;
- public long wifiRunningTimeMs;
- public long cpuFgTimeMs;
- public long wakeLockTimeMs;
- public long cameraTimeMs;
- public long flashlightTimeMs;
+ public long audioTimeMs;
public long bluetoothRunningTimeMs;
+ public long cameraTimeMs;
+ public long cpuFgTimeMs;
+ public long cpuTimeMs;
+ public long flashlightTimeMs;
+ public long gpsTimeMs;
+ public long videoTimeMs;
+ public long wakeLockTimeMs;
+ public long wifiRunningTimeMs;
public long mobileRxPackets;
public long mobileTxPackets;
@@ -102,15 +104,17 @@
// Measured in mAh (milli-ampere per hour).
// These are included when summed.
- public double wifiPowerMah;
- public double cpuPowerMah;
- public double wakeLockPowerMah;
- public double mobileRadioPowerMah;
- public double gpsPowerMah;
- public double sensorPowerMah;
- public double cameraPowerMah;
- public double flashlightPowerMah;
+ public double audioPowerMah;
public double bluetoothPowerMah;
+ public double cameraPowerMah;
+ public double cpuPowerMah;
+ public double flashlightPowerMah;
+ public double gpsPowerMah;
+ public double mobileRadioPowerMah;
+ public double sensorPowerMah;
+ public double videoPowerMah;
+ public double wakeLockPowerMah;
+ public double wifiPowerMah;
public enum DrainType {
AMBIENT_DISPLAY,
@@ -177,10 +181,12 @@
totalPowerMah += other.totalPowerMah;
usageTimeMs += other.usageTimeMs;
usagePowerMah += other.usagePowerMah;
+ audioTimeMs += other.audioTimeMs;
cpuTimeMs += other.cpuTimeMs;
gpsTimeMs += other.gpsTimeMs;
wifiRunningTimeMs += other.wifiRunningTimeMs;
cpuFgTimeMs += other.cpuFgTimeMs;
+ videoTimeMs += other.videoTimeMs;
wakeLockTimeMs += other.wakeLockTimeMs;
cameraTimeMs += other.cameraTimeMs;
flashlightTimeMs += other.flashlightTimeMs;
@@ -197,6 +203,7 @@
wifiTxBytes += other.wifiTxBytes;
btRxBytes += other.btRxBytes;
btTxBytes += other.btTxBytes;
+ audioPowerMah += other.audioPowerMah;
wifiPowerMah += other.wifiPowerMah;
gpsPowerMah += other.gpsPowerMah;
cpuPowerMah += other.cpuPowerMah;
@@ -207,6 +214,7 @@
flashlightPowerMah += other.flashlightPowerMah;
bluetoothPowerMah += other.bluetoothPowerMah;
screenPowerMah += other.screenPowerMah;
+ videoPowerMah += other.videoPowerMah;
proportionalSmearMah += other.proportionalSmearMah;
totalSmearedPowerMah += other.totalSmearedPowerMah;
}
@@ -220,7 +228,7 @@
public double sumPower() {
totalPowerMah = usagePowerMah + wifiPowerMah + gpsPowerMah + cpuPowerMah +
sensorPowerMah + mobileRadioPowerMah + wakeLockPowerMah + cameraPowerMah +
- flashlightPowerMah + bluetoothPowerMah;
+ flashlightPowerMah + bluetoothPowerMah + audioPowerMah + videoPowerMah;
totalSmearedPowerMah = totalPowerMah + screenPowerMah + proportionalSmearMah;
return totalPowerMah;
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index a76cf0a..1e5bd18 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -136,6 +136,7 @@
PowerCalculator mCameraPowerCalculator;
PowerCalculator mFlashlightPowerCalculator;
PowerCalculator mMemoryPowerCalculator;
+ PowerCalculator mMediaPowerCalculator;
boolean mHasWifiPowerReporting = false;
boolean mHasBluetoothPowerReporting = false;
@@ -424,6 +425,11 @@
}
mFlashlightPowerCalculator.reset();
+ if (mMediaPowerCalculator == null) {
+ mMediaPowerCalculator = new MediaPowerCalculator(mPowerProfile);
+ }
+ mMediaPowerCalculator.reset();
+
mStatsType = statsType;
mRawUptimeUs = rawUptimeUs;
mRawRealtimeUs = rawRealtimeUs;
@@ -560,6 +566,7 @@
mCameraPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
mFlashlightPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs,
mStatsType);
+ mMediaPowerCalculator.calculateApp(app, u, mRawRealtimeUs, mRawUptimeUs, mStatsType);
final double totalPower = app.sumPower();
if (DEBUG && totalPower != 0) {
diff --git a/core/java/com/android/internal/os/MediaPowerCalculator.java b/core/java/com/android/internal/os/MediaPowerCalculator.java
new file mode 100644
index 0000000..a35c134
--- /dev/null
+++ b/core/java/com/android/internal/os/MediaPowerCalculator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 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.os.BatteryStats;
+
+/**
+ * A {@link PowerCalculator} to calculate power consumed by audio and video hardware.
+ *
+ * Also see {@link PowerProfile#POWER_AUDIO} and {@link PowerProfile#POWER_VIDEO}.
+ */
+public class MediaPowerCalculator extends PowerCalculator {
+ private static final int MS_IN_HR = 1000 * 60 * 60;
+ private final double mAudioAveragePowerMa;
+ private final double mVideoAveragePowerMa;
+
+ public MediaPowerCalculator(PowerProfile profile) {
+ mAudioAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_AUDIO);
+ mVideoAveragePowerMa = profile.getAveragePower(PowerProfile.POWER_VIDEO);
+ }
+
+ @Override
+ public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+ long rawUptimeUs, int statsType) {
+ // Calculate audio power usage, an estimate based on the average power routed to different
+ // components like speaker, bluetooth, usb-c, earphone, etc.
+ final BatteryStats.Timer audioTimer = u.getAudioTurnedOnTimer();
+ if (audioTimer == null) {
+ app.audioTimeMs = 0;
+ app.audioPowerMah = 0;
+ } else {
+ final long totalTime = audioTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+ app.audioTimeMs = totalTime;
+ app.audioPowerMah = (totalTime * mAudioAveragePowerMa) / MS_IN_HR;
+ }
+
+ // Calculate video power usage.
+ final BatteryStats.Timer videoTimer = u.getVideoTurnedOnTimer();
+ if (videoTimer == null) {
+ app.videoTimeMs = 0;
+ app.videoPowerMah = 0;
+ } else {
+ final long totalTime = videoTimer.getTotalTimeLocked(rawRealtimeUs, statsType) / 1000;
+ app.videoTimeMs = totalTime;
+ app.videoPowerMah = (totalTime * mVideoAveragePowerMa) / MS_IN_HR;
+ }
+ }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 747d633..344c772 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -43,12 +43,12 @@
*/
public static final String POWER_NONE = "none";
- /**
+ /*
* POWER_CPU_SUSPEND: Power consumption when CPU is in power collapse mode.
* POWER_CPU_IDLE: Power consumption when CPU is awake (when a wake lock is held). This should
* be zero on devices that can go into full CPU power collapse even when a wake
* lock is held. Otherwise, this is the power consumption in addition to
- * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
+ * POWER_CPU_SUSPEND due to a wake lock being held but with no CPU activity.
* POWER_CPU_ACTIVE: Power consumption when CPU is running, excluding power consumed by clusters
* and cores.
*
@@ -84,7 +84,6 @@
// Updated power constants. These are not estimated, they are real world
// currents and voltages for the underlying bluetooth and wifi controllers.
//
-
public static final String POWER_WIFI_CONTROLLER_IDLE = "wifi.controller.idle";
public static final String POWER_WIFI_CONTROLLER_RX = "wifi.controller.rx";
public static final String POWER_WIFI_CONTROLLER_TX = "wifi.controller.tx";
@@ -117,6 +116,7 @@
/**
* Power consumption when Bluetooth driver is on.
+ *
* @deprecated
*/
@Deprecated
@@ -124,6 +124,7 @@
/**
* Power consumption when Bluetooth driver is transmitting/receiving.
+ *
* @deprecated
*/
@Deprecated
@@ -131,6 +132,7 @@
/**
* Power consumption when Bluetooth driver gets an AT command.
+ *
* @deprecated
*/
@Deprecated
@@ -171,13 +173,13 @@
* Power consumed by the audio hardware when playing back audio content. This is in addition
* to the CPU power, probably due to a DSP and / or amplifier.
*/
- public static final String POWER_AUDIO = "dsp.audio";
+ public static final String POWER_AUDIO = "audio";
/**
* Power consumed by any media hardware when playing back video content. This is in addition
* to the CPU power, probably due to a DSP.
*/
- public static final String POWER_VIDEO = "dsp.video";
+ public static final String POWER_VIDEO = "video";
/**
* Average power consumption when camera flashlight is on.
@@ -409,6 +411,7 @@
/**
* Returns the number of memory bandwidth buckets defined in power_profile.xml, or a
* default value if the subsystem has no recorded value.
+ *
* @return the number of memory bandwidth buckets.
*/
public int getNumElements(String key) {
@@ -423,7 +426,8 @@
/**
* Returns the average current in mA consumed by the subsystem, or the given
* default value if the subsystem has no recorded value.
- * @param type the subsystem type
+ *
+ * @param type the subsystem type
* @param defaultValue the value to return if the subsystem has no recorded value.
* @return the average current in milliAmps.
*/
@@ -439,19 +443,21 @@
/**
* Returns the average current in mA consumed by the subsystem
+ *
* @param type the subsystem type
* @return the average current in milliAmps.
*/
public double getAveragePower(String type) {
return getAveragePowerOrDefault(type, 0);
}
-
+
/**
* Returns the average current in mA consumed by the subsystem for the given level.
- * @param type the subsystem type
+ *
+ * @param type the subsystem type
* @param level the level of power at which the subsystem is running. For instance, the
- * signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
- * If there is no data for multiple levels, the level is ignored.
+ * signal strength of the cell network between 0 and 4 (if there are 4 bars max.)
+ * If there is no data for multiple levels, the level is ignored.
* @return the average current in milliAmps.
*/
public double getAveragePower(String type, int level) {
@@ -474,6 +480,7 @@
/**
* Returns the battery capacity, if available, in milli Amp Hours. If not available,
* it returns zero.
+ *
* @return the battery capacity in mAh
*/
public double getBatteryCapacity() {
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index bd0958d..eff7c55 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -36,8 +36,8 @@
<item name="wifi.on">0.1</item> <!-- ~3mA -->
<item name="wifi.active">0.1</item> <!-- WIFI data transfer, ~200mA -->
<item name="wifi.scan">0.1</item> <!-- WIFI network scanning, ~100mA -->
- <item name="dsp.audio">0.1</item> <!-- ~10mA -->
- <item name="dsp.video">0.1</item> <!-- ~50mA -->
+ <item name="audio">0.1</item> <!-- ~10mA -->
+ <item name="video">0.1</item> <!-- ~50mA -->
<item name="camera.flashlight">0.1</item> <!-- Avg. power for camera flash, ~160mA -->
<item name="camera.avg">0.1</item> <!-- Avg. power use of camera in standard usecases, ~550mA -->
<item name="gps.on">0.1</item> <!-- ~50mA -->
diff --git a/core/res/res/xml/power_profile_test.xml b/core/res/res/xml/power_profile_test.xml
index eb34732..6dc5c7e 100644
--- a/core/res/res/xml/power_profile_test.xml
+++ b/core/res/res/xml/power_profile_test.xml
@@ -96,8 +96,11 @@
minute. -->
<item name="camera.avg">600</item>
- <!-- Additional power used when audio decoding/encoding via DSP -->
- <item name="dsp.audio">100</item>
+ <!-- Additional power used by the audio hardware, probably due to DSP -->
+ <item name="audio">100.0</item>
+
+ <!-- Additional power used by the video hardware, probably due to DSP -->
+ <item name="video">150.0</item> <!-- ~50mA -->
<!-- Additional power used when GPS is acquiring a signal -->
<item name="gps.on">10</item>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index 98b7a3f..f5fe80c 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -44,6 +44,7 @@
KernelUidCpuClusterTimeReaderTest.class,
KernelWakelockReaderTest.class,
LongSamplingCounterArrayTest.class,
+ PowerCalculatorTest.class,
PowerProfileTest.class
})
public class BatteryStatsTests {
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
new file mode 100644
index 0000000..14d62e0
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/PowerCalculatorTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2018 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 org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.os.BatteryStats;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class PowerCalculatorTest extends TestCase {
+ private static final long US_IN_HR = 1000L * 1000L * 60L * 60L;
+
+ @Mock
+ private PowerProfile mPowerProfile;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /** Test {@link MediaPowerCalculator#calculateApp} */
+ @Test
+ public void testMediaPowerCalculator() {
+ when(mPowerProfile.getAveragePower(PowerProfile.POWER_AUDIO)).thenReturn(12.0);
+ when(mPowerProfile.getAveragePower(PowerProfile.POWER_VIDEO)).thenReturn(25.0);
+
+ BatteryStats.Uid u = mock(BatteryStats.Uid.class);
+ BatteryStats.Timer audioTimer = mock(BatteryStats.Timer.class);
+ when(u.getAudioTurnedOnTimer()).thenReturn(audioTimer);
+ when(audioTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(2L * US_IN_HR);
+ BatteryStats.Timer videoTimer = mock(BatteryStats.Timer.class);
+ when(u.getVideoTurnedOnTimer()).thenReturn(videoTimer);
+ when(videoTimer.getTotalTimeLocked(2L * US_IN_HR, 0)).thenReturn(1L * US_IN_HR);
+
+ MediaPowerCalculator mediaPowerCalculator = new MediaPowerCalculator(mPowerProfile);
+ BatterySipper app = new BatterySipper(BatterySipper.DrainType.APP, null, 0);
+
+ mediaPowerCalculator.calculateApp(app, u, 2L * US_IN_HR, 2L * US_IN_HR, 0);
+ assertEquals(49.0, app.sumPower());
+ }
+
+
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
index c7de99a..2853c96 100644
--- a/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/PowerProfileTest.java
@@ -54,6 +54,8 @@
assertEquals(60.0, mProfile.getAveragePowerForCpuCore(1, 3));
assertEquals(3000.0, mProfile.getBatteryCapacity());
assertEquals(0.5, mProfile.getAveragePower(PowerProfile.POWER_AMBIENT_DISPLAY));
+ assertEquals(100.0, mProfile.getAveragePower(PowerProfile.POWER_AUDIO));
+ assertEquals(150.0, mProfile.getAveragePower(PowerProfile.POWER_VIDEO));
}
}