Rewrite battery history storage.

We now write battery history directly into a buffer, instead of
creating objects.  This allows for more efficient storage; later
it can be even better because we can only write deltas.

The old code is still there temporarily for validation.

Change-Id: I9707d4d8ff30855be8ebdc93bc078911040d8e0b
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 90e2e79..d7483ba 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -412,6 +412,7 @@
         
         public long time;
         
+        public static final byte CMD_NULL = -1;
         public static final byte CMD_UPDATE = 0;
         public static final byte CMD_START = 1;
         public static final byte CMD_OVERFLOW = 2;
@@ -466,16 +467,7 @@
         
         public HistoryItem(long time, Parcel src) {
             this.time = time;
-            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();
+            readFromParcel(src);
         }
         
         public int describeContents() {
@@ -496,6 +488,28 @@
             dest.writeInt(states);
         }
         
+        public void writeDelta(Parcel dest, HistoryItem last) {
+            writeToParcel(dest, 0);
+        }
+
+        private void readFromParcel(Parcel src) {
+            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();
+        }
+
+        public void readDelta(Parcel src, HistoryItem last) {
+            time = src.readLong();
+            readFromParcel(src);
+        }
+
         public void setTo(HistoryItem o) {
             time = o.time;
             cmd = o.cmd;
@@ -556,11 +570,14 @@
 
     public abstract boolean getNextHistoryLocked(HistoryItem out);
 
-    /**
-     * Return the current history of battery state changes.
-     */
-    public abstract HistoryItem getHistory();
-    
+    public abstract void finishIteratingHistoryLocked();
+
+    public abstract boolean startIteratingOldHistoryLocked();
+
+    public abstract boolean getNextOldHistoryLocked(HistoryItem out);
+
+    public abstract void finishIteratingOldHistoryLocked();
+
     /**
      * Return the base time offset for the battery history.
      */
@@ -1729,7 +1746,7 @@
         }
     }
 
-    void printBitDescriptions(PrintWriter pw, int oldval, int newval, BitDescription[] descriptions) {
+    static 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++) {
@@ -1753,6 +1770,125 @@
         }
     }
     
