Various improvements to battery stats collection

We now clear the battery stats when unplugging after the
battery is full.  This allows us to use the "total" stats as
a new "since last charged" stat.  Total is gone.  I never used
it, it was worthless.  Since last charged is a lot more
interesting.

The battery history now collects a lot more stats, and keeps
control over how much it can collect.  Printing is now more
descriptive.

The kinds of stats have been renamed to SINCE_UNPLUGGED and
SINCE_DISCHARGED.  The other two stats are still there, but
no longer printed; a future change will eliminate them
completely along with all of their state.

Change-Id: I4e9fcfcf8c30510092c76a8594f6021e9502fbc1
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 4adeaeb..e3f3b87 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -87,7 +87,7 @@
     /**
      * Include all of the data in the stats, including previously saved data.
      */
-    public static final int STATS_TOTAL = 0;
+    public static final int STATS_SINCE_CHARGED = 0;
 
     /**
      * Include only the last run in the stats.
@@ -102,7 +102,7 @@
     /**
      * Include only the run since the last time the device was unplugged in the stats.
      */
-    public static final int STATS_UNPLUGGED = 3;
+    public static final int STATS_SINCE_UNPLUGGED = 3;
 
     // NOTE: Update this list if you add/change any stats above.
     // These characters are supposed to represent "total", "last", "current", 
@@ -377,14 +377,26 @@
         }
     }
 
-    public final class BatteryHistoryRecord implements Parcelable {
-        public BatteryHistoryRecord next;
+    public final class HistoryItem implements Parcelable {
+        public HistoryItem next;
         
         public long time;
-        public byte batteryLevel;
         
-        public static final int STATE_SCREEN_MASK = 0x000000f;
-        public static final int STATE_SCREEN_SHIFT = 0;
+        public static final byte CMD_UPDATE = 0;
+        public static final byte CMD_START = 1;
+        
+        public byte cmd;
+        
+        public byte batteryLevel;
+        public byte batteryStatus;
+        public byte batteryHealth;
+        public byte batteryPlugType;
+        
+        public char batteryTemperature;
+        public char batteryVoltage;
+        
+        public static final int STATE_BRIGHTNESS_MASK = 0x000000f;
+        public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_SIGNAL_STRENGTH_MASK = 0x00000f0;
         public static final int STATE_SIGNAL_STRENGTH_SHIFT = 4;
         public static final int STATE_PHONE_STATE_MASK = 0x0000f00;
@@ -396,23 +408,32 @@
         public static final int STATE_SCREEN_ON_FLAG = 1<<29;
         public static final int STATE_GPS_ON_FLAG = 1<<28;
         public static final int STATE_PHONE_ON_FLAG = 1<<27;
-        public static final int STATE_WIFI_ON_FLAG = 1<<26;
-        public static final int STATE_WIFI_RUNNING_FLAG = 1<<25;
-        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<24;
-        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<23;
-        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<22;
-        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<21;
-        public static final int STATE_AUDIO_ON_FLAG = 1<<20;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<19;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<26;
+        public static final int STATE_WIFI_ON_FLAG = 1<<25;
+        public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
+        public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<23;
+        public static final int STATE_WIFI_SCAN_LOCK_FLAG = 1<<22;
+        public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<21;
+        public static final int STATE_BLUETOOTH_ON_FLAG = 1<<20;
+        public static final int STATE_AUDIO_ON_FLAG = 1<<19;
+        public static final int STATE_VIDEO_ON_FLAG = 1<<18;
         
         public int states;
 
-        public BatteryHistoryRecord() {
+        public HistoryItem() {
         }
         
-        public BatteryHistoryRecord(long time, Parcel src) {
+        public HistoryItem(long time, Parcel src) {
             this.time = time;
-            batteryLevel = (byte)src.readInt();
+            int bat = src.readInt();
+            cmd = (byte)(bat&0xff);
+            batteryLevel = (byte)((bat>>8)&0xff);
+            batteryStatus = (byte)((bat>>16)&0xf);
+            batteryHealth = (byte)((bat>>20)&0xf);
+            batteryPlugType = (byte)((bat>>24)&0xf);
+            bat = src.readInt();
+            batteryTemperature = (char)(bat&0xffff);
+            batteryVoltage = (char)((bat>>16)&0xffff);
             states = src.readInt();
         }
         
@@ -422,15 +443,56 @@
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeLong(time);
-            dest.writeInt(batteryLevel);
+            int bat = (((int)cmd)&0xff)
+                    | ((((int)batteryLevel)<<8)&0xff00)
+                    | ((((int)batteryStatus)<<16)&0xf0000)
+                    | ((((int)batteryHealth)<<20)&0xf00000)
+                    | ((((int)batteryPlugType)<<24)&0xf000000);
+            dest.writeInt(bat);
+            bat = (((int)batteryTemperature)&0xffff)
+                    | ((((int)batteryVoltage)<<16)&0xffff0000);
+            dest.writeInt(bat);
             dest.writeInt(states);
         }
+        
+        public void setTo(long time, byte cmd, HistoryItem o) {
+            this.time = time;
+            this.cmd = cmd;
+            batteryLevel = o.batteryLevel;
+            batteryStatus = o.batteryStatus;
+            batteryHealth = o.batteryHealth;
+            batteryPlugType = o.batteryPlugType;
+            batteryTemperature = o.batteryTemperature;
+            batteryVoltage = o.batteryVoltage;
+            states = o.states;
+        }
+    }
+    
+    public static final class BitDescription {
+        public final int mask;
+        public final int shift;
+        public final String name;
+        public final String[] values;
+        
+        public BitDescription(int mask, String name) {
+            this.mask = mask;
+            this.shift = -1;
+            this.name = name;
+            this.values = null;
+        }
+        
+        public BitDescription(int mask, int shift, String name, String[] values) {
+            this.mask = mask;
+            this.shift = shift;
+            this.name = name;
+            this.values = values;
+        }
     }
     
     /**
      * Return the current history of battery state changes.
      */
-    public abstract BatteryHistoryRecord getHistory();
+    public abstract HistoryItem getHistory();
     
     /**
      * Returns the number of times the device has been started.
@@ -517,13 +579,23 @@
     public static final int DATA_CONNECTION_GPRS = 1;
     public static final int DATA_CONNECTION_EDGE = 2;
     public static final int DATA_CONNECTION_UMTS = 3;
-    public static final int DATA_CONNECTION_OTHER = 4;
+    public static final int DATA_CONNECTION_CDMA = 4;
+    public static final int DATA_CONNECTION_EVDO_0 = 5;
+    public static final int DATA_CONNECTION_EVDO_A = 6;
+    public static final int DATA_CONNECTION_1xRTT = 7;
+    public static final int DATA_CONNECTION_HSDPA = 8;
+    public static final int DATA_CONNECTION_HSUPA = 9;
+    public static final int DATA_CONNECTION_HSPA = 10;
+    public static final int DATA_CONNECTION_IDEN = 11;
+    public static final int DATA_CONNECTION_EVDO_B = 12;
+    public static final int DATA_CONNECTION_OTHER = 13;
     
     static final String[] DATA_CONNECTION_NAMES = {
-        "none", "gprs", "edge", "umts", "other"
+        "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
+        "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "other"
     };
     
-    public static final int NUM_DATA_CONNECTION_TYPES = 5;
+    public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
     
     /**
      * Returns the time in microseconds that the phone has been running with
@@ -541,6 +613,35 @@
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
+    
+    public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
+            = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged"),
+        new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen"),
+        new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps"),
+        new BitDescription(HistoryItem.STATE_PHONE_ON_FLAG, "phone"),
+        new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning"),
+        new BitDescription(HistoryItem.STATE_WIFI_ON_FLAG, "wifi"),
+        new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running"),
+        new BitDescription(HistoryItem.STATE_WIFI_FULL_LOCK_FLAG, "wifi_full_lock"),
+        new BitDescription(HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG, "wifi_scan_lock"),
+        new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast"),
+        new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth"),
+        new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio"),
+        new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video"),
+        new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
+                HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness",
+                SCREEN_BRIGHTNESS_NAMES),
+        new BitDescription(HistoryItem.STATE_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT, "signal_strength",
+                SIGNAL_STRENGTH_NAMES),
+        new BitDescription(HistoryItem.STATE_PHONE_STATE_MASK,
+                HistoryItem.STATE_PHONE_STATE_SHIFT, "phone_state",
+                new String[] {"in", "out", "emergency", "off"}),
+        new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
+                HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn",
+                DATA_CONNECTION_NAMES),
+    };
 
     /**
      * Returns the time in microseconds that wifi has been on while the device was
@@ -832,7 +933,7 @@
         
         // Dump "battery" stat
         dumpLine(pw, 0 /* uid */, category, BATTERY_DATA, 
-                which == STATS_TOTAL ? getStartCount() : "N/A",
+                which == STATS_SINCE_CHARGED ? getStartCount() : "N/A",
                 whichBatteryRealtime / 1000, whichBatteryUptime / 1000,
                 totalRealtime / 1000, totalUptime / 1000); 
         
@@ -905,7 +1006,7 @@
         }
         dumpLine(pw, 0 /* uid */, category, DATA_CONNECTION_COUNT_DATA, args);
         
