Merge "Statsd atom: Power Use"
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 53d9673..244974d 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -192,6 +192,9 @@
         NativeProcessMemoryState native_process_memory_state = 10036;
         CpuTimePerThreadFreq cpu_time_per_thread_freq = 10037;
         OnDevicePowerMeasurement on_device_power_measurement = 10038;
+        DeviceCalculatedPowerUse device_calculated_power_use = 10039;
+        DeviceCalculatedPowerBlameUid device_calculated_power_blame_uid = 10040;
+        DeviceCalculatedPowerBlameOther device_calculated_power_blame_other = 10041;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -3198,3 +3201,63 @@
     // Time spent in frequency in milliseconds, since thread start.
     optional uint32 time_millis = 7;
 }
+
+/**
+ * Pulls on-device BatteryStats power use calculations for the overall device.
+ */
+message DeviceCalculatedPowerUse {
+    // Power used by the device in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    // Currently, this is BatteryStatsHelper.getComputedPower() (not getTotalPower()).
+    optional float computed_power_milli_amp_hours = 1;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations broken down by uid.
+ * This atom should be complemented by DeviceCalculatedPowerBlameOther, which contains the power use
+ * that is attributed to non-uid items. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameUid {
+    // Uid being blamed. Note: isolated uids have already been mapped to host uid.
+    optional int32 uid = 1 [(is_uid) = true];
+
+    // Power used by this uid in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    optional float power_milli_amp_hours = 2;
+}
+
+/**
+ * Pulls on-device BatteryStats power use calculations that are not due to a uid, broken down by
+ * drain type.
+ * This atom should be complemented by DeviceCalculatedPowerBlameUid, which contains the blame that
+ * is attributed uids. They must all be included to get the total power use.
+ */
+message DeviceCalculatedPowerBlameOther {
+    // The type of item whose power use is being reported.
+    enum DrainType {
+        AMBIENT_DISPLAY = 0;
+        // reserved 1; reserved "APP"; // Logged instead in DeviceCalculatedPowerBlameUid.
+        BLUETOOTH = 2;
+        CAMERA = 3;
+        // Cell-standby
+        CELL = 4;
+        FLASHLIGHT = 5;
+        IDLE = 6;
+        MEMORY = 7;
+        // Amount that total computed drain exceeded the drain estimated using the
+        // battery level changes and capacity.
+        OVERCOUNTED = 8;
+        PHONE = 9;
+        SCREEN = 10;
+        // Amount that total computed drain was below the drain estimated using the
+        // battery level changes and capacity.
+        UNACCOUNTED = 11;
+        // reserved 12; reserved "USER"; // Entire drain for a user. This is NOT supported.
+        WIFI = 13;
+    }
+    optional DrainType drain_type = 1;
+
+    // Power used by this item in mAh, as computed by BatteryStats, since BatteryStats last reset
+    // (i.e. roughly since device was last significantly charged).
+    optional float power_milli_amp_hours = 2;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 8378ae1..0e131cb 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -243,6 +243,20 @@
           {2, 3, 4, 5, 6},
           1 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::CPU_TIME_PER_THREAD_FREQ)}},