+    public void prepareForDumpLocked() {
+    }
+
+    public static class HistoryPrinter {
+        int oldState = 0;
+        int oldStatus = -1;
+        int oldHealth = -1;
+        int oldPlug = -1;
+        int oldTemp = -1;
+        int oldVolt = -1;
+
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long now) {
+            pw.print("  ");
+            TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+            pw.print(" ");
+            if (rec.cmd == HistoryItem.CMD_START) {
+                pw.println(" START");
+            } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
+                pw.println(" *OVERFLOW*");
+            } 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;
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -1760,122 +1896,28 @@
      */
     @SuppressWarnings("unused")
     public void dumpLocked(PrintWriter pw) {
+        prepareForDumpLocked();
+
+        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
         final HistoryItem rec = new HistoryItem();
         if (startIteratingHistoryLocked()) {
             pw.println("Battery History:");
-            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-            int oldState = 0;
-            int oldStatus = -1;
-            int oldHealth = -1;
-            int oldPlug = -1;
-            int oldTemp = -1;
-            int oldVolt = -1;
+            HistoryPrinter hprinter = new HistoryPrinter();
             while (getNextHistoryLocked(rec)) {
-                pw.print("  ");
-                TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                pw.print(" ");
-                if (rec.cmd == HistoryItem.CMD_START) {
-                    pw.println(" START");
-                } else if (rec.cmd == HistoryItem.CMD_OVERFLOW) {
-                    pw.println(" *OVERFLOW*");
-                } 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;
+                hprinter.printNextItem(pw, rec, now);
             }
+            finishIteratingHistoryLocked();
+            pw.println("");
+        }
+
+        if (startIteratingOldHistoryLocked()) {
+            pw.println("Old battery History:");
+            HistoryPrinter hprinter = new HistoryPrinter();
+            while (getNextOldHistoryLocked(rec)) {
+                hprinter.printNextItem(pw, rec, now);
+            }
+            finishIteratingOldHistoryLocked();
             pw.println("");
         }
         
@@ -1918,6 +1960,8 @@
     
     @SuppressWarnings("unused")
     public void dumpCheckinLocked(PrintWriter pw, String[] args, List<ApplicationInfo> apps) {
+        prepareForDumpLocked();
+
         boolean isUnpluggedOnly = false;
         
         for (String arg : args) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d86504d..fcda673 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -36,6 +36,7 @@
 import android.telephony.SignalStrength;
 import android.telephony.TelephonyManager;
 import android.util.Log;
+import android.util.LogWriter;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
@@ -70,7 +71,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 54;
+    private static final int VERSION = 57;
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -154,11 +155,26 @@
     boolean mHaveBatteryLevel = false;
     boolean mRecordingHistory = true;
     int mNumHistoryItems;
+
+    static final int MAX_HISTORY_BUFFER = 64*1024; // 64KB
+    static final int MAX_MAX_HISTORY_BUFFER = 92*1024; // 92KB
+    final Parcel mHistoryBuffer = Parcel.obtain();
+    final HistoryItem mHistoryLastWritten = new HistoryItem();
+    final HistoryItem mHistoryLastLastWritten = new HistoryItem();
+    int mHistoryBufferLastPos = -1;
+    boolean mHistoryOverflow = false;
+    long mLastHistoryTime = 0;
+
+    final HistoryItem mHistoryCur = new HistoryItem();
+
     HistoryItem mHistory;
     HistoryItem mHistoryEnd;
     HistoryItem mHistoryLastEnd;
     HistoryItem mHistoryCache;
-    final HistoryItem mHistoryCur = new HistoryItem();
+
+    private HistoryItem mHistoryIterator;
+    private boolean mReadOverflow;
+    private boolean mIteratingHistory;
 
     int mStartCount;
 
@@ -1189,9 +1205,82 @@
         mBtHeadset = headset;
     }
 
+    int mChangedBufferStates = 0;
+
+    void addHistoryBufferLocked(long curTime) {
+        if (!mHaveBatteryLevel || !mRecordingHistory) {
+            return;
+        }
+
+        if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
+                && (mHistoryBaseTime+curTime) < (mHistoryLastWritten.time+2000)
+                && ((mHistoryLastWritten.states^mHistoryCur.states)&mChangedBufferStates) == 0) {
+            // If the current is the same as the one before, then we no
+            // longer need the entry.
+            mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
+            mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
+            mHistoryBufferLastPos = -1;
+            if (mHistoryLastLastWritten.cmd == HistoryItem.CMD_UPDATE
+                    && mHistoryLastLastWritten.same(mHistoryCur)) {
+                // If this results in us returning to the state written
+                // prior to the last one, then we can just delete the last
+                // written one and drop the new one.  Nothing more to do.
+                mHistoryLastWritten.setTo(mHistoryLastLastWritten);
+                mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+                return;
+            }
+            mChangedBufferStates |= mHistoryLastWritten.states^mHistoryCur.states;
+            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+        } else {
+            mChangedBufferStates = 0;
+        }
+
+        final int dataSize = mHistoryBuffer.dataSize();
+        if (dataSize >= MAX_HISTORY_BUFFER) {
+            if (!mHistoryOverflow) {
+                mHistoryOverflow = true;
+                addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+            }
+
+            // Once we've reached the maximum number of items, we only
+            // record changes to the battery level and the most interesting states.
+            // Once we've reached the maximum maximum number of items, we only
+            // record changes to the battery level.
+            if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+                    (dataSize >= MAX_MAX_HISTORY_BUFFER
+                            || ((mHistoryEnd.states^mHistoryCur.states)
+                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
+                return;
+            }
+        }
+
+        addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+    }
+
+    void addHistoryBufferLocked(long curTime, byte cmd) {
+        int origPos = 0;
+        if (mIteratingHistory) {
+            origPos = mHistoryBuffer.dataPosition();
+            mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+        }
+        mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
+        mHistoryLastLastWritten.setTo(mHistoryLastWritten);
+        mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        mHistoryLastWritten.writeDelta(mHistoryBuffer, mHistoryLastLastWritten);
+        mLastHistoryTime = curTime;
+        if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
+                + " now " + mHistoryBuffer.dataPosition()
+                + " size is now " + mHistoryBuffer.dataSize());
+        if (mIteratingHistory) {
+            mHistoryBuffer.setDataPosition(origPos);
+        }
+    }
+
     int mChangedStates = 0;
 
     void addHistoryRecordLocked(long curTime) {
+        addHistoryBufferLocked(curTime);
+
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
@@ -1268,6 +1357,7 @@
     }
 
     void clearHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "********** CLEARING HISTORY!");
         if (mHistory != null) {
             mHistoryEnd.next = mHistoryCache;
             mHistoryCache = mHistory;
@@ -1275,6 +1365,15 @@
         }
         mNumHistoryItems = 0;
         mHistoryBaseTime = 0;
