More battery history improvements.

- Better batching of history items.  Fixed problems where empty
  entries would be created because state toggles got lost.
- The string pool is now a HistoryTag pool, containing both a string
  and uid; now an entry only requires 16 bits in the history data.
- Acquiring the first wake lock also now includes a HistoryTag
  identifying who did the aquisition.
- Cleaned up printing of signal strengths and cell radio types.
- There was code that tried to allow you to add new history entries
  while iterating the history...  but these should never happen
  together, so turned that into a failure...  and fixed an issue
  where we could leave the battery stats in a state where it
  thinks it is continually iterating.

Change-Id: I1afa57ee2d66b186932c502dbdd633cdd4aed353
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 2afea1f..27c0f5d 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -452,6 +452,56 @@
         }
     }
 
+    public final static class HistoryTag {
+        public String string;
+        public int uid;
+
+        public int poolIdx;
+
+        public void setTo(HistoryTag o) {
+            string = o.string;
+            uid = o.uid;
+            poolIdx = o.poolIdx;
+        }
+
+        public void setTo(String _string, int _uid) {
+            string = _string;
+            uid = _uid;
+            poolIdx = -1;
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(string);
+            dest.writeInt(uid);
+        }
+
+        public void readFromParcel(Parcel src) {
+            string = src.readString();
+            uid = src.readInt();
+            poolIdx = -1;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            HistoryTag that = (HistoryTag) o;
+
+            if (uid != that.uid) return false;
+            if (!string.equals(that.string)) return false;
+
+            return true;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = string.hashCode();
+            result = 31 * result + uid;
+            return result;
+        }
+    }
+
     public final static class HistoryItem implements Parcelable {
         public HistoryItem next;
         
@@ -500,7 +550,7 @@
         public static final int STATE_PHONE_STATE_MASK = 0x7 << STATE_PHONE_STATE_SHIFT;
         // Constants from DATA_CONNECTION_*
         public static final int STATE_DATA_CONNECTION_SHIFT = 9;
-        public static final int STATE_DATA_CONNECTION_MASK = 0x1f;
+        public static final int STATE_DATA_CONNECTION_MASK = 0x1f << STATE_DATA_CONNECTION_SHIFT;
 
         // These states always appear directly in the first int token
         // of a delta change; they should be ones that change relatively
@@ -529,21 +579,30 @@
 
         public int states;
 
+        // The wake lock that was acquired at this point.
+        public HistoryTag wakelockTag;
+
         public static final int EVENT_NONE = 0;
         public static final int EVENT_PROC_STARTED = 1;
         public static final int EVENT_PROC_FINISHED = 2;
 
         // For CMD_EVENT.
         public int eventCode;
-        public String eventName;
-        public int eventNameIdx;    // only filled in when iterating.
-        public int eventUid;
+        public HistoryTag eventTag;
+
+        // Meta-data when reading.
+        public int numReadInts;
+
+        // Pre-allocated objects.
+        public final HistoryTag localWakelockTag = new HistoryTag();
+        public final HistoryTag localEventTag = new HistoryTag();
 
         public HistoryItem() {
         }
         
         public HistoryItem(long time, Parcel src) {
             this.time = time;
+            numReadInts = 2;
             readFromParcel(src);
         }
         
@@ -563,14 +622,20 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(states);
+            if (wakelockTag != null) {
+                dest.writeInt(1);
+                wakelockTag.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
             if (cmd == CMD_EVENT) {
                 dest.writeInt(eventCode);
-                dest.writeInt(eventUid);
-                dest.writeString(eventName);
+                eventTag.writeToParcel(dest, flags);
             }
         }
 
         public void readFromParcel(Parcel src) {
+            int start = src.dataPosition();
             int bat = src.readInt();
             cmd = (byte)(bat&0xff);
             batteryLevel = (byte)((bat>>8)&0xff);
@@ -581,14 +646,21 @@
             batteryTemperature = (short)(bat&0xffff);
             batteryVoltage = (char)((bat>>16)&0xffff);
             states = src.readInt();
+            if (src.readInt() != 0) {
+                wakelockTag = localWakelockTag;
+                wakelockTag.readFromParcel(src);
+            } else {
+                wakelockTag = null;
+            }
             if (cmd == CMD_EVENT) {
                 eventCode = src.readInt();
-                eventUid = src.readInt();
-                eventName = src.readString();
-                eventNameIdx = 0;
+                eventTag = localEventTag;
+                eventTag.readFromParcel(src);
             } else {
                 eventCode = EVENT_NONE;
+                eventTag = null;
             }
+            numReadInts += (src.dataPosition()-start)/4;
         }
 
         public void clear() {
@@ -601,9 +673,9 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             states = 0;
+            wakelockTag = null;
             eventCode = EVENT_NONE;
-            eventUid = 0;
-            eventName = null;
+            eventTag = null;
         }
         
         public void setTo(HistoryItem o) {
@@ -616,10 +688,19 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             states = o.states;
+            if (o.wakelockTag != null) {
+                wakelockTag = localWakelockTag;
+                wakelockTag.setTo(o.wakelockTag);
+            } else {
+                wakelockTag = null;
+            }
             eventCode = o.eventCode;
-            eventUid = o.eventUid;
-            eventName = o.eventName;
-            eventNameIdx = o.eventNameIdx;
+            if (o.eventTag != null) {
+                eventTag = localEventTag;
+                eventTag.setTo(o.eventTag);
+            } else {
+                eventTag = null;
+            }
         }
 
         public void setTo(long time, byte cmd, int eventCode, int eventUid, String eventName,
@@ -627,9 +708,12 @@
             this.time = time;
             this.cmd = cmd;
             this.eventCode = eventCode;
-            this.eventUid = eventUid;
-            this.eventName = eventName;
-            this.eventNameIdx = 0;
+            if (eventCode != EVENT_NONE) {
+                eventTag = localEventTag;
+                eventTag.setTo(eventName, eventUid);
+            } else {
+                eventTag = null;
+            }
             batteryLevel = o.batteryLevel;
             batteryStatus = o.batteryStatus;
             batteryHealth = o.batteryHealth;
@@ -637,6 +721,12 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             states = o.states;
+            if (o.wakelockTag != null) {
+                wakelockTag = localWakelockTag;
+                wakelockTag.setTo(o.wakelockTag);
+            } else {
+                wakelockTag = null;
+            }
         }
 
         public boolean sameNonEvent(HistoryItem o) {
@@ -650,13 +740,26 @@
         }
 
         public boolean same(HistoryItem o) {
-            if (!sameNonEvent(o) || eventCode != o.eventCode || eventUid != o.eventUid) {
+            if (!sameNonEvent(o) || eventCode != o.eventCode) {
                 return false;
             }
-            if (eventName == o.eventName) {
-                return true;
+            if (wakelockTag != o.wakelockTag) {
+                if (wakelockTag == null || o.wakelockTag == null) {
+                    return false;
+                }
+                if (!wakelockTag.equals(o.wakelockTag)) {
+                    return false;
+                }
             }
-            return eventName != null && o.eventName != null && eventName.equals(o.eventName);
+            if (eventTag != o.eventTag) {
+                if (eventTag == null || o.eventTag == null) {
+                    return false;
+                }
+                if (!eventTag.equals(o.eventTag)) {
+                    return false;
+                }
+            }
+            return true;
         }
     }
     
@@ -688,11 +791,19 @@
         }
     }
     
