Fix issue #3074745: Crash in system process

Also some tweaks to battery history collection to hopefully
improve the data we have.

Change-Id: I178a54a8c2d15cf38dcceaeef939406f50059aa4
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index d49c8be..d67e6f5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -449,6 +449,10 @@
         public static final int STATE_WAKE_LOCK_FLAG = 1<<17;
         public static final int STATE_SENSOR_ON_FLAG = 1<<16;
         
+        public static final int MOST_INTERESTING_STATES =
+            STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
+            | STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
+
         public int states;
 
         public HistoryItem() {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index b033fad..a9e5052 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -72,6 +72,9 @@
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
     
+    // No, really, THIS is the maximum number of items we will record in the history.
+    private static final int MAX_MAX_HISTORY_ITEMS = 3000;
+
     // 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.
@@ -1169,15 +1172,20 @@
         mBtHeadset = headset;
     }
 
+    int mChangedStates = 0;
+
     void addHistoryRecordLocked(long curTime) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
         // If the current time is basically the same as the last time,
-        // just collapse into one record.
+        // and no states have since the last recorded entry changed and
+        // are now resetting back to their original value, then just collapse
+        // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
-                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)) {
+                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
+                && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
@@ -1188,20 +1196,29 @@
                 mHistoryEnd = mHistoryLastEnd;
                 mHistoryLastEnd = null;
             } else {
+                mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
                 mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
             }
             return;
         }
 
-        if (mNumHistoryItems == MAX_HISTORY_ITEMS) {
+        mChangedStates = 0;
+
+        if (mNumHistoryItems == MAX_HISTORY_ITEMS
+                || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
             addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
         }
 
         if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
             // 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 (mHistoryEnd != null && mHistoryEnd.batteryLevel
-                    == mHistoryCur.batteryLevel) {
+                    == mHistoryCur.batteryLevel &&
+                    (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
+                            || ((mHistoryEnd.states^mHistoryCur.states)
+                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
         }
@@ -4450,10 +4467,13 @@
     }
 
     public void commitPendingDataToDisk() {
-        Parcel next;
+        final Parcel next;
         synchronized (this) {
             next = mPendingWrite;
             mPendingWrite = null;
+            if (next == null) {
+                return;
+            }
 
             mWriteLock.lock();
         }