-        if (which == STATS_UNPLUGGED) {
+        if (which == STATS_SINCE_UNPLUGGED) {
             dumpLine(pw, 0 /* uid */, category, BATTERY_LEVEL_DATA, getDischargeStartLevel(), 
                     getDischargeCurrentLevel());
         }
@@ -1253,7 +1354,7 @@
         
         pw.println(" ");
 
-        if (which == STATS_UNPLUGGED) {
+        if (which == STATS_SINCE_UNPLUGGED) {
             if (getIsOnBattery()) {
                 pw.print(prefix); pw.println("  Device is currently unplugged");
                 pw.print(prefix); pw.print("    Discharge cycle start level: "); 
@@ -1477,6 +1578,30 @@
         }
     }
 
+    void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
+        int diff = oldval ^ newval;
+        if (diff == 0) return;
+        for (int i=0; i<descriptions.length; i++) {
+            BitDescription bd = descriptions[i];
+            if ((diff&bd.mask) != 0) {
+                if (bd.shift < 0) {
+                    pw.print((newval&bd.mask) != 0 ? " +" : " -");
+                    pw.print(bd.name);
+                } else {
+                    pw.print(" ");
+                    pw.print(bd.name);
+                    pw.print("=");
+                    int val = (newval&bd.mask)>>bd.shift;
+                    if (bd.values != null && val >= 0 && val < bd.values.length) {
+                        pw.print(bd.values[val]);
+                    } else {
+                        pw.print(val);
+                    }
+                }
+            }
+        }
+    }
+    
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -1484,33 +1609,129 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
-        BatteryHistoryRecord rec = getHistory();
+        HistoryItem rec = getHistory();
         if (rec != null) {
             pw.println("Battery History:");
+            int oldState = 0;
+            int oldStatus = -1;
+            int oldHealth = -1;
+            int oldPlug = -1;
+            int oldTemp = -1;
+            int oldVolt = -1;
             while (rec != null) {
                 pw.print("  ");
                 pw.print(rec.time);
                 pw.print(" ");
-                pw.print(rec.batteryLevel);
-                pw.print(" ");
-                pw.println(Integer.toHexString(rec.states));
+                if (rec.cmd == HistoryItem.CMD_START) {
+                    pw.println(" START");
+                } else {
+                    if (rec.batteryLevel < 10) pw.print("00");
+                    else if (rec.batteryLevel < 100) pw.print("0");
+                    pw.print(rec.batteryLevel);
+                    pw.print(" ");
+                    if (rec.states < 0x10) pw.print("0000000");
+                    else if (rec.states < 0x100) pw.print("000000");
+                    else if (rec.states < 0x1000) pw.print("00000");
+                    else if (rec.states < 0x10000) pw.print("0000");
+                    else if (rec.states < 0x100000) pw.print("000");
+                    else if (rec.states < 0x1000000) pw.print("00");
+                    else if (rec.states < 0x10000000) pw.print("0");
+                    pw.print(Integer.toHexString(rec.states));
+                    if (oldStatus != rec.batteryStatus) {
+                        oldStatus = rec.batteryStatus;
+                        pw.print(" status=");
+                        switch (oldStatus) {
+                            case BatteryManager.BATTERY_STATUS_UNKNOWN:
+                                pw.print("unknown");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_CHARGING:
+                                pw.print("charging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_DISCHARGING:
+                                pw.print("discharging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_NOT_CHARGING:
+                                pw.print("not-charging");
+                                break;
+                            case BatteryManager.BATTERY_STATUS_FULL:
+                                pw.print("full");
+                                break;
+                            default:
+                                pw.print(oldStatus);
+                                break;
+                        }
+                    }
+                    if (oldHealth != rec.batteryHealth) {
+                        oldHealth = rec.batteryHealth;
+                        pw.print(" health=");
+                        switch (oldHealth) {
+                            case BatteryManager.BATTERY_HEALTH_UNKNOWN:
+                                pw.print("unknown");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_GOOD:
+                                pw.print("good");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_OVERHEAT:
+                                pw.print("overheat");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_DEAD:
+                                pw.print("dead");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_OVER_VOLTAGE:
+                                pw.print("over-voltage");
+                                break;
+                            case BatteryManager.BATTERY_HEALTH_UNSPECIFIED_FAILURE:
+                                pw.print("failure");
+                                break;
+                            default:
+                                pw.print(oldHealth);
+                                break;
+                        }
+                    }
+                    if (oldPlug != rec.batteryPlugType) {
+                        oldPlug = rec.batteryPlugType;
+                        pw.print(" plug=");
+                        switch (oldPlug) {
+                            case 0:
+                                pw.print("none");
+                                break;
+                            case BatteryManager.BATTERY_PLUGGED_AC:
+                                pw.print("ac");
+                                break;
+                            case BatteryManager.BATTERY_PLUGGED_USB:
+                                pw.print("usb");
+                                break;
+                            default:
+                                pw.print(oldPlug);
+                                break;
+                        }
+                    }
+                    if (oldTemp != rec.batteryTemperature) {
+                        oldTemp = rec.batteryTemperature;
+                        pw.print(" temp=");
+                        pw.print(oldTemp);
+                    }
+                    if (oldVolt != rec.batteryVoltage) {
+                        oldVolt = rec.batteryVoltage;
+                        pw.print(" volt=");
+                        pw.print(oldVolt);
+                    }
+                    printBitDescriptions(pw, oldState, rec.states,
+                            HISTORY_STATE_DESCRIPTIONS);
+                    pw.println();
+                }
+                oldState = rec.states;
                 rec = rec.next;
             }
         }
         
-        pw.println("Total Statistics (Current and Historic):");
+        pw.println("Statistics since last charge:");
         pw.println("  System starts: " + getStartCount()
                 + ", currently on battery: " + getIsOnBattery());
-        dumpLocked(pw, "", STATS_TOTAL, -1);
+        dumpLocked(pw, "", STATS_SINCE_CHARGED, -1);
         pw.println("");
-        pw.println("Last Run Statistics (Previous run of system):");
-        dumpLocked(pw, "", STATS_LAST, -1);
-        pw.println("");
-        pw.println("Current Battery Statistics (Currently running system):");
-        dumpLocked(pw, "", STATS_CURRENT, -1);
-        pw.println("");
-        pw.println("Unplugged Statistics (Since last unplugged from power):");
-        dumpLocked(pw, "", STATS_UNPLUGGED, -1);
+        pw.println("Statistics since last unplugged:");
+        dumpLocked(pw, "", STATS_SINCE_UNPLUGGED, -1);
     }
     
     @SuppressWarnings("unused")
@@ -1525,14 +1746,11 @@
         }
         
         if (isUnpluggedOnly) {
-            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_UNPLUGGED, -1);
         }
         else {
-            dumpCheckinLocked(pw, STATS_TOTAL, -1);
-            dumpCheckinLocked(pw, STATS_LAST, -1);
-            dumpCheckinLocked(pw, STATS_UNPLUGGED, -1);
-            dumpCheckinLocked(pw, STATS_CURRENT, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_CHARGED, -1);
+            dumpCheckinLocked(pw, STATS_SINCE_UNPLUGGED, -1);
         }
     }
-    
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d5ccdeb..88ef395 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -50,8 +50,7 @@
     void noteScanWifiLockReleased(int uid);
     void noteWifiMulticastEnabled(int uid);
     void noteWifiMulticastDisabled(int uid);
-    void setOnBattery(boolean onBattery, int level);
-    void recordCurrentLevel(int level);
+    void setBatteryState(int status, int health, int plugType, int level, int temp, int volt);
     long getAwakeTimeBattery();
     long getAwakeTimePlugged();
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3833725..f8ab04c 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -20,6 +20,7 @@
 
 import android.bluetooth.BluetoothHeadset;
 import android.net.TrafficStats;
+import android.os.BatteryManager;
 import android.os.BatteryStats;
 import android.os.Parcel;
 import android.os.ParcelFormatException;
@@ -62,8 +63,11 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS' 
 
     // Current on-disk Parcel version
-    private static final int VERSION = 44;
+    private static final int VERSION = 48;
 
+    // Maximum number of items we will record in the history.
+    private static final int MAX_HISTORY_ITEMS = 1000;
+    
     // The maximum number of names wakelocks we will keep track of
     // per uid; once the limit is reached, we batch the remaining wakelocks
     // in to one common name.
@@ -95,10 +99,16 @@
     // is unplugged from power.
     final ArrayList<Unpluggable> mUnpluggables = new ArrayList<Unpluggable>();
     
-    BatteryHistoryRecord mHistory;
-    BatteryHistoryRecord mHistoryEnd;
-    BatteryHistoryRecord mHistoryCache;
-    final BatteryHistoryRecord mHistoryCur = new BatteryHistoryRecord();
+    boolean mShuttingDown;
+    
+    long mHistoryBaseTime;
+    boolean mHaveBatteryLevel = false;
+    boolean mRecordingHistory = true;
+    int mNumHistoryItems;
+    HistoryItem mHistory;
+    HistoryItem mHistoryEnd;
+    HistoryItem mHistoryCache;
+    final HistoryItem mHistoryCur = new HistoryItem();
     
     int mStartCount;
 
@@ -172,6 +182,7 @@
      * These keep track of battery levels (1-100) at the last plug event and the last unplug event.
      */
     int mDischargeStartLevel;
+    int mDischargeUnplugLevel;
     int mDischargeCurrentLevel;
 
     long mLastWriteTime = 0; // Milliseconds
@@ -238,12 +249,14 @@
      */
     public static class Counter extends BatteryStats.Counter implements Unpluggable {
         final AtomicInteger mCount = new AtomicInteger();
+        final ArrayList<Unpluggable> mUnpluggables;
         int mLoadedCount;
         int mLastCount;
         int mUnpluggedCount;
         int mPluggedCount;
         
         Counter(ArrayList<Unpluggable> unpluggables, Parcel in) {
+            mUnpluggables = unpluggables;
             mPluggedCount = in.readInt();
             mCount.set(mPluggedCount);
             mLoadedCount = in.readInt();
@@ -253,6 +266,7 @@
         }
 
         Counter(ArrayList<Unpluggable> unpluggables) {
+            mUnpluggables = unpluggables;
             unpluggables.add(this);
         }
         
@@ -295,9 +309,9 @@
                 val = mLastCount;
             } else {
                 val = mCount.get();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedCount;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedCount;
                 }
             }
@@ -316,6 +330,21 @@
             mCount.incrementAndGet();
         }
 