+    public abstract int getHistoryTotalSize();
+
+    public abstract int getHistoryUsedSize();
+
     public abstract boolean startIteratingHistoryLocked();
 
     public abstract int getHistoryStringPoolSize();
 
-    public abstract String getHistoryStringPoolItem(int index);
+    public abstract int getHistoryStringPoolBytes();
+
+    public abstract String getHistoryTagPoolString(int index);
+
+    public abstract int getHistoryTagPoolUid(int index);
 
     public abstract boolean getNextHistoryLocked(HistoryItem out);
 
@@ -1746,14 +1857,14 @@
         
         sb.setLength(0);
         sb.append(prefix);
-        sb.append("  Signal levels: ");
+        sb.append("  Signal levels:");
         didOne = false;
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             final long time = getPhoneSignalStrengthTime(i, batteryRealtime, which);
             if (time == 0) {
                 continue;
             }
-            if (didOne) sb.append(", ");
+            sb.append("\n    ");
             didOne = true;
             sb.append(SignalStrength.SIGNAL_STRENGTH_NAMES[i]);
             sb.append(" ");
@@ -1764,7 +1875,7 @@
             sb.append(getPhoneSignalStrengthCount(i, which));
             sb.append("x");
         }
-        if (!didOne) sb.append("No activity");
+        if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
         sb.setLength(0);