+        mLastHistoryTime = 0;
+
+        mHistoryBuffer.setDataSize(0);
+        mHistoryBuffer.setDataPosition(0);
+        mHistoryBuffer.setDataCapacity(MAX_HISTORY_BUFFER/2);
+        mHistoryLastLastWritten.cmd = HistoryItem.CMD_NULL;
+        mHistoryLastWritten.cmd = HistoryItem.CMD_NULL;
+        mHistoryBufferLastPos = -1;
+        mHistoryOverflow = false;
     }
 
     public void doUnplugLocked(long batteryUptime, long batteryRealtime) {
@@ -3910,11 +4009,13 @@
         mDischargeUnplugLevel = 0;
         mDischargeCurrentLevel = 0;
         initDischarge();
+        clearHistoryLocked();
     }
 
     public BatteryStatsImpl(Parcel p) {
         mFile = null;
         mHandler = null;
+        clearHistoryLocked();
         readFromParcel(p);
     }
 
@@ -3932,25 +4033,79 @@
         }
     }
 
-    private HistoryItem mHistoryIterator;
-
-    public boolean startIteratingHistoryLocked() {
+    @Override
+    public boolean startIteratingOldHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+                + " pos=" + mHistoryBuffer.dataPosition());
+        mHistoryBuffer.setDataPosition(0);
+        mReadOverflow = false;
+        mIteratingHistory = true;
         return (mHistoryIterator = mHistory) != null;
     }
 