+        /**
+         * Clear state of this counter.
+         */
+        void reset(boolean detachIfReset) {
+            mCount.set(0);
+            mLoadedCount = mLastCount = mPluggedCount = mUnpluggedCount = 0;
+            if (detachIfReset) {
+                detach();
+            }
+        }
+        
+        void detach() {
+            mUnpluggables.remove(this);
+        }
+        
         void writeSummaryFromParcelLocked(Parcel out) {
             int count = mCount.get();
             out.writeInt(count);
@@ -350,7 +379,7 @@
      */
     public static abstract class Timer extends BatteryStats.Timer implements Unpluggable {
         final int mType;
-
+        final ArrayList<Unpluggable> mUnpluggables;
         
         int mCount;
         int mLoadedCount;
@@ -395,6 +424,7 @@
          */
         Timer(int type, ArrayList<Unpluggable> unpluggables, Parcel in) {
             mType = type;
+            mUnpluggables = unpluggables;
             
             mCount = in.readInt();
             mLoadedCount = in.readInt();
@@ -409,6 +439,7 @@
 
         Timer(int type, ArrayList<Unpluggable> unpluggables) {
             mType = type;
+            mUnpluggables = unpluggables;
             unpluggables.add(this);
         }
 
@@ -416,6 +447,22 @@
         
         protected abstract int computeCurrentCountLocked();
         
+        /**
+         * Clear state of this timer.  Returns true if the timer is inactive
+         * so can be completely dropped.
+         */
+        boolean reset(boolean detachIfReset) {
+            mTotalTime = mLoadedTime = mLastTime = 0;
+            mCount = mLoadedCount = mLastCount = 0;
+            if (detachIfReset) {
+                detach();
+            }
+            return true;
+        }
+        
+        void detach() {
+            mUnpluggables.remove(this);
+        }
         
         public void writeToParcel(Parcel out, long batteryRealtime) {
             out.writeInt(mCount);
@@ -480,9 +527,9 @@
                 val = mLastTime;
             } else {
                 val = computeRunTimeLocked(batteryRealtime);
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedTime;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedTime;
                 }
             }
@@ -497,9 +544,9 @@
                 val = mLastCount;
             } else {
                 val = computeCurrentCountLocked();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     val -= mUnpluggedCount;
-                } else if (which != STATS_TOTAL) {
+                } else if (which != STATS_SINCE_CHARGED) {
                     val -= mLoadedCount;
                 }
             }
@@ -670,6 +717,12 @@
             out.writeInt(mTrackingReportedValues ? 1 : 0);
         }
         