@@ -1775,14 +1886,14 @@
 
         sb.setLength(0);
         sb.append(prefix);
-        sb.append("  Radio types: ");
+        sb.append("  Radio types:");
         didOne = false;
         for (int i=0; i<NUM_DATA_CONNECTION_TYPES; i++) {
             final long time = getPhoneDataConnectionTime(i, batteryRealtime, which);
             if (time == 0) {
                 continue;
             }
-            if (didOne) sb.append(", ");
+            sb.append("\n    ");
             didOne = true;
             sb.append(DATA_CONNECTION_NAMES[i]);
             sb.append(" ");
@@ -1793,7 +1904,7 @@
             sb.append(getPhoneDataConnectionCount(i, which));
             sb.append("x");
         }
-        if (!didOne) sb.append("No activity");
+        if (!didOne) sb.append(" (no activity)");
         pw.println(sb.toString());
 
         sb.setLength(0);
@@ -2259,21 +2370,30 @@
         }
     }
 
-    static void printBitDescriptions(PrintWriter pw, int oldval, int newval,
+    static void printBitDescriptions(PrintWriter pw, int oldval, int newval, HistoryTag wakelockTag,
             BitDescription[] descriptions, boolean longNames) {
         int diff = oldval ^ newval;
         if (diff == 0) return;
+        boolean didWake = false;
         for (int i=0; i<descriptions.length; i++) {
             BitDescription bd = descriptions[i];
-            int mask = bd.mask;
-            if (bd.shift > 0) {
-                mask <<= bd.shift;
-            }
-            if ((diff&mask) != 0) {
+            if ((diff&bd.mask) != 0) {
                 pw.print(longNames ? " " : ",");
                 if (bd.shift < 0) {
-                    pw.print((newval&mask) != 0 ? "+" : "-");
+                    pw.print((newval&bd.mask) != 0 ? "+" : "-");
                     pw.print(longNames ? bd.name : bd.shortName);
+                    if (bd.mask == HistoryItem.STATE_WAKE_LOCK_FLAG && wakelockTag != null) {
+                        didWake = true;
+                        pw.print("=");
+                        if (longNames) {
+                            UserHandle.formatUid(pw, wakelockTag.uid);
+                            pw.print(":\"");
+                            pw.print(wakelockTag.string);
+                            pw.print("\"");
+                        } else {
+                            pw.print(wakelockTag.poolIdx);
+                        }
+                    }
                 } else {
                     pw.print(longNames ? bd.name : bd.shortName);
                     pw.print("=");
@@ -2286,6 +2406,17 @@
                 }
             }
         }
+        if (!didWake && wakelockTag != null) {
+            pw.print(longNames ? "wake_lock=" : "w=");
+            if (longNames) {
+                UserHandle.formatUid(pw, wakelockTag.uid);
+                pw.print(":\"");
+                pw.print(wakelockTag.string);
+                pw.print("\"");
+            } else {
+                pw.print(wakelockTag.poolIdx);
+            }
+        }
     }
     
     public void prepareForDumpLocked() {
@@ -2305,7 +2436,9 @@
             if (!checkin) {
                 pw.print("  ");
                 TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                pw.print(" ");
+                pw.print(" (");
+                pw.print(rec.numReadInts);
+                pw.print(") ");
             } else {
                 if (lastTime < 0) {
                     pw.print("@");
@@ -2427,7 +2560,7 @@
                     pw.print(checkin ? ",Bv=" : " volt=");
                     pw.print(oldVolt);
                 }
-                printBitDescriptions(pw, oldState, rec.states,
+                printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
                 if (rec.eventCode != HistoryItem.EVENT_NONE) {
                     switch (rec.eventCode) {
@@ -2450,15 +2583,12 @@
                             break;
                     }
                     if (checkin) {
-                        pw.print(rec.eventUid);
+                        pw.print(rec.eventTag.poolIdx);
                     } else {
-                        UserHandle.formatUid(pw, rec.eventUid);
-                    }
-                    pw.print(":");
-                    if (checkin) {
-                        pw.print(rec.eventNameIdx);
-                    } else {
-                        pw.print(rec.eventName);
+                        UserHandle.formatUid(pw, rec.eventTag.uid);
+                        pw.print(":\"");
+                        pw.print(rec.eventTag.string);
+                        pw.print("\"");
                     }
                 }
                 pw.println();
@@ -2467,6 +2597,33 @@
         }
     }
 
+    private void printSizeValue(PrintWriter pw, long size) {
+        float result = size;
+        String suffix = "";
+        if (result >= 10*1024) {
+            suffix = "KB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "MB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "GB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "TB";
+            result = result / 1024;
+        }
+        if (result >= 10*1024) {
+            suffix = "PB";
+            result = result / 1024;
+        }
+        pw.print((int)result);
+        pw.print(suffix);
+    }
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
@@ -2480,24 +2637,42 @@
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
         final HistoryItem rec = new HistoryItem();
+        final long historyTotalSize = getHistoryTotalSize();
+        final long historyUsedSize = getHistoryUsedSize();
         if (startIteratingHistoryLocked()) {
-            pw.println("Battery History:");
-            HistoryPrinter hprinter = new HistoryPrinter();
-            while (getNextHistoryLocked(rec)) {
-                hprinter.printNextItem(pw, rec, now, false);
+            try {
+                pw.print("Battery History (");
+                pw.print((100*historyUsedSize)/historyTotalSize);
+                pw.print("% used, ");
+                printSizeValue(pw, historyUsedSize);
+                pw.print(" used of ");
+                printSizeValue(pw, historyTotalSize);
+                pw.print(", ");
+                pw.print(getHistoryStringPoolSize());
+                pw.print(" strings using ");
+                printSizeValue(pw, getHistoryStringPoolBytes());
+                pw.println("):");
+                HistoryPrinter hprinter = new HistoryPrinter();
+                while (getNextHistoryLocked(rec)) {
+                    hprinter.printNextItem(pw, rec, now, false);
+                }
+                pw.println();
+            } finally {
+                finishIteratingHistoryLocked();
             }
-            finishIteratingHistoryLocked();
-            pw.println("");
         }
 
         if (startIteratingOldHistoryLocked()) {
-            pw.println("Old battery History:");
-            HistoryPrinter hprinter = new HistoryPrinter();
-            while (getNextOldHistoryLocked(rec)) {
-                hprinter.printNextItem(pw, rec, now, false);
+            try {
+                pw.println("Old battery History:");
+                HistoryPrinter hprinter = new HistoryPrinter();
+                while (getNextOldHistoryLocked(rec)) {
+                    hprinter.printNextItem(pw, rec, now, false);
+                }
+                pw.println();
+            } finally {
+                finishIteratingOldHistoryLocked();
             }
-            finishIteratingOldHistoryLocked();
-            pw.println("");
         }
 
         if (historyOnly) {
@@ -2553,21 +2728,26 @@
         if (includeHistory || historyOnly) {
             final HistoryItem rec = new HistoryItem();
             if (startIteratingHistoryLocked()) {
-                for (int i=0; i<getHistoryStringPoolSize(); i++) {
-                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                    pw.print(HISTORY_STRING_POOL); pw.print(',');
-                    pw.print(i);
-                    pw.print(',');
-                    pw.print(getHistoryStringPoolItem(i));
-                    pw.println();
+                try {
+                    for (int i=0; i<getHistoryStringPoolSize(); i++) {
+                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                        pw.print(HISTORY_STRING_POOL); pw.print(',');
+                        pw.print(i);
+                        pw.print(',');
+                        pw.print(getHistoryTagPoolString(i));
+                        pw.print(',');
+                        pw.print(getHistoryTagPoolUid(i));
+                        pw.println();
+                    }
+                    HistoryPrinter hprinter = new HistoryPrinter();
+                    while (getNextHistoryLocked(rec)) {
+                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+                        pw.print(HISTORY_DATA); pw.print(',');
+                        hprinter.printNextItem(pw, rec, now, true);
+                    }
+                } finally {
+                    finishIteratingHistoryLocked();
                 }
-                HistoryPrinter hprinter = new HistoryPrinter();
-                while (getNextHistoryLocked(rec)) {
-                    pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                    pw.print(HISTORY_DATA); pw.print(',');
-                    hprinter.printNextItem(pw, rec, now, true);
-                }
-                finishIteratingHistoryLocked();
             }
         }