-    public boolean getNextHistoryLocked(HistoryItem out) {
+    @Override
+    public boolean getNextOldHistoryLocked(HistoryItem out) {
+        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
+        if (!end) {
+            mHistoryLastWritten.readDelta(mHistoryBuffer, null);
+            mReadOverflow |= mHistoryLastWritten.cmd == HistoryItem.CMD_OVERFLOW;
+        }
         HistoryItem cur = mHistoryIterator;
         if (cur == null) {
+            if (!mReadOverflow && !end) {
+                Slog.w(TAG, "Old history ends before new history!");
+            }
             return false;
         }
         out.setTo(cur);
         mHistoryIterator = cur.next;
+        if (!mReadOverflow) {
+            if (end) {
+                Slog.w(TAG, "New history ends before old history!");
+            } else if (!out.same(mHistoryLastWritten)) {
+                long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+                PrintWriter pw = new PrintWriter(new LogWriter(android.util.Log.WARN, TAG));
+                pw.println("Histories differ!");
+                pw.println("Old history:");
+                (new HistoryPrinter()).printNextItem(pw, out, now);
+                pw.println("New history:");
+                (new HistoryPrinter()).printNextItem(pw, mHistoryLastWritten, now);
+            }
+        }
         return true;
     }
 
     @Override
-    public HistoryItem getHistory() {
-        return mHistory;
+    public void finishIteratingOldHistoryLocked() {
+        mIteratingHistory = false;
+        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
+    }
+
+    @Override
+    public boolean startIteratingHistoryLocked() {
+        if (DEBUG_HISTORY) Slog.i(TAG, "ITERATING: buff size=" + mHistoryBuffer.dataSize()
+                + " pos=" + mHistoryBuffer.dataPosition());
+        mHistoryBuffer.setDataPosition(0);
+        mReadOverflow = false;
+        mIteratingHistory = true;
+        return mHistoryBuffer.dataSize() > 0;
+    }
+
+    @Override
+    public boolean getNextHistoryLocked(HistoryItem out) {
+        boolean end = mHistoryBuffer.dataPosition() >= mHistoryBuffer.dataSize();
+        if (end) {
+            return false;
+        }
+
+        out.readDelta(mHistoryBuffer, null);
+        return true;
+    }
+
+    @Override
+    public void finishIteratingHistoryLocked() {
+        mIteratingHistory = false;
+        mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize());
     }
 
     @Override
@@ -4697,7 +4852,9 @@
             Slog.e("BatteryStats", "Error reading battery statistics", e);
         }
 
-        addHistoryRecordLocked(SystemClock.elapsedRealtime(), HistoryItem.CMD_START);
+        long now = SystemClock.elapsedRealtime();
+        addHistoryRecordLocked(now, HistoryItem.CMD_START);
+        addHistoryBufferLocked(now, HistoryItem.CMD_START);
     }
 
     public int describeContents() {
@@ -4705,30 +4862,54 @@
     }
 
     void readHistory(Parcel in) {
-        mHistory = mHistoryEnd = mHistoryCache = null;
-        mHistoryBaseTime = 0;
-        long time;
-        while ((time=in.readLong()) >= 0) {
-            HistoryItem rec = new HistoryItem(time, in);
-            addHistoryRecordLocked(rec);
-            if (rec.time > mHistoryBaseTime) {
-                mHistoryBaseTime = rec.time;
-            }
+        mHistoryBaseTime = in.readLong();
+
+        mHistoryBuffer.setDataSize(0);
+        mHistoryBuffer.setDataPosition(0);
+
+        int bufSize = in.readInt();
+        int curPos = in.dataPosition();
+        if (bufSize >= (MAX_MAX_HISTORY_BUFFER*3)) {
+            Slog.w(TAG, "File corrupt: history data buffer too large " + bufSize);
+        } else if ((bufSize&~3) != bufSize) {
+            Slog.w(TAG, "File corrupt: history data buffer not aligned " + bufSize);
+        } else {
+            if (DEBUG_HISTORY) Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize
+                    + " bytes at " + curPos);
+            mHistoryBuffer.appendFrom(in, curPos, bufSize);
+            in.setDataPosition(curPos + bufSize);
         }
 
-        long oldnow = SystemClock.elapsedRealtime() - (5*60*100);
+        long oldnow = SystemClock.elapsedRealtime() - (5*60*1000);
         if (oldnow > 0) {
             // If the system process has restarted, but not the entire
             // system, then the mHistoryBaseTime already accounts for
             // much of the elapsed time.  We thus want to adjust it back,
             // to avoid large gaps in the data.  We determine we are
             // in this case by arbitrarily saying it is so if at this
-            // point in boot the elapsed time is already more than 5 seconds.
+            // point in boot the elapsed time is already more than 5 minutes.
             mHistoryBaseTime -= oldnow;
         }
     }
 
