Battery stats tweaks.

- Generate currently active events when a partial
  history is requested.
- Tag wake lock release with the wake lock name if it is
  different than the one that was acquired.
- New RESET mark for the start of the history.

Change-Id: Ic105656649c46f52855946ed0ad7f98f1d24dedc
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 8b7467f..4857533 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -21,6 +21,7 @@
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Formatter;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -30,6 +31,8 @@
 import android.text.format.DateFormat;
 import android.util.Printer;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.TimeUtils;
 import com.android.internal.os.BatterySipper;
 import com.android.internal.os.BatteryStatsHelper;
@@ -537,6 +540,7 @@
         public static final byte CMD_START = 4;
         public static final byte CMD_CURRENT_TIME = 5;
         public static final byte CMD_OVERFLOW = 6;
+        public static final byte CMD_RESET = 7;
 
         public byte cmd = CMD_NULL;
         
@@ -620,6 +624,8 @@
         public static final int EVENT_SYNC = 0x0004;
         // Number of event types.
         public static final int EVENT_COUNT = 0x0005;
+        // Mask to extract out only the type part of the event.
+        public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
 
         public static final int EVENT_PROC_START = EVENT_PROC | EVENT_FLAG_START;
         public static final int EVENT_PROC_FINISH = EVENT_PROC | EVENT_FLAG_FINISH;
@@ -684,7 +690,7 @@
                 dest.writeInt(eventCode);
                 eventTag.writeToParcel(dest, flags);
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 dest.writeLong(currentTime);
             }
         }
@@ -722,7 +728,7 @@
                 eventCode = EVENT_NONE;
                 eventTag = null;
             }
-            if (cmd == CMD_CURRENT_TIME) {
+            if (cmd == CMD_CURRENT_TIME || cmd == CMD_RESET) {
                 currentTime = src.readLong();
             } else {
                 currentTime = 0;
@@ -833,7 +839,59 @@
             return true;
         }
     }
-    
+
+    public final static class HistoryEventTracker {
+        private final HashMap<String, SparseIntArray>[] mActiveEvents
+                = (HashMap<String, SparseIntArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+
+        public boolean updateState(int code, String name, int uid, int poolIdx) {
+            if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    active = new HashMap<String, SparseIntArray>();
+                    mActiveEvents[idx] = active;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    uids = new SparseIntArray();
+                    active.put(name, uids);
+                }
+                if (uids.indexOfKey(uid) >= 0) {
+                    // Already set, nothing to do!
+                    return false;
+                }
+                uids.put(uid, poolIdx);
+            } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
+                int idx = code&HistoryItem.EVENT_TYPE_MASK;
+                HashMap<String, SparseIntArray> active = mActiveEvents[idx];
+                if (active == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                SparseIntArray uids = active.get(name);
+                if (uids == null) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                idx = uids.indexOfKey(uid);
+                if (idx < 0) {
+                    // not currently active, nothing to do.
+                    return false;
+                }
+                uids.removeAt(idx);
+                if (uids.size() <= 0) {
+                    active.remove(name);
+                }
+            }
+            return true;
+        }
+
+        public HashMap<String, SparseIntArray> getStateForEvent(int code) {
+            return mActiveEvents[code];
+        }
+    }
+
     public static final class BitDescription {
         public final int mask;
         public final int shift;
@@ -861,7 +919,7 @@
             this.shortValues = shortValues;
         }
     }
-    
+
     public abstract int getHistoryTotalSize();
 
     public abstract int getHistoryUsedSize();
@@ -2958,10 +3016,14 @@
                     pw.print(":");
                 }
                 pw.println("START");