+        boolean reset(boolean detachIfReset) {
+            super.reset(detachIfReset);
+            setStale();
+            return true;
+        }
+        
         void writeSummaryFromParcelLocked(Parcel out, long batteryRealtime) {
             super.writeSummaryFromParcelLocked(out, batteryRealtime);
             out.writeLong(mCurrentReportedTotalTime);
@@ -692,7 +745,6 @@
         final ArrayList<StopwatchTimer> mTimerPool;
         int mNesting;
 
-
         /**
          * The last time at which we updated the timer.  If mNesting is > 0,
          * subtract this from the current battery time to find the amount of
@@ -842,6 +894,19 @@
             return mCount;
         }
 
+        boolean reset(boolean detachIfReset) {
+            boolean canDetach = mNesting <= 0;
+            super.reset(canDetach && detachIfReset);
+            return canDetach;
+        }
+        
+        void detach() {
+            super.detach();
+            if (mTimerPool != null) {
+                mTimerPool.remove(this);
+            }
+        }
+        
         void readSummaryFromParcelLocked(Parcel in) {
             super.readSummaryFromParcelLocked(in);
             mNesting = 0;
@@ -979,12 +1044,12 @@
     }
 
     private void doDataPlug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED];
-        dataTransfer[STATS_UNPLUGGED] = -1;
+        dataTransfer[STATS_LAST] = dataTransfer[STATS_SINCE_UNPLUGGED];
+        dataTransfer[STATS_SINCE_UNPLUGGED] = -1;
     }
 
     private void doDataUnplug(long[] dataTransfer, long currentBytes) {
-        dataTransfer[STATS_UNPLUGGED] = currentBytes;
+        dataTransfer[STATS_SINCE_UNPLUGGED] = currentBytes;
     }
 
     /**
@@ -1048,20 +1113,35 @@
         mBtHeadset = headset;
     }
 
-    void addHistoryRecord(long curTime) {
-        BatteryHistoryRecord rec = mHistoryCache;
+    void addHistoryRecordLocked(long curTime) {
+        if (!mHaveBatteryLevel || !mRecordingHistory) {
+            return;
+        }
+        if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
+            // Once we've reached the maximum number of items, we only
+            // record changes to the battery level.
+            if (mHistoryEnd != null && mHistoryEnd.batteryLevel
+                    == mHistoryCur.batteryLevel) {
+                return;
+            }
+        }
+        addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+    }
+    
+    void addHistoryRecordLocked(long curTime, byte cmd) {
+        HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
-            rec = new BatteryHistoryRecord();
+            rec = new HistoryItem();
         }
-        rec.time = curTime;
-        rec.batteryLevel = mHistoryCur.batteryLevel;
-        rec.states = mHistoryCur.states;
-        addHistoryRecord(rec);
+        rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        
+        addHistoryRecordLocked(rec);
     }
     
-    void addHistoryRecord(BatteryHistoryRecord rec) {
+    void addHistoryRecordLocked(HistoryItem rec) {
+        mNumHistoryItems++;
         rec.next = null;
         if (mHistoryEnd != null) {
             mHistoryEnd.next = rec;
@@ -1071,15 +1151,17 @@
         }
     }
     
-    void clearHistory() {
+    void clearHistoryLocked() {
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
             mHistoryCache = mHistory;
             mHistory = mHistoryEnd = null;
         }
+        mNumHistoryItems = 0;
+        mHistoryBaseTime = 0;
     }
     
-    public void doUnplug(long batteryUptime, long batteryRealtime) {
+    public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
             u.mStartedTcpBytesReceived = TrafficStats.getUidRxBytes(u.mUid);
@@ -1103,7 +1185,7 @@
         mBluetoothPingCount = 0;
     }
 
-    public void doPlug(long batteryUptime, long batteryRealtime) {
+    public void doPlugLocked(long batteryUptime, long batteryRealtime) {
         for (int iu = mUidStats.size() - 1; iu >= 0; iu--) {
             Uid u = mUidStats.valueAt(iu);
             if (u.mStartedTcpBytesReceived >= 0) {
@@ -1133,34 +1215,34 @@
 
     int mGpsNesting;
     
-    public void noteStartGps(int uid) {
+    public void noteStartGpsLocked(int uid) {
         if (mGpsNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mGpsNesting++;
         getUidStatsLocked(uid).noteStartGps();
     }
     
-    public void noteStopGps(int uid) {
+    public void noteStopGpsLocked(int uid) {
         mGpsNesting--;
         if (mGpsNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_GPS_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteStopGps();
     }
 
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mGpsNesting++;
             mScreenOn = true;
             mScreenOnTimer.startRunningLocked(this);
@@ -1172,10 +1254,10 @@
     
     public void noteScreenOffLocked() {
         if (mScreenOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_SCREEN_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mScreenOn = false;
             mScreenOnTimer.stopRunningLocked(this);
             if (mScreenBrightnessBin >= 0) {
@@ -1190,11 +1272,11 @@
         if (bin < 0) bin = 0;
         else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
         if (mScreenBrightnessBin != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SCREEN_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_SCREEN_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
+                    | (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mScreenOn) {
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(this);
@@ -1215,10 +1297,10 @@
     
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_PHONE_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneOn = true;
             mPhoneOnTimer.startRunningLocked(this);
         }
@@ -1226,10 +1308,10 @@
     
     public void notePhoneOffLocked() {
         if (mPhoneOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_PHONE_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_PHONE_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneOn = false;
             mPhoneOnTimer.stopRunningLocked(this);
         }
@@ -1251,7 +1333,11 @@
             }
         }
         // Stop Signal Scanning timer, in case we're going into service
-        while (mPhoneSignalScanningTimer.isRunningLocked()) {
+        if (mPhoneSignalScanningTimer.isRunningLocked()) {
+            mHistoryCur.states &= ~HistoryItem.STATE_PHONE_SCANNING_FLAG;
+            if (DEBUG_HISTORY) Slog.v(TAG, "Phone stopped scanning to: "
+                    + Integer.toHexString(mHistoryCur.states));
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneSignalScanningTimer.stopRunningLocked(this);
         }
 
@@ -1267,16 +1353,20 @@
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].startRunningLocked(this);
             }
             if (!mPhoneSignalScanningTimer.isRunningLocked()) {
+                mHistoryCur.states |= HistoryItem.STATE_PHONE_SCANNING_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Phone started scanning to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
                 mPhoneSignalScanningTimer.startRunningLocked(this);
             }
         }
         
         if (mPhoneServiceState != state) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_PHONE_STATE_MASK)
-                    | (state << BatteryHistoryRecord.STATE_PHONE_STATE_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_STATE_MASK)
+                    | (state << HistoryItem.STATE_PHONE_STATE_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone state " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mPhoneServiceState = state;
         }
     }
@@ -1305,11 +1395,11 @@
             else bin = SIGNAL_STRENGTH_POOR;
         }
         if (mPhoneSignalStrengthBin != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_SIGNAL_STRENGTH_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
+                    | (bin << HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mPhoneSignalStrengthBin >= 0) {
                 mPhoneSignalStrengthsTimer[mPhoneSignalStrengthBin].stopRunningLocked(this);
             }
@@ -1331,6 +1421,33 @@
                 case TelephonyManager.NETWORK_TYPE_UMTS:
                     bin = DATA_CONNECTION_UMTS;
                     break;
+                case TelephonyManager.NETWORK_TYPE_CDMA:
+                    bin = DATA_CONNECTION_CDMA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_0:
+                    bin = DATA_CONNECTION_EVDO_0;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_A:
+                    bin = DATA_CONNECTION_EVDO_A;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_1xRTT:
+                    bin = DATA_CONNECTION_1xRTT;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSDPA:
+                    bin = DATA_CONNECTION_HSDPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSUPA:
+                    bin = DATA_CONNECTION_HSUPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_HSPA:
+                    bin = DATA_CONNECTION_HSPA;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_IDEN:
+                    bin = DATA_CONNECTION_IDEN;
+                    break;
+                case TelephonyManager.NETWORK_TYPE_EVDO_B:
+                    bin = DATA_CONNECTION_EVDO_B;
+                    break;
                 default:
                     bin = DATA_CONNECTION_OTHER;
                     break;
@@ -1338,11 +1455,11 @@
         }
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
-            mHistoryCur.states = (mHistoryCur.states&~BatteryHistoryRecord.STATE_DATA_CONNECTION_MASK)
-                    | (bin << BatteryHistoryRecord.STATE_DATA_CONNECTION_SHIFT);
+            mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
+                    | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this);
             }
@@ -1353,10 +1470,10 @@
     
     public void noteWifiOnLocked(int uid) {
         if (!mWifiOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(this);
         }
@@ -1371,10 +1488,10 @@
     
     public void noteWifiOffLocked(int uid) {
         if (mWifiOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(this);
         }
@@ -1386,10 +1503,10 @@
 
     public void noteAudioOnLocked(int uid) {
         if (!mAudioOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mAudioOn = true;
             mAudioOnTimer.startRunningLocked(this);
         }
@@ -1398,10 +1515,10 @@
     
     public void noteAudioOffLocked(int uid) {
         if (mAudioOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_AUDIO_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mAudioOn = false;
             mAudioOnTimer.stopRunningLocked(this);
         }
@@ -1410,10 +1527,10 @@
 
     public void noteVideoOnLocked(int uid) {
         if (!mVideoOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mVideoOn = true;
             mVideoOnTimer.startRunningLocked(this);
         }
@@ -1422,10 +1539,10 @@
     
     public void noteVideoOffLocked(int uid) {
         if (mVideoOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mVideoOn = false;
             mVideoOnTimer.stopRunningLocked(this);
         }
@@ -1434,10 +1551,10 @@
 
     public void noteWifiRunningLocked() {
         if (!mWifiRunning) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiRunning = true;
             mWifiRunningTimer.startRunningLocked(this);
         }
@@ -1445,10 +1562,10 @@
 
     public void noteWifiStoppedLocked() {
         if (mWifiRunning) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mWifiRunning = false;
             mWifiRunningTimer.stopRunningLocked(this);
         }
@@ -1456,10 +1573,10 @@
 
     public void noteBluetoothOnLocked() {
         if (!mBluetoothOn) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mBluetoothOn = true;
             mBluetoothOnTimer.startRunningLocked(this);
         }
@@ -1467,10 +1584,10 @@
     
     public void noteBluetoothOffLocked() {
         if (mBluetoothOn) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BLUETOOTH_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
             mBluetoothOn = false;
             mBluetoothOnTimer.stopRunningLocked(this);
         }
@@ -1480,10 +1597,10 @@
     
     public void noteFullWifiLockAcquiredLocked(int uid) {
         if (mWifiFullLockNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked();
@@ -1492,10 +1609,10 @@
     public void noteFullWifiLockReleasedLocked(int uid) {
         mWifiFullLockNesting--;
         if (mWifiFullLockNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_FULL_LOCK_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked();
     }
@@ -1504,10 +1621,10 @@
     
     public void noteScanWifiLockAcquiredLocked(int uid) {
         if (mWifiScanLockNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiScanLockNesting++;
         getUidStatsLocked(uid).noteScanWifiLockAcquiredLocked();
@@ -1516,10 +1633,10 @@
     public void noteScanWifiLockReleasedLocked(int uid) {
         mWifiScanLockNesting--;
         if (mWifiScanLockNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_SCAN_LOCK_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteScanWifiLockReleasedLocked();
     }
@@ -1528,10 +1645,10 @@
     
     public void noteWifiMulticastEnabledLocked(int uid) {
         if (mWifiMulticastNesting == 0) {
-            mHistoryCur.states |= BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked();
@@ -1540,10 +1657,10 @@
     public void noteWifiMulticastDisabledLocked(int uid) {
         mWifiMulticastNesting--;
         if (mWifiMulticastNesting == 0) {
-            mHistoryCur.states &= ~BatteryHistoryRecord.STATE_WIFI_MULTICAST_ON_FLAG;
+            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(SystemClock.elapsedRealtime());
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked();
     }
@@ -1712,9 +1829,9 @@
                 return mLoadedTcpBytesReceived;
             } else {
                 long current = computeCurrentTcpBytesReceived();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     current -= mTcpBytesReceivedAtLastUnplug;
-                } else if (which == STATS_TOTAL) {
+                } else if (which == STATS_SINCE_CHARGED) {
                     current += mLoadedTcpBytesReceived;
                 }
                 return current;
@@ -1732,9 +1849,9 @@
                 return mLoadedTcpBytesSent;
             } else {
                 long current = computeCurrentTcpBytesSent();
-                if (which == STATS_UNPLUGGED) {
+                if (which == STATS_SINCE_UNPLUGGED) {
                     current -= mTcpBytesSentAtLastUnplug;
-                } else if (which == STATS_TOTAL) {
+                } else if (which == STATS_SINCE_CHARGED) {
                     current += mLoadedTcpBytesSent;
                 }
                 return current;
@@ -1745,6 +1862,10 @@
         public void noteWifiTurnedOnLocked() {
             if (!mWifiTurnedOn) {
                 mWifiTurnedOn = true;
+                if (mWifiTurnedOnTimer == null) {
+                    mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                            null, mUnpluggables);
+                }
                 mWifiTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1761,43 +1882,15 @@
         public void noteFullWifiLockAcquiredLocked() {
             if (!mFullWifiLockOut) {
                 mFullWifiLockOut = true;
+                if (mFullWifiLockTimer == null) {
+                    mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                            null, mUnpluggables);
+                }
                 mFullWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
         
         @Override
-        public void noteVideoTurnedOnLocked() {
-            if (!mVideoTurnedOn) {
-                mVideoTurnedOn = true;
-                mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteVideoTurnedOffLocked() {
-            if (mVideoTurnedOn) {
-                mVideoTurnedOn = false;
-                mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteAudioTurnedOnLocked() {
-            if (!mAudioTurnedOn) {
-                mAudioTurnedOn = true;
-                mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
-        public void noteAudioTurnedOffLocked() {
-            if (mAudioTurnedOn) {
-                mAudioTurnedOn = false;
-                mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
-            }
-        }
-
-        @Override
         public void noteFullWifiLockReleasedLocked() {
             if (mFullWifiLockOut) {
                 mFullWifiLockOut = false;
@@ -1809,6 +1902,10 @@
         public void noteScanWifiLockAcquiredLocked() {
             if (!mScanWifiLockOut) {
                 mScanWifiLockOut = true;
+                if (mScanWifiLockTimer == null) {
+                    mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                            null, mUnpluggables);
+                }
                 mScanWifiLockTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1825,6 +1922,10 @@
         public void noteWifiMulticastEnabledLocked() {
             if (!mWifiMulticastEnabled) {
                 mWifiMulticastEnabled = true;
+                if (mWifiMulticastTimer == null) {
+                    mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                            null, mUnpluggables);
+                }
                 mWifiMulticastTimer.startRunningLocked(BatteryStatsImpl.this);
             }
         }
@@ -1837,37 +1938,95 @@
             }
         }
 
+        @Override
+        public void noteAudioTurnedOnLocked() {
+            if (!mAudioTurnedOn) {
+                mAudioTurnedOn = true;
+                if (mAudioTurnedOnTimer == null) {
+                    mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                            null, mUnpluggables);
+                }
+                mAudioTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteAudioTurnedOffLocked() {
+            if (mAudioTurnedOn) {
+                mAudioTurnedOn = false;
+                mAudioTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteVideoTurnedOnLocked() {
+            if (!mVideoTurnedOn) {
+                mVideoTurnedOn = true;
+                if (mVideoTurnedOnTimer == null) {
+                    mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                            null, mUnpluggables);
+                }
+                mVideoTurnedOnTimer.startRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
+        @Override
+        public void noteVideoTurnedOffLocked() {
+            if (mVideoTurnedOn) {
+                mVideoTurnedOn = false;
+                mVideoTurnedOnTimer.stopRunningLocked(BatteryStatsImpl.this);
+            }
+        }
+
         @Override 
         public long getWifiTurnedOnTime(long batteryRealtime, int which) {
+            if (mWifiTurnedOnTimer == null) {
+                return 0;
+            }
             return mWifiTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
         @Override 
-        public long getAudioTurnedOnTime(long batteryRealtime, int which) {
-            return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
-        }
-
-        @Override 
-        public long getVideoTurnedOnTime(long batteryRealtime, int which) {
-            return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
-        }
-
-        @Override 
         public long getFullWifiLockTime(long batteryRealtime, int which) {
+            if (mFullWifiLockTimer == null) {
+                return 0;
+            }
             return mFullWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
         }
         
         @Override 
         public long getScanWifiLockTime(long batteryRealtime, int which) {
+            if (mScanWifiLockTimer == null) {
+                return 0;
+            }
             return mScanWifiLockTimer.getTotalTimeLocked(batteryRealtime, which);
         }
 
         @Override
         public long getWifiMulticastTime(long batteryRealtime, int which) {
+            if (mWifiMulticastTimer == null) {
+                return 0;
+            }
             return mWifiMulticastTimer.getTotalTimeLocked(batteryRealtime,
                                                           which);
         }
 
+        @Override 
+        public long getAudioTurnedOnTime(long batteryRealtime, int which) {
+            if (mAudioTurnedOnTimer == null) {
+                return 0;
+            }
+            return mAudioTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+        }
+
+        @Override 
+        public long getVideoTurnedOnTime(long batteryRealtime, int which) {
+            if (mVideoTurnedOnTimer == null) {
+                return 0;
+            }
+            return mVideoTurnedOnTimer.getTotalTimeLocked(batteryRealtime, which);
+        }
+
         @Override
         public void noteUserActivityLocked(int type) {
             if (mUserActivityCounters == null) {
@@ -1903,6 +2062,126 @@
                     ? (TrafficStats.getUidTxBytes(mUid) - mStartedTcpBytesSent) : 0);
         }
 
+        /**
+         * Clear all stats for this uid.  Returns true if the uid is completely
+         * inactive so can be dropped.
+         */
+        boolean reset() {
+            boolean active = false;
+            
+            if (mWifiTurnedOnTimer != null) {
+                active |= !mWifiTurnedOnTimer.reset(false);
+                active |= mWifiTurnedOn;
+            }
+            if (mFullWifiLockTimer != null) {
+                active |= !mFullWifiLockTimer.reset(false);
+                active |= mFullWifiLockOut;
+            }
+            if (mScanWifiLockTimer != null) {
+                active |= !mScanWifiLockTimer.reset(false);
+                active |= mScanWifiLockOut;
+            }
+            if (mWifiMulticastTimer != null) {
+                active |= !mWifiMulticastTimer.reset(false);
+                active |= mWifiMulticastEnabled;
+            }
+            if (mAudioTurnedOnTimer != null) {
+                active |= !mAudioTurnedOnTimer.reset(false);
+                active |= mAudioTurnedOn;
+            }
+            if (mVideoTurnedOnTimer != null) {
+                active |= !mVideoTurnedOnTimer.reset(false);
+                active |= mVideoTurnedOn;
+            }
+            
+            mLoadedTcpBytesReceived = mLoadedTcpBytesSent = 0;
+            mCurrentTcpBytesReceived = mCurrentTcpBytesSent = 0;
+            
+            if (mUserActivityCounters != null) {
+                for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
+                    mUserActivityCounters[i].reset(false);
+                }
+            }
+
+            if (mWakelockStats.size() > 0) {
+                Iterator<Map.Entry<String, Wakelock>> it = mWakelockStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Wakelock> wakelockEntry = it.next();
+                    Wakelock wl = wakelockEntry.getValue();
+                    if (wl.reset()) {
+                        it.remove();
+                    } else {
+                        active = true;
+                    }
+                }
+            }
+            if (mSensorStats.size() > 0) {
+                Iterator<Map.Entry<Integer, Sensor>> it = mSensorStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<Integer, Sensor> sensorEntry = it.next();
+                    Sensor s = sensorEntry.getValue();
+                    if (s.reset()) {
+                        it.remove();
+                    } else {
+                        active = true;
+                    }
+                }
+            }
+            if (mProcessStats.size() > 0) {
+                Iterator<Map.Entry<String, Proc>> it = mProcessStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Proc> procEntry = it.next();
+                    procEntry.getValue().detach();
+                }
+                mProcessStats.clear();
+            }
+            if (mPackageStats.size() > 0) {
+                Iterator<Map.Entry<String, Pkg>> it = mPackageStats.entrySet().iterator();
+                while (it.hasNext()) {
+                    Map.Entry<String, Pkg> pkgEntry = it.next();
+                    Pkg p = pkgEntry.getValue();
+                    p.detach();
+                    if (p.mServiceStats.size() > 0) {
+                        Iterator<Map.Entry<String, Pkg.Serv>> it2
+                                = p.mServiceStats.entrySet().iterator();
+                        while (it2.hasNext()) {
+                            Map.Entry<String, Pkg.Serv> servEntry = it2.next();
+                            servEntry.getValue().detach();
+                        }
+                    }
+                }
+                mPackageStats.clear();
+            }
+            
+            if (!active) {
+                if (mWifiTurnedOnTimer != null) {
+                    mWifiTurnedOnTimer.detach();
+                }
+                if (mFullWifiLockTimer != null) {
+                    mFullWifiLockTimer.detach();
+                }
+                if (mScanWifiLockTimer != null) {
+                    mScanWifiLockTimer.detach();
+                }
+                if (mWifiMulticastTimer != null) {
+                    mWifiMulticastTimer.detach();
+                }
+                if (mAudioTurnedOnTimer != null) {
+                    mAudioTurnedOnTimer.detach();
+                }
+                if (mVideoTurnedOnTimer != null) {
+                    mVideoTurnedOnTimer.detach();
+                }
+                if (mUserActivityCounters != null) {
+                    for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
+                        mUserActivityCounters[i].detach();
+                    }
+                }
+            }
+            
+            return !active;
+        }
+        
         void writeToParcelLocked(Parcel out, long batteryRealtime) {
             out.writeInt(mWakelockStats.size());
             for (Map.Entry<String, Uid.Wakelock> wakelockEntry : mWakelockStats.entrySet()) {
@@ -1938,19 +2217,49 @@
             out.writeLong(computeCurrentTcpBytesSent());
             out.writeLong(mTcpBytesReceivedAtLastUnplug);
             out.writeLong(mTcpBytesSentAtLastUnplug);
-            mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
-            mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime);
-            mScanWifiLockTimer.writeToParcel(out, batteryRealtime);
-            mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
-            if (mUserActivityCounters == null) {
-                out.writeInt(0);
+            if (mWifiTurnedOnTimer != null) {
+                out.writeInt(1);
+                mWifiTurnedOnTimer.writeToParcel(out, batteryRealtime);
             } else {
+                out.writeInt(0);
+            }
+            if (mFullWifiLockTimer != null) {
+                out.writeInt(1);
+                mFullWifiLockTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mScanWifiLockTimer != null) {
+                out.writeInt(1);
+                mScanWifiLockTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mWifiMulticastTimer != null) {
+                out.writeInt(1);
+                mWifiMulticastTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mAudioTurnedOnTimer != null) {
+                out.writeInt(1);
+                mAudioTurnedOnTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mVideoTurnedOnTimer != null) {
+                out.writeInt(1);
+                mVideoTurnedOnTimer.writeToParcel(out, batteryRealtime);
+            } else {
+                out.writeInt(0);
+            }
+            if (mUserActivityCounters != null) {
                 out.writeInt(1);
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i].writeToParcel(out);
                 }
+            } else {
+                out.writeInt(0);
             }
         }
 
