Merge "Calculate and account for memory power use"
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index f0cc390..370f7f9 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -30,6 +30,7 @@
 import android.telephony.SignalStrength;
 import android.text.format.DateFormat;
 import android.util.ArrayMap;
+import android.util.LongSparseArray;
 import android.util.MutableBoolean;
 import android.util.Pair;
 import android.util.Printer;
@@ -2450,6 +2451,8 @@
 
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
+    public abstract LongSparseArray<? extends Timer> getKernelMemoryStats();
+
     public abstract void writeToParcelWithoutUids(Parcel out, int flags);
 
     private final static void formatTimeRaw(StringBuilder out, long seconds) {
@@ -4116,6 +4119,17 @@
             }
         }
 
+        final LongSparseArray<? extends Timer> mMemoryStats = getKernelMemoryStats();
+        pw.println("Memory Stats");
+        for (int i = 0; i < mMemoryStats.size(); i++) {
+            sb.setLength(0);
+            sb.append("Bandwidth ");
+            sb.append(mMemoryStats.keyAt(i));
+            sb.append(" Time ");
+            sb.append(mMemoryStats.valueAt(i).getTotalTimeLocked(rawRealtime, which));
+            pw.println(sb.toString());
+        }
+
         for (int iu=0; iu<NU; iu++) {
             final int uid = uidStats.keyAt(iu);
             if (reqUid >= 0 && uid != reqUid && uid != Process.SYSTEM_UID) {
diff --git a/core/java/com/android/internal/os/BatterySipper.java b/core/java/com/android/internal/os/BatterySipper.java
index d92e596..5ea9475 100644
--- a/core/java/com/android/internal/os/BatterySipper.java
+++ b/core/java/com/android/internal/os/BatterySipper.java
@@ -88,7 +88,8 @@
         USER,
         UNACCOUNTED,
         OVERCOUNTED,
-        CAMERA
+        CAMERA,
+        MEMORY
     }
 
     public BatterySipper(DrainType drainType, Uid uid, double value) {
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 27ffb8b..1ae5c66 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -125,6 +125,7 @@
     PowerCalculator mSensorPowerCalculator;
     PowerCalculator mCameraPowerCalculator;
     PowerCalculator mFlashlightPowerCalculator;
+    PowerCalculator mMemoryPowerCalculator;
 
     boolean mHasWifiPowerReporting = false;
     boolean mHasBluetoothPowerReporting = false;
@@ -342,6 +343,11 @@
         }
         mCpuPowerCalculator.reset();
 
+        if (mMemoryPowerCalculator == null) {
+            mMemoryPowerCalculator = new MemoryPowerCalculator(mPowerProfile);
+        }
+        mMemoryPowerCalculator.reset();
+
         if (mWakelockPowerCalculator == null) {
             mWakelockPowerCalculator = new WakelockPowerCalculator(mPowerProfile);
         }
@@ -672,12 +678,23 @@
         }
     }
 
+    private void addMemoryUsage() {
+        BatterySipper memory = new BatterySipper(DrainType.MEMORY, null, 0);
+        mMemoryPowerCalculator.calculateRemaining(memory, mStats, mRawRealtimeUs, mRawUptimeUs,
+                mStatsType);
+        memory.sumPower();
+        if (memory.totalPowerMah > 0) {
+            mUsageList.add(memory);
+        }
+    }
+
     private void processMiscUsage() {
         addUserUsage();
         addPhoneUsage();
         addScreenUsage();
         addWiFiUsage();
         addBluetoothUsage();
+        addMemoryUsage();
         addIdleUsage(); // Not including cellular idle power
         // Don't compute radio usage if it's a wifi-only device
         if (!mWifiOnly) {
diff --git a/core/java/com/android/internal/os/MemoryPowerCalculator.java b/core/java/com/android/internal/os/MemoryPowerCalculator.java
new file mode 100644
index 0000000..efd3ab5
--- /dev/null
+++ b/core/java/com/android/internal/os/MemoryPowerCalculator.java
@@ -0,0 +1,54 @@
+package com.android.internal.os;
+
+import android.os.BatteryStats;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+public class MemoryPowerCalculator extends PowerCalculator {
+
+    public static final String TAG = "MemoryPowerCalculator";
+    private static final boolean DEBUG = BatteryStatsHelper.DEBUG;
+    private final double[] powerAverages;
+
+    public MemoryPowerCalculator(PowerProfile profile) {
+        int numBuckets = profile.getNumElements(PowerProfile.POWER_MEMORY);
+        powerAverages = new double[numBuckets];
+        for (int i = 0; i < numBuckets; i++) {
+            powerAverages[i] = profile.getAveragePower(PowerProfile.POWER_MEMORY, i);
+            if (powerAverages[i] == 0 && DEBUG) {
+                Log.d(TAG, "Problem with PowerProfile. Received 0 value in MemoryPowerCalculator");
+            }
+        }
+    }
+
+    @Override
+    public void calculateApp(BatterySipper app, BatteryStats.Uid u, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {}
+
+    @Override
+    public void calculateRemaining(BatterySipper app, BatteryStats stats, long rawRealtimeUs,
+            long rawUptimeUs, int statsType) {
+        double totalMah = 0;
+        long totalTimeMs = 0;
+        LongSparseArray<? extends BatteryStats.Timer> timers = stats.getKernelMemoryStats();
+        for (int i = 0; i < timers.size() && i < powerAverages.length; i++) {
+            double mAatRail = powerAverages[(int) timers.keyAt(i)];
+            long timeMs = timers.valueAt(i).getTotalTimeLocked(rawRealtimeUs, statsType);
+            double mAm = (mAatRail * timeMs) / (1000*60);
+            if(DEBUG) {
+                Log.d(TAG, "Calculating mAh for bucket " + timers.keyAt(i) + " while unplugged");
+                Log.d(TAG, "Converted power profile number from "
+                        + powerAverages[(int) timers.keyAt(i)] + " into " + mAatRail);
+                Log.d(TAG, "Calculated mAm " + mAm);
+            }
+            totalMah += mAm/60;
+            totalTimeMs += timeMs;
+        }
+        app.usagePowerMah = totalMah;
+        app.usageTimeMs = totalTimeMs;
+        if (DEBUG) {
+            Log.d(TAG, String.format("Calculated total mAh for memory %f while unplugged %d ",
+                    totalMah, totalTimeMs));
+        }
+    }
+}
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index ad14a20..51cf2ea 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -171,6 +171,11 @@
     public static final String POWER_FLASHLIGHT = "camera.flashlight";
 
     /**
+     * Power consumption when DDR is being used.
+     */
+    public static final String POWER_MEMORY = "memory.bandwidths";
+
+    /**
      * Average power consumption when the camera is on over all standard use cases.
      *
      * TODO: Add more fine-grained camera power metrics.
@@ -365,6 +370,24 @@
     }
 
     /**
+     * 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) {
+        if (sPowerMap.containsKey(key)) {
+            Object data = sPowerMap.get(key);
+            if (data instanceof Double[]) {
+                final Double[] values = (Double[]) data;
+                return values.length;
+            } else {
+                return 1;
+            }
+        }
+        return 0;
+    }
+
+    /**
      * 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
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ff13125..67cac47 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1516,6 +1516,7 @@
 
                 mStats.updateCpuTimeLocked();
                 mStats.updateKernelWakelocksLocked();
+                mStats.updateKernelMemoryBandwidthLocked();
 
                 if (wifiInfo != null) {
                     if (wifiInfo.isValid()) {