-            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
+            } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                    || rec.cmd == HistoryItem.CMD_RESET) {
                 if (checkin) {
                     pw.print(":");
                 }
+                if (rec.cmd == HistoryItem.CMD_RESET) {
+                    pw.print("RESET:");
+                }
                 pw.print("TIME:");
                 if (checkin) {
                     pw.println(rec.currentTime);
@@ -3187,6 +3249,86 @@
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
     public static final int DUMP_VERBOSE = 1<<4;
 
+    private void dumpHistoryLocked(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        final HistoryPrinter hprinter = new HistoryPrinter();
+        final HistoryItem rec = new HistoryItem();
+        long lastTime = -1;
+        long baseTime = -1;
+        boolean printed = false;
+        HistoryEventTracker tracker = null;
+        while (getNextHistoryLocked(rec)) {
+            lastTime = rec.time;
+            if (baseTime < 0) {
+                baseTime = lastTime;
+            }
+            if (rec.time >= histStart) {
+                if (histStart >= 0 && !printed) {
+                    if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
+                            || rec.cmd == HistoryItem.CMD_RESET) {
+                        printed = true;
+                    } else if (rec.currentTime != 0) {
+                        printed = true;
+                        byte cmd = rec.cmd;
+                        rec.cmd = HistoryItem.CMD_CURRENT_TIME;
+                        if (checkin) {
+                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                            pw.print(HISTORY_DATA); pw.print(',');
+                        }
+                        hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                (flags&DUMP_VERBOSE) != 0);
+                        rec.cmd = cmd;
+                    }
+                    if (tracker != null) {
+                        int oldCode = rec.eventCode;
+                        HistoryTag oldTag = rec.eventTag;
+                        rec.eventTag = new HistoryTag();
+                        for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
+                            HashMap<String, SparseIntArray> active
+                                    = tracker.getStateForEvent(i);
+                            if (active == null) {
+                                continue;
+                            }
+                            for (HashMap.Entry<String, SparseIntArray> ent
+                                    : active.entrySet()) {
+                                SparseIntArray uids = ent.getValue();
+                                for (int j=0; j<uids.size(); j++) {
+                                    rec.eventCode = i;
+                                    rec.eventTag.string = ent.getKey();
+                                    rec.eventTag.uid = uids.keyAt(j);
+                                    rec.eventTag.poolIdx = uids.valueAt(j);
+                                    if (checkin) {
+                                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                                        pw.print(HISTORY_DATA); pw.print(',');
+                                    }
+                                    hprinter.printNextItem(pw, rec, baseTime, checkin,
+                                            (flags&DUMP_VERBOSE) != 0);
+                                }
+                            }
+                        }
+                        rec.eventCode = oldCode;
+                        rec.eventTag = oldTag;
+                        tracker = null;
+                    }
+                }
+                if (checkin) {
+                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                    pw.print(HISTORY_DATA); pw.print(',');
+                }
+                hprinter.printNextItem(pw, rec, baseTime, checkin,
+                        (flags&DUMP_VERBOSE) != 0);
+            } else if (rec.eventCode != HistoryItem.EVENT_NONE) {
+                if (tracker == null) {
+                    tracker = new HistoryEventTracker();
+                }
+                tracker.updateState(rec.eventCode, rec.eventTag.string,
+                        rec.eventTag.uid, rec.eventTag.poolIdx);
+            }
+        }
+        if (histStart >= 0) {
+            pw.print(checkin ? "NEXT: " : "  NEXT: "); pw.println(lastTime+1);
+        }
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -3200,9 +3342,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
-            long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
-
-            final HistoryItem rec = new HistoryItem();
             final long historyTotalSize = getHistoryTotalSize();
             final long historyUsedSize = getHistoryUsedSize();
             if (startIteratingHistoryLocked()) {
@@ -3218,35 +3357,7 @@
                     pw.print(" strings using ");
                     printSizeValue(pw, getHistoryStringPoolBytes());
                     pw.println("):");
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    hprinter.printNextItem(pw, rec, baseTime, false,
-                                            (flags&DUMP_VERBOSE) != 0);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            hprinter.printNextItem(pw, rec, baseTime, false,
-                                    (flags&DUMP_VERBOSE) != 0);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("  NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, false);
                     pw.println();
                 } finally {
                     finishIteratingHistoryLocked();
@@ -3255,6 +3366,7 @@
 
             if (startIteratingOldHistoryLocked()) {
                 try {
+                    final HistoryItem rec = new HistoryItem();
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     long baseTime = -1;
@@ -3348,7 +3460,6 @@
                 (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
         if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
-            final HistoryItem rec = new HistoryItem();
             if (startIteratingHistoryLocked()) {
                 try {
                     for (int i=0; i<getHistoryStringPoolSize(); i++) {
@@ -3365,37 +3476,7 @@
                         pw.print("\"");
                         pw.println();
                     }
-                    HistoryPrinter hprinter = new HistoryPrinter();
-                    long lastTime = -1;
-                    long baseTime = -1;
-                    boolean printed = false;
-                    while (getNextHistoryLocked(rec)) {
-                        lastTime = rec.time;
-                        if (baseTime < 0) {
-                            baseTime = lastTime;
-                        }
-                        if (rec.time >= histStart) {
-                            if (histStart >= 0 && !printed) {
-                                if (rec.cmd == HistoryItem.CMD_CURRENT_TIME) {
-                                    printed = true;
-                                } else if (rec.currentTime != 0) {
-                                    printed = true;
-                                    byte cmd = rec.cmd;
-                                    rec.cmd = HistoryItem.CMD_CURRENT_TIME;
-                                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                                    pw.print(HISTORY_DATA); pw.print(',');
-                                    hprinter.printNextItem(pw, rec, baseTime, true, false);
-                                    rec.cmd = cmd;
-                                }
-                            }
-                            pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                            pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, baseTime, true, false);
-                        }
-                    }
-                    if (histStart >= 0) {
-                        pw.print("NEXT: "); pw.println(lastTime+1);
-                    }
+                    dumpHistoryLocked(pw, flags, histStart, true);
                 } finally {
                     finishIteratingHistoryLocked();
                 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 1aff190..7bd5b12 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -188,8 +188,7 @@
 
     boolean mShuttingDown;
 
-    HashMap<String, SparseBooleanArray>[] mActiveEvents
-            = (HashMap<String, SparseBooleanArray>[]) new HashMap[HistoryItem.EVENT_COUNT];
+    final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
@@ -2297,44 +2296,8 @@
 
     public void noteEventLocked(int code, String name, int uid) {
         uid = mapUid(uid);
-        if ((code&HistoryItem.EVENT_FLAG_START) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_START;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                active = new HashMap<String, SparseBooleanArray>();
-                mActiveEvents[idx] = active;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                uids = new SparseBooleanArray();
-                active.put(name, uids);
-            }
-            if (uids.get(uid)) {
-                // Already set, nothing to do!
-                return;
-            }
-            uids.put(uid, true);
-        } else if ((code&HistoryItem.EVENT_FLAG_FINISH) != 0) {
-            int idx = code&~HistoryItem.EVENT_FLAG_FINISH;
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[idx];
-            if (active == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            SparseBooleanArray uids = active.get(name);
-            if (uids == null) {
-                // not currently active, nothing to do.
-                return;
-            }
-            idx = uids.indexOfKey(uid);
-            if (idx < 0 || !uids.valueAt(idx)) {
-                // not currently active, nothing to do.
-                return;
-            }
-            uids.removeAt(idx);
-            if (uids.size() <= 0) {
-                active.remove(name);
-            }
+        if (!mActiveEvents.updateState(code, name, uid, 0)) {
+            return;
         }
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
@@ -2348,6 +2311,9 @@
         }
     }
 
+    private String mInitialAcquireWakeName;
+    private int mInitialAcquireWakeUid = -1;
+
     public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
             boolean unimportantForLogging, long elapsedRealtime, long uptime) {
         uid = mapUid(uid);
@@ -2360,8 +2326,9 @@
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                mHistoryCur.wakelockTag.uid = uid;
+                mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                        = historyName != null ? historyName : name;
+                mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                 mWakeLockImportant = !unimportantForLogging;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             } else if (!mWakeLockImportant && !unimportantForLogging) {
@@ -2369,8 +2336,9 @@
                     // We'll try to update the last tag.
                     mHistoryLastWritten.wakelockTag = null;
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                    mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
-                    mHistoryCur.wakelockTag.uid = uid;
+                    mHistoryCur.wakelockTag.string = mInitialAcquireWakeName
+                            = historyName != null ? historyName : name;
+                    mHistoryCur.wakelockTag.uid = mInitialAcquireWakeUid = uid;
                     addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 mWakeLockImportant = true;
@@ -2395,6 +2363,14 @@
                 mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
+                if ((name != null && !name.equals(mInitialAcquireWakeName))
+                        || uid != mInitialAcquireWakeUid) {
+                    mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
+                    mHistoryCur.wakelockTag.string = name;
+                    mHistoryCur.wakelockTag.uid = uid;
+                }
+                mInitialAcquireWakeName = null;
+                mInitialAcquireWakeUid = -1;
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             }
         }
@@ -5699,7 +5675,8 @@
         final long lastRealtime = out.time;
         final long lastWalltime = out.currentTime;
         readHistoryDelta(mHistoryBuffer, out);
-        if (out.cmd != HistoryItem.CMD_CURRENT_TIME && lastWalltime != 0) {
+        if (out.cmd != HistoryItem.CMD_CURRENT_TIME
+                && out.cmd != HistoryItem.CMD_RESET && lastWalltime != 0) {
             out.currentTime = lastWalltime + (out.time - lastRealtime);
         }
         return true;
@@ -5845,17 +5822,15 @@
 
     private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
         for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
-            HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
+            HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i);
             if (active == null) {
                 continue;
             }
-            for (HashMap.Entry<String, SparseBooleanArray> ent : active.entrySet()) {
-                SparseBooleanArray uids = ent.getValue();
+            for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) {
+                SparseIntArray uids = ent.getValue();
                 for (int j=0; j<uids.size(); j++) {
-                    if (uids.valueAt(j)) {
-                        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
-                                uids.keyAt(j));
-                    }
+                    addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+                            uids.keyAt(j));
                 }
             }
         }
@@ -5971,7 +5946,8 @@
             boolean reset) {
         mRecordingHistory = true;
         mHistoryCur.currentTime = System.currentTimeMillis();
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs,
+                reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME,
                 mHistoryCur);
         mHistoryCur.currentTime = 0;
         if (reset) {