@@ -2003,25 +2312,54 @@
             mTcpBytesReceivedAtLastUnplug = in.readLong();
             mTcpBytesSentAtLastUnplug = in.readLong();
             mWifiTurnedOn = false;
-            mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON, null, mUnpluggables, in);
-            mFullWifiLockOut = false;
-            mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK, null, mUnpluggables, in);
-            mAudioTurnedOn = false;
-            mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON, null, mUnpluggables, in);
-            mVideoTurnedOn = false;
-            mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON, null, mUnpluggables, in);
-            mScanWifiLockOut = false;
-            mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK, null, mUnpluggables, in);
-            mWifiMulticastEnabled = false;
-            mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
-                    null, mUnpluggables, in);
-            if (in.readInt() == 0) {
-                mUserActivityCounters = null;
+            if (in.readInt() != 0) {
+                mWifiTurnedOnTimer = new StopwatchTimer(WIFI_TURNED_ON,
+                        null, mUnpluggables, in);
             } else {
+                mWifiTurnedOnTimer = null;
+            }
+            mFullWifiLockOut = false;
+            if (in.readInt() != 0) {
+                mFullWifiLockTimer = new StopwatchTimer(FULL_WIFI_LOCK,
+                        null, mUnpluggables, in);
+            } else {
+                mFullWifiLockTimer = null;
+            }
+            mScanWifiLockOut = false;
+            if (in.readInt() != 0) {
+                mScanWifiLockTimer = new StopwatchTimer(SCAN_WIFI_LOCK,
+                        null, mUnpluggables, in);
+            } else {
+                mScanWifiLockTimer = null;
+            }
+            mWifiMulticastEnabled = false;
+            if (in.readInt() != 0) {
+                mWifiMulticastTimer = new StopwatchTimer(WIFI_MULTICAST_ENABLED,
+                        null, mUnpluggables, in);
+            } else {
+                mWifiMulticastTimer = null;
+            }
+            mAudioTurnedOn = false;
+            if (in.readInt() != 0) {
+                mAudioTurnedOnTimer = new StopwatchTimer(AUDIO_TURNED_ON,
+                        null, mUnpluggables, in);
+            } else {
+                mAudioTurnedOnTimer = null;
+            }
+            mVideoTurnedOn = false;
+            if (in.readInt() != 0) {
+                mVideoTurnedOnTimer = new StopwatchTimer(VIDEO_TURNED_ON,
+                        null, mUnpluggables, in);
+            } else {
+                mVideoTurnedOnTimer = null;
+            }
+            if (in.readInt() != 0) {
                 mUserActivityCounters = new Counter[NUM_USER_ACTIVITY_TYPES];
                 for (int i=0; i<NUM_USER_ACTIVITY_TYPES; i++) {
                     mUserActivityCounters[i] = new Counter(mUnpluggables, in);
                 }
+            } else {
+                mUserActivityCounters = null;
             }
         }
 
@@ -2060,6 +2398,34 @@
                 return new StopwatchTimer(type, pool, unpluggables, in);
             }
 
+            boolean reset() {
+                boolean wlactive = false;
+                if (mTimerFull != null) {
+                    wlactive |= !mTimerFull.reset(false);
+                }
+                if (mTimerPartial != null) {
+                    wlactive |= !mTimerPartial.reset(false);
+                }
+                if (mTimerWindow != null) {
+                    wlactive |= !mTimerWindow.reset(false);
+                }
+                if (!wlactive) {
+                    if (mTimerFull != null) {
+                        mTimerFull.detach();
+                        mTimerFull = null;
+                    }
+                    if (mTimerPartial != null) {
+                        mTimerPartial.detach();
+                        mTimerPartial = null;
+                    }
+                    if (mTimerWindow != null) {
+                        mTimerWindow.detach();
+                        mTimerWindow = null;
+                    }
+                }
+                return !wlactive;
+            }
+            
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimerPartial = readTimerFromParcel(WAKE_TYPE_PARTIAL,
                         mPartialTimers, unpluggables, in);