+    void readOldHistory(Parcel in) {
+        mHistory = mHistoryEnd = mHistoryCache = null;
+        long time;
+        while ((time=in.readLong()) >= 0) {
+            HistoryItem rec = new HistoryItem(time, in);
+            addHistoryRecordLocked(rec);
+        }
+    }
+
     void writeHistory(Parcel out) {
+        out.writeLong(mLastHistoryTime);
+        out.writeInt(mHistoryBuffer.dataSize());
+        if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
+                + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
+        out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+    }
+
+    void writeOldHistory(Parcel out) {
         HistoryItem rec = mHistory;
         while (rec != null) {
             if (rec.time >= 0) rec.writeToParcel(out, 0);
@@ -4746,6 +4927,7 @@
         }
 
         readHistory(in);
+        readOldHistory(in);
 
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
@@ -4935,6 +5117,9 @@
      * @param out the Parcel to be written to.
      */
     public void writeSummaryToParcel(Parcel out) {
+        // Need to update with current kernel wake lock counts.
+        updateKernelWakelocksLocked();
+
         final long NOW_SYS = SystemClock.uptimeMillis() * 1000;
         final long NOWREAL_SYS = SystemClock.elapsedRealtime() * 1000;
         final long NOW = getBatteryUptimeLocked(NOW_SYS);
@@ -4943,6 +5128,7 @@
         out.writeInt(VERSION);
 
         writeHistory(out);
+        writeOldHistory(out);
 
         out.writeInt(mStartCount);
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -5256,6 +5442,9 @@
 
     @SuppressWarnings("unused")
     void writeToParcelLocked(Parcel out, boolean inclUids, int flags) {
+        // Need to update with current kernel wake lock counts.
+        updateKernelWakelocksLocked();
+
         final long uSecUptime = SystemClock.uptimeMillis() * 1000;
         final long uSecRealtime = SystemClock.elapsedRealtime() * 1000;
         final long batteryUptime = getBatteryUptimeLocked(uSecUptime);
@@ -5358,6 +5547,11 @@
         }
     };
 
+    public void prepareForDumpLocked() {
+        // Need to retrieve current kernel wake lock stats before printing.
+        updateKernelWakelocksLocked();
+    }
+
     public void dumpLocked(PrintWriter pw) {
         if (DEBUG) {
             Printer pr = new PrintWriterPrinter(pw);
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index d57f2c9..6ed85d7 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -338,7 +338,7 @@
 
 status_t Parcel::setDataCapacity(size_t size)
 {
-    if (size > mDataSize) return continueWrite(size);
+    if (size > mDataCapacity) return continueWrite(size);
     return NO_ERROR;
 }
 
@@ -386,10 +386,12 @@
     }
     int numObjects = lastIndex - firstIndex + 1;
 
-    // grow data
-    err = growData(len);
-    if (err != NO_ERROR) {
-        return err;
+    if ((mDataSize+len) > mDataCapacity) {
+        // grow data
+        err = growData(len);
+        if (err != NO_ERROR) {
+            return err;
+        }
     }
 
     // append data
@@ -1384,8 +1386,10 @@
                 return NO_MEMORY;
             }
         } else {
-            mDataSize = desired;
-            LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+            if (mDataSize > desired) {
+                mDataSize = desired;
+                LOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
+            }
             if (mDataPos > desired) {
                 mDataPos = desired;
                 LOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index 963a691..b4fdc9f 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -449,6 +449,7 @@
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         boolean isCheckin = false;
+        boolean noOutput = false;
         if (args != null) {
             for (String arg : args) {
                 if ("--checkin".equals(arg)) {
@@ -457,10 +458,22 @@
                     synchronized (mStats) {
                         mStats.resetAllStatsLocked();
                         pw.println("Battery stats reset.");
+                        noOutput = true;
                     }
+                } else if ("--write".equals(arg)) {
+                    synchronized (mStats) {
+                        mStats.writeSyncLocked();
+                        pw.println("Battery stats written.");
+                        noOutput = true;
+                    }
+                } else {
+                    pw.println("Unknown option: " + arg);
                 }
             }
         }
+        if (noOutput) {
+            return;
+        }
         if (isCheckin) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
             synchronized (mStats) {