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));
     }
 
 }