@@ -2108,6 +2474,14 @@
                 return new StopwatchTimer(0, pool, unpluggables, in);
             }
 
+            boolean reset() {
+                if (mTimer.reset(true)) {
+                    mTimer = null;
+                    return true;
+                }
+                return false;
+            }
+            
             void readFromParcelLocked(ArrayList<Unpluggable> unpluggables, Parcel in) {
                 mTimer = readTimerFromParcel(unpluggables, in);
             }
@@ -2216,9 +2590,6 @@
             Proc() {
                 mUnpluggables.add(this);
                 mSpeedBins = new SamplingCounter[getCpuSpeedSteps()];
-                for (int i = 0; i < mSpeedBins.length; i++) {
-                    mSpeedBins[i] = new SamplingCounter(mUnpluggables);
-                }
             }
 
             public void unplug(long batteryUptime, long batteryRealtime) {
@@ -2231,10 +2602,18 @@
             public void plug(long batteryUptime, long batteryRealtime) {
             }
             
+            void detach() {
+                mUnpluggables.remove(this);
+                for (int i = 0; i < mSpeedBins.length; i++) {
+                    SamplingCounter c = mSpeedBins[i];
+                    if (c != null) {
+                        mUnpluggables.remove(c);
+                        mSpeedBins[i] = null;
+                    }
+                }
+            }
+            
             void writeToParcelLocked(Parcel out) {
-                final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
-                final long batteryRealtime = getBatteryRealtimeLocked(uSecRealtime);
-                
                 out.writeLong(mUserTime);
                 out.writeLong(mSystemTime);
                 out.writeLong(mForegroundTime);
@@ -2254,7 +2633,13 @@
 
                 out.writeInt(mSpeedBins.length);
                 for (int i = 0; i < mSpeedBins.length; i++) {
-                    mSpeedBins[i].writeToParcel(out);
+                    SamplingCounter c = mSpeedBins[i];
+                    if (c != null) {
+                        out.writeInt(1);
+                        c.writeToParcel(out);
+                    } else {
+                        out.writeInt(0);
+                    }
                 }
             }
 
@@ -2277,9 +2662,12 @@
                 mUnpluggedStarts = in.readInt();
 
                 int bins = in.readInt();
-                mSpeedBins = new SamplingCounter[bins];
+                int steps = getCpuSpeedSteps();
+                mSpeedBins = new SamplingCounter[bins >= steps ? bins : steps];
                 for (int i = 0; i < bins; i++) {
-                    mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
+                    if (in.readInt() != 0) {
+                        mSpeedBins[i] = new SamplingCounter(mUnpluggables, in);
+                    }
                 }
             }
 
@@ -2309,7 +2697,7 @@
                     val = mUserTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedUserTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedUserTime;
                     }
                 }
@@ -2325,7 +2713,7 @@
                     val = mSystemTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedSystemTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedSystemTime;
                     }
                 }
@@ -2341,7 +2729,7 @@
                     val = mForegroundTime;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedForegroundTime;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedForegroundTime;
                     }
                 }
@@ -2357,7 +2745,7 @@
                     val = mStarts;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedStarts;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedStarts;
                     }
                 }
@@ -2367,14 +2755,22 @@
             /* Called by ActivityManagerService when CPU times are updated. */
             public void addSpeedStepTimes(long[] values) {
                 for (int i = 0; i < mSpeedBins.length && i < values.length; i++) {
-                    mSpeedBins[i].addCountAtomic(values[i]);
+                    long amt = values[i];
+                    if (amt != 0) {
+                        SamplingCounter c = mSpeedBins[i];
+                        if (c == null) {
+                            mSpeedBins[i] = c = new SamplingCounter(mUnpluggables);
+                        }
+                        c.addCountAtomic(values[i]);
+                    }
                 }
             }
 
             @Override
             public long getTimeAtCpuSpeedStep(int speedStep, int which) {
                 if (speedStep < mSpeedBins.length) {
-                    return mSpeedBins[speedStep].getCountLocked(which);
+                    SamplingCounter c = mSpeedBins[speedStep];
+                    return c != null ? c.getCountLocked(which) : 0;
                 } else {
                     return 0;
                 }
@@ -2425,6 +2821,10 @@
             public void plug(long batteryUptime, long batteryRealtime) {
             }
             
+            void detach() {
+                mUnpluggables.remove(this);
+            }
+            
             void readFromParcelLocked(Parcel in) {
                 mWakeups = in.readInt();
                 mLoadedWakeups = in.readInt();
@@ -2471,7 +2871,7 @@
                     val = mWakeups;
                     if (which == STATS_CURRENT) {
                         val -= mLoadedWakeups;
-                    } else if (which == STATS_UNPLUGGED) {
+                    } else if (which == STATS_SINCE_UNPLUGGED) {
                         val -= mUnpluggedWakeups;
                     }
                 }
@@ -2586,6 +2986,10 @@
                 public void plug(long batteryUptime, long batteryRealtime) {
                 }
                 
+                void detach() {
+                    mUnpluggables.remove(this);
+                }
+                
                 void readFromParcelLocked(Parcel in) {
                     mStartTime = in.readLong();
                     mRunningSince = in.readLong();
@@ -2690,7 +3094,7 @@
                         val = mLaunches;
                         if (which == STATS_CURRENT) {
                             val -= mLoadedLaunches;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedLaunches;
                         }
                     }
@@ -2707,7 +3111,7 @@
                         val = getStartTimeToNowLocked(now);
                         if (which == STATS_CURRENT) {
                             val -= mLoadedStartTime;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedStartTime;
                         }
                     }
@@ -2724,7 +3128,7 @@
                         val = mStarts;
                         if (which == STATS_CURRENT) {
                             val -= mLoadedStarts;
-                        } else if (which == STATS_UNPLUGGED) {
+                        } else if (which == STATS_SINCE_UNPLUGGED) {
                             val -= mUnpluggedStarts;
                         }
                     }
@@ -2920,7 +3324,9 @@
         mWifiRunningTimer = new StopwatchTimer(-4, null, mUnpluggables);
         mBluetoothOnTimer = new StopwatchTimer(-5, null, mUnpluggables);
         mAudioOnTimer = new StopwatchTimer(-6, null, mUnpluggables);
+        mVideoOnTimer = new StopwatchTimer(-7, null, mUnpluggables);
         mOnBattery = mOnBatteryInternal = false;
+        initTimes();
         mTrackBatteryPastUptime = 0;
         mTrackBatteryPastRealtime = 0;
         mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
@@ -2928,6 +3334,7 @@
         mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
         mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
         mDischargeStartLevel = 0;
+        mDischargeUnplugLevel = 0;
         mDischargeCurrentLevel = 0;
     }
 
@@ -2947,7 +3354,7 @@
     }
 
     @Override
-    public BatteryHistoryRecord getHistory() {
+    public HistoryItem getHistory() {
         return mHistory;
     }
     
@@ -2960,53 +3367,174 @@
         return mOnBattery;
     }
 
