Merge change 3857 into donut

* changes:
  Allow arrays of values for power profile data.
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 99a381c..16a3bad 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -30,9 +30,11 @@
 import android.util.Printer;
 import android.util.SparseArray;
 
+import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -53,7 +55,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 38;
+    private static final int VERSION = 39;
 
     private final File mFile;
     private final File mBackupFile;
@@ -94,7 +96,7 @@
     
     boolean mScreenOn;
     StopwatchTimer mScreenOnTimer;
-    
+
     int mScreenBrightnessBin = -1;
     final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
     
@@ -137,10 +139,10 @@
     long mTrackBatteryUptimeStart;
     long mTrackBatteryPastRealtime;
     long mTrackBatteryRealtimeStart;
-    
+
     long mUnpluggedBatteryUptime;
     long mUnpluggedBatteryRealtime;
-    
+
     /*
      * These keep track of battery levels (1-100) at the last plug event and the last unplug event.
      */
@@ -149,6 +151,15 @@
 
     long mLastWriteTime = 0; // Milliseconds
 
+    // Mobile data transferred while on battery
+    private long[] mMobileDataTx = new long[4];
+    private long[] mMobileDataRx = new long[4];
+    private long[] mTotalDataTx = new long[4];
+    private long[] mTotalDataRx = new long[4];
+
+    private long mRadioDataUptime;
+    private long mRadioDataStart;
+
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
      */
@@ -893,7 +904,40 @@
         }
         return kwlt;
     }
-    
+
+    private void doDataPlug(long[] dataTransfer, long currentBytes) {
+        dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
+        dataTransfer[STATS_UNPLUGGED] = -1;
+    }
+
+    private void doDataUnplug(long[] dataTransfer, long currentBytes) {
+        dataTransfer[STATS_UNPLUGGED] = currentBytes;
+    }
+
+    private long getCurrentRadioDataUptimeMs() {
+        try {
+            File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms");
+            if (!awakeTimeFile.exists()) return 0;
+            BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile));
+            String line = br.readLine();
+            br.close();
+            return Long.parseLong(line);
+        } catch (NumberFormatException nfe) {
+            // Nothing
+        } catch (IOException ioe) {
+            // Nothing
+        }
+        return 0;
+    }
+
+    public long getRadioDataUptimeMs() {
+        if (mRadioDataStart == -1) {
+            return mRadioDataUptime;
+        } else {
+            return getCurrentRadioDataUptimeMs() - mRadioDataStart;
+        }
+    }
+
     public void doUnplug(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -905,8 +949,16 @@
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime);
         }
+        // Track total mobile data
+        doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes());
+        doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes());
+        doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes());
+        doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes());
+        // Track radio awake time
+        mRadioDataStart = getCurrentRadioDataUptimeMs();
+        mRadioDataUptime = 0;
     }
-    
+
     public void doPlug(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
@@ -922,8 +974,15 @@
         for (int i = mUnpluggables.size() - 1; i >= 0; i--) {
             mUnpluggables.get(i).plug(batteryUptime, batteryRealtime);
         }
+        doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes());
+        doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes());
+        doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes());
+        doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes());
+        // Track radio awake time
+        mRadioDataUptime = getRadioDataUptimeMs();
+        mRadioDataStart = -1;
     }
-    
+
     public void noteStartGps(int uid) {
         mUidStats.get(uid).noteStartGps();
     }
@@ -931,7 +990,7 @@
     public void noteStopGps(int uid) {
         mUidStats.get(uid).noteStopGps();
     }
-    
+
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
             mScreenOn = true;
@@ -1039,6 +1098,7 @@
                     break;
             }
         }
+        if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
@@ -2701,7 +2761,44 @@
     public long getBatteryRealtime(long curTime) {
         return getBatteryRealtimeLocked(curTime);
     }