+        // DeviceCalculatedPowerUse.
+        {android::util::DEVICE_CALCULATED_POWER_USE,
+         {{}, {}, 1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_USE)}},
+        // DeviceCalculatedPowerBlameUid.
+        {android::util::DEVICE_CALCULATED_POWER_BLAME_UID,
+         {{}, {}, // BatteryStats already merged isolated with host ids so it's unnecessary here.
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_UID)}},
+        // DeviceCalculatedPowerBlameOther.
+        {android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER,
+         {{}, {},
+          1 * NS_PER_SEC,
+          new StatsCompanionServicePuller(android::util::DEVICE_CALCULATED_POWER_BLAME_OTHER)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 72e1ab9..866bd9a 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -104,14 +104,6 @@
     }
 
     /**
-     * Write a double value.
-     */
-    public void writeDouble(double val) {
-        mTypes.add(EVENT_TYPE_DOUBLE);
-        mValues.add(val);
-    }
-
-    /**
      * Write a storage value.
      */
     public void writeStorage(byte[] val) {
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index 0baf73c..02c9542 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -130,6 +130,10 @@
     public double wakeLockPowerMah;
     public double wifiPowerMah;
 
+    //                           ****************
+    // This list must be kept current with atoms.proto (frameworks/base/cmds/statsd/src/atoms.proto)
+    // so the ordinal values (and therefore the order) must never change.
+    //                           ****************
     public enum DrainType {
         AMBIENT_DISPLAY,
         @UnsupportedAppUsage
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index e81f678..7467114 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -41,6 +41,7 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 import android.text.format.DateUtils;
+import android.util.StatsLog;
 
 import junit.framework.TestCase;
 
@@ -258,6 +259,36 @@
         assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
     }
 
+    @Test
+    public void testDrainTypesSyncedWithProto() {
+        assertEquals(BatterySipper.DrainType.AMBIENT_DISPLAY.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__AMBIENT_DISPLAY);
+        // AtomsProto has no "APP"
+        assertEquals(BatterySipper.DrainType.BLUETOOTH.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__BLUETOOTH);
+        assertEquals(BatterySipper.DrainType.CAMERA.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CAMERA);
+        assertEquals(BatterySipper.DrainType.CELL.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__CELL);
+        assertEquals(BatterySipper.DrainType.FLASHLIGHT.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__FLASHLIGHT);
+        assertEquals(BatterySipper.DrainType.IDLE.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__IDLE);
+        assertEquals(BatterySipper.DrainType.MEMORY.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__MEMORY);
+        assertEquals(BatterySipper.DrainType.OVERCOUNTED.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__OVERCOUNTED);
+        assertEquals(BatterySipper.DrainType.PHONE.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__PHONE);
+        assertEquals(BatterySipper.DrainType.SCREEN.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__SCREEN);
+        assertEquals(BatterySipper.DrainType.UNACCOUNTED.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__UNACCOUNTED);
+        // AtomsProto has no "USER"
+        assertEquals(BatterySipper.DrainType.WIFI.ordinal(),
+                StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER__DRAIN_TYPE__WIFI);
+    }
+
     private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
             int uidCode, boolean isUidNull) {
         final BatterySipper sipper = mock(BatterySipper.class);
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 68f24fb..a4c5ed2 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -173,6 +173,7 @@
     <assign-permission name="android.permission.ACCESS_LOWPAN_STATE" uid="lowpan" />
     <assign-permission name="android.permission.MANAGE_LOWPAN_INTERFACES" uid="lowpan" />
 
+    <assign-permission name="android.permission.BATTERY_STATS" uid="statsd" />
     <assign-permission name="android.permission.DUMP" uid="statsd" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="statsd" />
     <assign-permission name="android.permission.STATSCOMPANION" uid="statsd" />
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index a1a6d9f..04c4629 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,9 +85,6 @@
       case StatsLogValue::FLOAT:
         mElements.push_back(StatsLogValue(in->readFloat()));
         break;
-      case StatsLogValue::DOUBLE:
-        mElements.push_back(StatsLogValue(in->readDouble()));
-        break;
       case StatsLogValue::STORAGE:
         mElements.push_back(StatsLogValue());
         mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 70220d8..3050409 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -47,6 +47,7 @@
 import android.net.NetworkStats;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.WifiActivityEnergyInfo;
+import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
 import android.os.Bundle;
@@ -87,6 +88,8 @@
 import com.android.internal.app.procstats.IProcessStats;
 import com.android.internal.app.procstats.ProcessStats;
 import com.android.internal.net.NetworkStatsFactory;
+import com.android.internal.os.BatterySipper;
+import com.android.internal.os.BatteryStatsHelper;
 import com.android.internal.os.BinderCallsStats.ExportedCallStat;
 import com.android.internal.os.KernelCpuSpeedReader;
 import com.android.internal.os.KernelCpuThreadReader;
@@ -205,6 +208,10 @@
     @Nullable
     private final KernelCpuThreadReader mKernelCpuThreadReader;
 
+    private BatteryStatsHelper mBatteryStatsHelper = null;
+    private static final int MAX_BATTERY_STATS_HELPER_FREQUENCY_MS = 1000;
+    private long mBatteryStatsHelperTimestampMs = -MAX_BATTERY_STATS_HELPER_FREQUENCY_MS;
+
     private static IThermalService sThermalService;
     private File mBaseDir =
             new File(SystemServiceManager.ensureSystemDir(), "stats_companion");
@@ -1449,6 +1456,73 @@
         pulledData.add(e);
     }
 
+    private BatteryStatsHelper getBatteryStatsHelper() {
+        if (mBatteryStatsHelper == null) {
+            final long callingToken = Binder.clearCallingIdentity();
+            try {
+                // clearCallingIdentity required for BatteryStatsHelper.checkWifiOnly().
+                mBatteryStatsHelper = new BatteryStatsHelper(mContext, false);
+            } finally {
+                Binder.restoreCallingIdentity(callingToken);
+            }
+            mBatteryStatsHelper.create((Bundle) null);
+        }
+        long currentTime = SystemClock.elapsedRealtime();
+        if (currentTime - mBatteryStatsHelperTimestampMs >= MAX_BATTERY_STATS_HELPER_FREQUENCY_MS) {
+            // Load BatteryStats and do all the calculations.
+            mBatteryStatsHelper.refreshStats(BatteryStats.STATS_SINCE_CHARGED, UserHandle.USER_ALL);
+            // Calculations are done so we don't need to save the raw BatteryStats data in RAM.
+            mBatteryStatsHelper.clearStats();
+            mBatteryStatsHelperTimestampMs = currentTime;
+        }
+        return mBatteryStatsHelper;
+    }
+
+    private void pullDeviceCalculatedPowerUse(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        BatteryStatsHelper bsHelper = getBatteryStatsHelper();
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+        e.writeFloat((float) bsHelper.getComputedPower());
+        pulledData.add(e);
+    }
+
+    private void pullDeviceCalculatedPowerBlameUid(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return;
+        }
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType != bs.drainType.APP) {
+                continue;
+            }
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(bs.uidObj.getUid());
+            e.writeFloat((float) bs.totalPowerMah);
+            pulledData.add(e);
+        }
+    }
+
+    private void pullDeviceCalculatedPowerBlameOther(int tagId,
+            long elapsedNanos, final long wallClockNanos, List<StatsLogEventWrapper> pulledData) {
+        final List<BatterySipper> sippers = getBatteryStatsHelper().getUsageList();
+        if (sippers == null) {
+            return;
+        }
+        for (BatterySipper bs : sippers) {
+            if (bs.drainType == bs.drainType.APP) {
+                continue; // This is a separate atom; see pullDeviceCalculatedPowerBlameUid().
+            }
+            if (bs.drainType == bs.drainType.USER) {
+                continue; // This is not supported. We purposefully calculate over USER_ALL.
+            }
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeInt(bs.drainType.ordinal());
+            e.writeFloat((float) bs.totalPowerMah);
+            pulledData.add(e);
+        }
+    }
+
     private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
@@ -1674,6 +1748,18 @@
                 pullCpuTimePerThreadFreq(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.DEVICE_CALCULATED_POWER_USE: {
+                pullDeviceCalculatedPowerUse(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_UID: {
+                pullDeviceCalculatedPowerBlameUid(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.DEVICE_CALCULATED_POWER_BLAME_OTHER: {
+                pullDeviceCalculatedPowerBlameOther(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;