-    public void setOnBattery(boolean onBattery, int level) {
+    void initTimes() {
+        mBatteryRealtime = mTrackBatteryPastUptime = 0;
+        mBatteryUptime = mTrackBatteryPastRealtime = 0;
+        mUptimeStart = mTrackBatteryUptimeStart = SystemClock.uptimeMillis() * 1000;
+        mRealtimeStart = mTrackBatteryRealtimeStart = SystemClock.elapsedRealtime() * 1000;
+        mUnpluggedBatteryUptime = getBatteryUptimeLocked(mUptimeStart);
+        mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(mRealtimeStart);
+    }
+    
+    public void resetAllStatsLocked() {
+        mStartCount = 0;
+        initTimes();
+        mScreenOnTimer.reset(false);
+        for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) {
+            mScreenBrightnessTimer[i].reset(false);
+        }
+        mInputEventCounter.reset(false);
+        mPhoneOnTimer.reset(false);
+        mAudioOnTimer.reset(false);
+        mVideoOnTimer.reset(false);
+        for (int i=0; i<NUM_SIGNAL_STRENGTH_BINS; i++) {
+            mPhoneSignalStrengthsTimer[i].reset(false);
+        }
+        mPhoneSignalScanningTimer.reset(false);
+        for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
+            mPhoneDataConnectionsTimer[i].reset(false);
+        }
+        mWifiOnTimer.reset(false);
+        mWifiRunningTimer.reset(false);
+        mBluetoothOnTimer.reset(false);
+        
+        for (int i=0; i<mUidStats.size(); i++) {
+            if (mUidStats.valueAt(i).reset()) {
+                mUidStats.remove(mUidStats.keyAt(i));
+                i--;
+            }
+        }
+        
+        if (mKernelWakelockStats.size() > 0) {
+            for (SamplingTimer timer : mKernelWakelockStats.values()) {
+                mUnpluggables.remove(timer);
+            }
+            mKernelWakelockStats.clear();
+        }
+        
+        clearHistoryLocked();
+    }
+    
+    void setOnBattery(boolean onBattery, int oldStatus, int level) {
         synchronized(this) {
-            updateKernelWakelocksLocked();
-            if (mOnBattery != onBattery) {
-                mOnBattery = mOnBatteryInternal = onBattery;
-                
-                long uptime = SystemClock.uptimeMillis() * 1000;
-                long mSecRealtime = SystemClock.elapsedRealtime();
-                long realtime = mSecRealtime * 1000;
-                if (onBattery) {
-                    clearHistory();
-                    mHistoryCur.batteryLevel = (byte)level;
-                    mHistoryCur.states &= ~BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
-                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
-                            + Integer.toHexString(mHistoryCur.states));
-                    addHistoryRecord(mSecRealtime);
-                    mTrackBatteryUptimeStart = uptime;
-                    mTrackBatteryRealtimeStart = realtime;
-                    mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
-                    mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
-                    mDischargeCurrentLevel = mDischargeStartLevel = level;
-                    doUnplug(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
-                } else {
-                    mHistoryCur.batteryLevel = (byte)level;
-                    mHistoryCur.states |= BatteryHistoryRecord.STATE_BATTERY_PLUGGED_FLAG;
-                    if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
-                            + Integer.toHexString(mHistoryCur.states));
-                    addHistoryRecord(mSecRealtime);
-                    mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
-                    mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
-                    mDischargeCurrentLevel = level;
-                    doPlug(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+            boolean doWrite = false;
+            mOnBattery = mOnBatteryInternal = onBattery;
+            
+            long uptime = SystemClock.uptimeMillis() * 1000;
+            long mSecRealtime = SystemClock.elapsedRealtime();
+            long realtime = mSecRealtime * 1000;
+            if (onBattery) {
+                // We will reset our status if we are unplugging after the
+                // battery was last full, or the level is at 100, or
+                // we have gone through a significant charge (from a very low
+                // level to a now very high level).
+                if (oldStatus == BatteryManager.BATTERY_STATUS_FULL
+                        || level >= 100
+                        || (mDischargeCurrentLevel < 20 && level > 90)) {
+                    doWrite = true;
+                    resetAllStatsLocked();
+                    mDischargeStartLevel = level;
                 }
-                if ((mLastWriteTime + (60 * 1000)) < mSecRealtime) {
-                    if (mFile != null) {
-                        writeLocked();
-                    }
+                updateKernelWakelocksLocked();
+                mHistoryCur.batteryLevel = (byte)level;
+                mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(mSecRealtime);
+                mTrackBatteryUptimeStart = uptime;
+                mTrackBatteryRealtimeStart = realtime;
+                mUnpluggedBatteryUptime = getBatteryUptimeLocked(uptime);
+                mUnpluggedBatteryRealtime = getBatteryRealtimeLocked(realtime);
+                mDischargeCurrentLevel = mDischargeUnplugLevel = level;
+                doUnplugLocked(mUnpluggedBatteryUptime, mUnpluggedBatteryRealtime);
+            } else {
+                updateKernelWakelocksLocked();
+                mHistoryCur.batteryLevel = (byte)level;
+                mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
+                        + Integer.toHexString(mHistoryCur.states));
+                addHistoryRecordLocked(mSecRealtime);
+                mTrackBatteryPastUptime += uptime - mTrackBatteryUptimeStart;
+                mTrackBatteryPastRealtime += realtime - mTrackBatteryRealtimeStart;
+                mDischargeCurrentLevel = level;
+                doPlugLocked(getBatteryUptimeLocked(uptime), getBatteryRealtimeLocked(realtime));
+            }
+            if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) {
+                if (mFile != null) {
+                    writeLocked();
                 }
             }
         }
     }
     
-    public void recordCurrentLevel(int level) {
-        if (mDischargeCurrentLevel != level) {
+    // This should probably be exposed in the API, though it's not critical
+    private static final int BATTERY_PLUGGED_NONE = 0;
+    
+    public void setBatteryState(int status, int health, int plugType, int level,
+            int temp, int volt) {
+        boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+        int oldStatus = mHistoryCur.batteryStatus;
+        if (!mHaveBatteryLevel) {
+            mHaveBatteryLevel = true;
+            // We start out assuming that the device is plugged in (not
+            // on battery).  If our first report is now that we are indeed
+            // plugged in, then twiddle our state to correctly reflect that
+            // since we won't be going through the full setOnBattery().
+            if (onBattery == mOnBattery) {
+                if (onBattery) {
+                    mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                } else {
+                    mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
+                }
+            }
+            oldStatus = status;
+        }
+        if (onBattery) {
             mDischargeCurrentLevel = level;
+            mRecordingHistory = true;
+        }
+        if (onBattery != mOnBattery) {
             mHistoryCur.batteryLevel = (byte)level;
-            addHistoryRecord(SystemClock.elapsedRealtime());
+            mHistoryCur.batteryStatus = (byte)status;
+            mHistoryCur.batteryHealth = (byte)health;
+            mHistoryCur.batteryPlugType = (byte)plugType;
+            mHistoryCur.batteryTemperature = (char)temp;
+            mHistoryCur.batteryVoltage = (char)volt;
+            setOnBattery(onBattery, oldStatus, level);
+        } else {
+            boolean changed = false;
+            if (mHistoryCur.batteryLevel != level) {
+                mHistoryCur.batteryLevel = (byte)level;
+                changed = true;
+            }
+            if (mHistoryCur.batteryStatus != status) {
+                mHistoryCur.batteryStatus = (byte)status;
+                changed = true;
+            }
+            if (mHistoryCur.batteryHealth != health) {
+                mHistoryCur.batteryHealth = (byte)health;
+                changed = true;
+            }
+            if (mHistoryCur.batteryPlugType != plugType) {
+                mHistoryCur.batteryPlugType = (byte)plugType;
+                changed = true;
+            }
+            if (mHistoryCur.batteryTemperature != temp) {
+                mHistoryCur.batteryTemperature = (char)temp;
+                changed = true;
+            }
+            if (mHistoryCur.batteryVoltage != volt) {
+                mHistoryCur.batteryVoltage = (char)volt;
+                changed = true;
+            }
+            if (changed) {
+                addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            }
+        }
+        if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
+            // We don't record history while we are plugged in and fully charged.
+            // The next time we are unplugged, history will be cleared.
+            mRecordingHistory = false;
         }
     }
     
@@ -3056,10 +3584,10 @@
     @Override
     public long computeUptime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL: return mUptime + (curTime-mUptimeStart);
+            case STATS_SINCE_CHARGED: return mUptime + (curTime-mUptimeStart);
             case STATS_LAST: return mLastUptime;
             case STATS_CURRENT: return (curTime-mUptimeStart);
-            case STATS_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
+            case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryUptimeStart);
         }
         return 0;
     }
@@ -3067,10 +3595,10 @@
     @Override
     public long computeRealtime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL: return mRealtime + (curTime-mRealtimeStart);
+            case STATS_SINCE_CHARGED: return mRealtime + (curTime-mRealtimeStart);
             case STATS_LAST: return mLastRealtime;
             case STATS_CURRENT: return (curTime-mRealtimeStart);
-            case STATS_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
+            case STATS_SINCE_UNPLUGGED: return (curTime-mTrackBatteryRealtimeStart);
         }
         return 0;
     }
@@ -3078,13 +3606,13 @@
     @Override
     public long computeBatteryUptime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL:
+            case STATS_SINCE_CHARGED:
                 return mBatteryUptime + getBatteryUptime(curTime);
             case STATS_LAST:
                 return mBatteryLastUptime;
             case STATS_CURRENT:
                 return getBatteryUptime(curTime);
-            case STATS_UNPLUGGED:
+            case STATS_SINCE_UNPLUGGED:
                 return getBatteryUptimeLocked(curTime) - mUnpluggedBatteryUptime;
         }
         return 0;
@@ -3093,13 +3621,13 @@
     @Override
     public long computeBatteryRealtime(long curTime, int which) {
         switch (which) {
-            case STATS_TOTAL:
+            case STATS_SINCE_CHARGED:
                 return mBatteryRealtime + getBatteryRealtimeLocked(curTime);
             case STATS_LAST:
                 return mBatteryLastRealtime;
             case STATS_CURRENT:
                 return getBatteryRealtimeLocked(curTime);
-            case STATS_UNPLUGGED:
+            case STATS_SINCE_UNPLUGGED:
                 return getBatteryRealtimeLocked(curTime) - mUnpluggedBatteryRealtime;
         }
         return 0;
@@ -3139,14 +3667,14 @@
         if (which == STATS_LAST) {
             return dataBytes[STATS_LAST];
         } else {
-            if (which == STATS_UNPLUGGED) {
-                if (dataBytes[STATS_UNPLUGGED] < 0) {
+            if (which == STATS_SINCE_UNPLUGGED) {
+                if (dataBytes[STATS_SINCE_UNPLUGGED] < 0) {
                     return dataBytes[STATS_LAST];
                 } else {
-                    return current - dataBytes[STATS_UNPLUGGED];
+                    return current - dataBytes[STATS_SINCE_UNPLUGGED];
                 }
-            } else if (which == STATS_TOTAL) {
-                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL];
+            } else if (which == STATS_SINCE_CHARGED) {
+                return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_SINCE_CHARGED];
             }
             return current - dataBytes[STATS_CURRENT];
         }
@@ -3180,7 +3708,7 @@
     }
     
     public int getDischargeStartLevelLocked() {
-            return mDischargeStartLevel;
+            return mDischargeUnplugLevel;
     }
     
     @Override
@@ -3263,17 +3791,21 @@
         return u.getServiceStatsLocked(pkg, name);
     }
 