-    
+
+    private long getTcpBytes(long current, long[] dataBytes, int which) {
+        if (which == STATS_LAST) {
+            return dataBytes[STATS_LAST];
+        } else {
+            if (which == STATS_UNPLUGGED) {
+                if (dataBytes[STATS_UNPLUGGED] < 0) {
+                    return dataBytes[STATS_LAST];
+                } else {
+                    return current - dataBytes[STATS_UNPLUGGED];
+                }
+            } else if (which == STATS_TOTAL) {
+                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
+            }
+            return current - dataBytes[STATS_CURRENT];
+        }
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getMobileTcpBytesSent(int which) {
+        return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getMobileTcpBytesReceived(int which) {
+        return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getTotalTcpBytesSent(int which) {
+        return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which);
+    }
+
+    /** Only STATS_UNPLUGGED works properly */
+    public long getTotalTcpBytesReceived(int which) {
+        return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which);
+    }
+
     @Override
     public int getDischargeStartLevel() {
         synchronized(this) {
@@ -3227,6 +3324,18 @@
         mDischargeCurrentLevel = in.readInt();
         mLastWriteTime = in.readLong();
 
+        mMobileDataRx[STATS_LAST] = in.readLong();
+        mMobileDataRx[STATS_UNPLUGGED] = -1;
+        mMobileDataTx[STATS_LAST] = in.readLong();
+        mMobileDataTx[STATS_UNPLUGGED] = -1;
+        mTotalDataRx[STATS_LAST] = in.readLong();
+        mTotalDataRx[STATS_UNPLUGGED] = -1;
+        mTotalDataTx[STATS_LAST] = in.readLong();
+        mTotalDataTx[STATS_UNPLUGGED] = -1;
+
+        mRadioDataUptime = in.readLong();
+        mRadioDataStart = -1;
+
         mKernelWakelockStats.clear();
         int NKW = in.readInt();
         for (int ikw = 0; ikw < NKW; ikw++) {
@@ -3301,6 +3410,14 @@
         out.writeInt(mDischargeCurrentLevel);
         out.writeLong(mLastWriteTime);
 
+        out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED));
+        out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED));
+
+        // Write radio uptime for data
+        out.writeLong(getRadioDataUptimeMs());
+
         out.writeInt(mKernelWakelockStats.size());
         for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) {
             SamplingTimer kwlt = ent.getValue();
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index f08dddd..a37bf6e 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -26,6 +26,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
@@ -118,26 +119,28 @@
      */
     public static final String POWER_VIDEO = "dsp.video";
 
-    static final HashMap<String, Double> sPowerMap = new HashMap<String, Double>();
+    static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>();
 
     private static final String TAG_DEVICE = "device";
     private static final String TAG_ITEM = "item";
+    private static final String TAG_ARRAY = "array";
+    private static final String TAG_ARRAYITEM = "value";
     private static final String ATTR_NAME = "name";
 
-    public PowerProfile(Context context, CharSequence profile) {
+    public PowerProfile(Context context) {
         // Read the XML file for the given profile (normally only one per
         // device)
         if (sPowerMap.size() == 0) {
-            readPowerValuesFromXml(context, profile);
+            readPowerValuesFromXml(context);
         }
     }
 
-    private void readPowerValuesFromXml(Context context, CharSequence profile) {
-        // FIXME
-        //int id = context.getResources().getIdentifier(profile.toString(), "xml", 
-        //        "com.android.internal");
-        int id = com.android.internal.R.xml.power_profile_default;
+    private void readPowerValuesFromXml(Context context) {
+        int id = com.android.internal.R.xml.power_profile;
         XmlResourceParser parser = context.getResources().getXml(id);
+        boolean parsingArray = false;
+        ArrayList<Double> array = new ArrayList<Double>();
+        String arrayName = null;
 
         try {
             XmlUtils.beginDocument(parser, TAG_DEVICE);
@@ -145,22 +148,39 @@
             while (true) {
                 XmlUtils.nextElement(parser);
 
-                String element = parser.getName(); 
-                if (element == null || !(element.equals(TAG_ITEM))) {
-                    break;
+                String element = parser.getName();
+                if (element == null) break;
+                
+                if (parsingArray && !element.equals(TAG_ARRAYITEM)) {
+                    // Finish array
+                    sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+                    parsingArray = false;
                 }
-
-                String name = parser.getAttributeValue(null, ATTR_NAME);
-                if (parser.next() == XmlPullParser.TEXT) {
-                    String power = parser.getText();
-                    double value = 0;
-                    try {
-                        value = Double.valueOf(power);
-                    } catch (NumberFormatException nfe) {
+                if (element.equals(TAG_ARRAY)) {
+                    parsingArray = true;
+                    array.clear();
+                    arrayName = parser.getAttributeValue(null, ATTR_NAME);
+                } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) {
+                    String name = null;
+                    if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME);
+                    if (parser.next() == XmlPullParser.TEXT) {
+                        String power = parser.getText();
+                        double value = 0;
+                        try {
+                            value = Double.valueOf(power);
+                        } catch (NumberFormatException nfe) {
+                        }
+                        if (element.equals(TAG_ITEM)) {
+                            sPowerMap.put(name, value);
+                        } else if (parsingArray) {
+                            array.add(value);
+                        }
                     }
-                    sPowerMap.put(name, value);
                 }
             }
+            if (parsingArray) {
+                sPowerMap.put(arrayName, array.toArray(new Double[array.size()]));
+            }
         } catch (XmlPullParserException e) {
             throw new RuntimeException(e);
         } catch (IOException e) {
@@ -177,7 +197,40 @@
      */
     public double getAveragePower(String type) {
         if (sPowerMap.containsKey(type)) {
-            return sPowerMap.get(type);
+            Object data = sPowerMap.get(type);
+            if (data instanceof Double[]) {
+                return ((Double[])data)[0];
+            } else {
+                return (Double) sPowerMap.get(type);
+            }
+        } else {
+            return 0;
+        }
+    }
+    
+    /**
+     * Returns the average current in mA consumed by the subsystem for the given level. 
+     * @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.
+     * @return the average current in milliAmps.
+     */
+    public double getAveragePower(String type, int level) {
+        if (sPowerMap.containsKey(type)) {
+            Object data = sPowerMap.get(type);
+            if (data instanceof double[]) {
+                final double[] values = (double[]) data;
+                if (values.length > level) {
+                    return values[level];
+                } else if (values.length < 0) {
+                    return values[0];
+                } else {
+                    return values[values.length - 1];
+                }
+            } else {
+                return (Double) data;
+            }
         } else {
             return 0;
         }
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
new file mode 100644
index 0000000..859902e
--- /dev/null
+++ b/core/res/res/xml/power_profile.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, 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.
+*/
+-->
+
+<device name="Android">
+  <item name="none">0</item>
+  <item name="screen.on">0.1</item>
+  <item name="bluetooth.active">0.1</item>
+  <item name="bluetooth.on">0.1</item>
+  <item name="screen.full">0.1</item>
+  <item name="wifi.on">0.1</item>
+  <item name="wifi.active">0.1</item>
+  <item name="wifi.scan">0.1</item>
+  <item name="cpu.idle">0.1</item>
+  <item name="cpu.normal">0.2</item>
+  <item name="cpu.full">1</item>
+  <item name="dsp.audio">0.1</item>
+  <item name="dsp.video">0.1</item>
+  <item name="radio.active">1</item>
+  <item name="gps.on">1</item>
+  <array name="radio.on"> <!-- Strength 0 to BINS-1 -->
+      <value>1</value>
+      <value>0.1</value>
+  </array>
+</device>
diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
deleted file mode 100644
index ceecb1a..0000000
--- a/core/res/res/xml/power_profile_default.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2009, 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.
-*/
--->
-
-<device name="Android">
-  <item name="none">0</item>
-  <item name="screen.on">30</item>
-  <item name="bluetooth.active">103</item>
-  <item name="bluetooth.on">5</item>
-  <item name="screen.full">114</item>
-  <item name="wifi.on">23</item>
-  <item name="wifi.active">200</item>
-  <item name="wifi.scan">200</item>
-  <item name="cpu.idle">1.6</item>
-  <item name="cpu.normal">100</item>
-  <item name="cpu.full">140</item>
-  <item name="dsp.audio">70</item>
-  <item name="dsp.video">100</item>
-  <item name="radio.on">3</item>
-  <item name="radio.active">175</item>
-  <item name="gps.on">120</item>
-</device>