-    private static JournaledFile makeJournaledFile() {
-        final String base = "/data/system/device_policies.xml";
-        return new JournaledFile(new File(base), new File(base + ".tmp"));
+    public void shutdownLocked() {
+        writeLocked();
+        mShuttingDown = true;
     }
-
+    
     public void writeLocked() {
         if (mFile == null) {
             Slog.w("BatteryStats", "writeLocked: no file associated with this instance");
             return;
         }
 
+        if (mShuttingDown) {
+            return;
+        }
+        
         try {
             FileOutputStream stream = new FileOutputStream(mFile.chooseForWrite());
             Parcel out = Parcel.obtain();
@@ -3341,6 +3873,8 @@
         } catch(java.io.IOException e) {
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
+        
+        addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
     }
 
     public int describeContents() {
@@ -3349,15 +3883,19 @@
 
     void readHistory(Parcel in) {
         mHistory = mHistoryEnd = mHistoryCache = null;
+        mHistoryBaseTime = 0;
         long time;
         while ((time=in.readLong()) >= 0) {
-            BatteryHistoryRecord rec = new BatteryHistoryRecord(time, in);
-            addHistoryRecord(rec);
+            HistoryItem rec = new HistoryItem(time, in);
+            addHistoryRecordLocked(rec);
+            if (rec.time > mHistoryBaseTime) {
+                mHistoryBaseTime = rec.time;
+            }
         }
     }
     
     void writeHistory(Parcel out) {
-        BatteryHistoryRecord rec = mHistory;
+        HistoryItem rec = mHistory;
         while (rec != null) {
             if (rec.time >= 0) rec.writeToParcel(out, 0);
             rec = rec.next;
@@ -3384,7 +3922,7 @@
         mLastUptime = in.readLong();
         mRealtime = in.readLong();
         mLastRealtime = in.readLong();
-        mDischargeStartLevel = in.readInt();
+        mDischargeUnplugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
         
         mStartCount++;
@@ -3436,17 +3974,29 @@
             mUidStats.put(uid, u);
 
             u.mWifiTurnedOn = false;
-            u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mWifiTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
             u.mFullWifiLockOut = false;
-            u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
-            u.mAudioTurnedOn = false;
-            u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
-            u.mVideoTurnedOn = false;
-            u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mFullWifiLockTimer.readSummaryFromParcelLocked(in);
+            }
             u.mScanWifiLockOut = false;
-            u.mScanWifiLockTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mScanWifiLockTimer.readSummaryFromParcelLocked(in);
+            }
             u.mWifiMulticastEnabled = false;
-            u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
+            if (in.readInt() != 0) {
+                u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
+            }
+            u.mAudioTurnedOn = false;
+            if (in.readInt() != 0) {
+                u.mAudioTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
+            u.mVideoTurnedOn = false;
+            if (in.readInt() != 0) {
+                u.mVideoTurnedOnTimer.readSummaryFromParcelLocked(in);
+            }
 
             if (in.readInt() != 0) {
                 if (u.mUserActivityCounters == null) {
@@ -3549,15 +4099,15 @@
         writeHistory(out);
         
         out.writeInt(mStartCount);
-        out.writeLong(computeBatteryUptime(NOW_SYS, STATS_TOTAL));
+        out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_CURRENT));
-        out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_TOTAL));
+        out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeBatteryRealtime(NOWREAL_SYS, STATS_CURRENT));
-        out.writeLong(computeUptime(NOW_SYS, STATS_TOTAL));
+        out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeUptime(NOW_SYS, STATS_CURRENT));
-        out.writeLong(computeRealtime(NOWREAL_SYS, STATS_TOTAL));
+        out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED));
         out.writeLong(computeRealtime(NOWREAL_SYS, STATS_CURRENT));
-        out.writeInt(mDischargeStartLevel);
+        out.writeInt(mDischargeUnplugLevel);
         out.writeInt(mDischargeCurrentLevel);
         
         
@@ -3597,12 +4147,42 @@
             out.writeInt(mUidStats.keyAt(iu));
             Uid u = mUidStats.valueAt(iu);
             
-            u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
-            u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            if (u.mWifiTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mWifiTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mFullWifiLockTimer != null) {
+                out.writeInt(1);
+                u.mFullWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mScanWifiLockTimer != null) {
+                out.writeInt(1);
+                u.mScanWifiLockTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mWifiMulticastTimer != null) {
+                out.writeInt(1);
+                u.mWifiMulticastTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mAudioTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mAudioTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
+            if (u.mVideoTurnedOnTimer != null) {
+                out.writeInt(1);
+                u.mVideoTurnedOnTimer.writeSummaryFromParcelLocked(out, NOWREAL);
+            } else {
+                out.writeInt(0);
+            }
 
             if (u.mUserActivityCounters == null) {
                 out.writeInt(0);
@@ -3701,8 +4281,8 @@
                 }
             }
             
-            out.writeLong(u.getTcpBytesReceived(STATS_TOTAL));
-            out.writeLong(u.getTcpBytesSent(STATS_TOTAL));
+            out.writeLong(u.getTcpBytesReceived(STATS_SINCE_CHARGED));
+            out.writeLong(u.getTcpBytesSent(STATS_SINCE_CHARGED));
         }
     }
 
@@ -3758,18 +4338,18 @@
         mTrackBatteryRealtimeStart = in.readLong();
         mUnpluggedBatteryUptime = in.readLong();
         mUnpluggedBatteryRealtime = in.readLong();
-        mDischargeStartLevel = in.readInt();
+        mDischargeUnplugLevel = in.readInt();
         mDischargeCurrentLevel = in.readInt();
         mLastWriteTime = in.readLong();
 
         mMobileDataRx[STATS_LAST] = in.readLong();
-        mMobileDataRx[STATS_UNPLUGGED] = -1;
+        mMobileDataRx[STATS_SINCE_UNPLUGGED] = -1;
         mMobileDataTx[STATS_LAST] = in.readLong();
-        mMobileDataTx[STATS_UNPLUGGED] = -1;
+        mMobileDataTx[STATS_SINCE_UNPLUGGED] = -1;
         mTotalDataRx[STATS_LAST] = in.readLong();
-        mTotalDataRx[STATS_UNPLUGGED] = -1;
+        mTotalDataRx[STATS_SINCE_UNPLUGGED] = -1;
         mTotalDataTx[STATS_LAST] = in.readLong();
-        mTotalDataTx[STATS_UNPLUGGED] = -1;
+        mTotalDataTx[STATS_SINCE_UNPLUGGED] = -1;
 
         mRadioDataUptime = in.readLong();
         mRadioDataStart = -1;
@@ -3853,14 +4433,14 @@
         out.writeLong(mTrackBatteryRealtimeStart);
         out.writeLong(mUnpluggedBatteryUptime);
         out.writeLong(mUnpluggedBatteryRealtime);
-        out.writeInt(mDischargeStartLevel);
+        out.writeInt(mDischargeUnplugLevel);
         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));
+        out.writeLong(getMobileTcpBytesReceived(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getMobileTcpBytesSent(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesReceived(STATS_SINCE_UNPLUGGED));
+        out.writeLong(getTotalTcpBytesSent(STATS_SINCE_UNPLUGGED));
 
         // Write radio uptime for data
         out.writeLong(getRadioDataUptime());
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index 5cf61bd..e6c32d9 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -210,9 +210,6 @@
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        shutdownIfNoPower();
-        shutdownIfOverTemp();
-
         mBatteryLevelCritical = mBatteryLevel <= CRITICAL_BATTERY_LEVEL;
         if (mAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
@@ -221,6 +218,19 @@
         } else {
             mPlugType = BATTERY_PLUGGED_NONE;
         }
+        
+        // Let the battery stats keep track of the current level.
+        try {
+            mBatteryStats.setBatteryState(mBatteryStatus, mBatteryHealth,
+                    mPlugType, mBatteryLevel, mBatteryTemperature,
+                    mBatteryVoltage);
+        } catch (RemoteException e) {
+            // Should never happen.
+        }
+        
+        shutdownIfNoPower();
+        shutdownIfOverTemp();
+
         if (mBatteryStatus != mLastBatteryStatus ||
                 mBatteryHealth != mLastBatteryHealth ||
                 mBatteryPresent != mLastBatteryPresent ||
@@ -263,16 +273,6 @@
                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
             }
-            if (mBatteryLevel != mLastBatteryLevel && mPlugType == BATTERY_PLUGGED_NONE) {
-                // If the battery level has changed and we are on battery, update the current level.
-                // This is used for discharge cycle tracking so this shouldn't be updated while the
-                // battery is charging.
-                try {
-                    mBatteryStats.recordCurrentLevel(mBatteryLevel);
-                } catch (RemoteException e) {
-                    // Should never happen.
-                }
-            }
             if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
                     mPlugType == BATTERY_PLUGGED_NONE) {
                 // We want to make sure we log discharge cycle outliers
@@ -342,11 +342,6 @@
         Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
-        try {
-            mBatteryStats.setOnBattery(mPlugType == BATTERY_PLUGGED_NONE, mBatteryLevel);
-        } catch (RemoteException e) {
-            // Should never happen.
-        }
 
         int icon = getIcon(mBatteryLevel);
 
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index ba13c51..37da6f7 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -59,7 +59,7 @@
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
         synchronized (mStats) {
-            mStats.writeLocked();
+            mStats.shutdownLocked();
         }
     }
     
@@ -96,7 +96,6 @@
     public void noteStartWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            Slog.i("battery", "Start wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStartWakeLocked(name, type);
         }
     }
@@ -104,7 +103,6 @@
     public void noteStopWakelock(int uid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            Slog.i("battery", "Stop wake lock: " + uid + " " + name);
             mStats.getUidStatsLocked(uid).noteStopWakeLocked(name, type);
         }
     }
@@ -126,14 +124,14 @@
     public void noteStartGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStartGps(uid);
+            mStats.noteStartGpsLocked(uid);
         }
     }
     
     public void noteStopGps(int uid) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStopGps(uid);
+            mStats.noteStopGpsLocked(uid);
         }
     }
         
@@ -323,14 +321,10 @@
         return mStats.isOnBattery();
     }
     
-    public void setOnBattery(boolean onBattery, int level) {
+    public void setBatteryState(int status, int health, int plugType, int level,
+            int temp, int volt) {
         enforceCallingPermission();
-        mStats.setOnBattery(onBattery, level);
-    }
-    
-    public void recordCurrentLevel(int level) {
-        enforceCallingPermission();
-        mStats.recordCurrentLevel(level);
+        mStats.setBatteryState(status, health, plugType, level, temp, volt);
     }
     
     public long getAwakeTimeBattery() {
@@ -361,7 +355,8 @@
                 for (String arg : args) {
                     if ("--checkin".equals(arg)) {
                         isCheckin = true;
-                        break;
+                    } else if ("--reset".equals(arg)) {
+                        mStats.resetAllStatsLocked();
                     }
                 }
             }