Battery stats: track actually running time

Use the uptime while creating the battery stats history to
generate a new event indicating when the CPU is actually running.
We can do this in combination with the new event reporting when
the CPU comes awake, looking at the difference between the
uptime and elapsed time at that point to determine when it last
when to sleep.

Also use this information to generate a new set of aggregated
states, the uptime caused by each wake reason.

Finally use new radio down timestamp to adjust the times we
compute for radio use.  Note that this information is not (yet)
being used to adjust how these are recorded in the history.

Change-Id: I723b3b526c8e7d64de0cac9d1193e04132d5a3e4
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index b0d94d5..87beb95 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
     private static final String FOREGROUND_DATA = "fg";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
+    private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
     private static final String USER_ACTIVITY_DATA = "ua";
     private static final String BATTERY_DATA = "bt";
@@ -201,6 +202,25 @@
     }
 
     /**
+     * State for keeping track of long counting information.
+     */
+    public static abstract class LongCounter {
+
+        /**
+         * Returns the count associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         */
+        public abstract long getCountLocked(int which);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
      * State for keeping track of timing information.
      */
     public static abstract class Timer {
@@ -552,8 +572,8 @@
         // These states always appear directly in the first int token
         // of a delta change; they should be ones that change relatively
         // frequently.
-        public static final int STATE_WAKE_LOCK_FLAG = 1<<31;
-        public static final int STATE_SENSOR_ON_FLAG = 1<<30;
+        public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
         public static final int STATE_GPS_ON_FLAG = 1<<29;
         public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
         public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
@@ -562,9 +582,9 @@
         public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
         // These are on the lower bits used for the command; if they change
         // we need to write another int of data.
-        public static final int STATE_PHONE_SCANNING_FLAG = 1<<23;
+        public static final int STATE_SENSOR_ON_FLAG = 1<<23;
         public static final int STATE_AUDIO_ON_FLAG = 1<<22;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
         public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
@@ -577,6 +597,7 @@
 
         public int states;
 
+        public static final int STATE2_VIDEO_ON_FLAG = 1<<0;
         public int states2;
 
         // The wake lock that was acquired at this point.
@@ -653,6 +674,7 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(states);
+            dest.writeInt(states2);
             if (wakelockTag != null) {
                 wakelockTag.writeToParcel(dest, flags);
             }
@@ -680,6 +702,7 @@
             batteryTemperature = (short)(bat2&0xffff);
             batteryVoltage = (char)((bat2>>16)&0xffff);
             states = src.readInt();
+            states2 = src.readInt();
             if ((bat&0x10000000) != 0) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.readFromParcel(src);
@@ -718,6 +741,7 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             states = 0;
+            states2 = 0;
             wakelockTag = null;
             wakeReasonTag = null;
             eventCode = EVENT_NONE;
@@ -744,6 +768,7 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             states = o.states;
+            states2 = o.states2;
             if (o.wakelockTag != null) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.setTo(o.wakelockTag);
@@ -774,6 +799,7 @@
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
                     && states == o.states
+                    && states2 == o.states2
                     && currentTime == o.currentTime;
         }
 
@@ -970,6 +996,15 @@
     public abstract int getMobileRadioActiveCount(int which);
 
     /**
+     * Returns the time in microseconds that is the difference between the mobile radio
+     * time we saw based on the elapsed timestamp when going down vs. the given time stamp
+     * from the radio.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioActiveAdjustedTime(int which);
+
+    /**
      * Returns the time in microseconds that the mobile network has been active
      * (in a high power state) but not being able to blame on an app.
      *
@@ -1026,9 +1061,10 @@
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
-    
+
     public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
             = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
         new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
         new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
         new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps", "g"),
@@ -1039,7 +1075,6 @@
         new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
         new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
-        new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
         new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
@@ -1062,6 +1097,11 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
+            = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+    };
+
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync"
     };
@@ -1294,6 +1334,8 @@
      */
     public abstract long computeRealtime(long curTime, int which);
     
+    public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
     /** Returns the number of different speeds that the CPU can run at */
@@ -1554,7 +1596,8 @@
                 wifiRunningTime / 1000, bluetoothOnTime / 1000,
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
                 fullWakeLockTimeTotal, partialWakeLockTimeTotal,
-                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which));
+                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which),
+                getMobileRadioActiveAdjustedTime(which));
         
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -1626,16 +1669,22 @@
         }
         
         if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
                     sb.setLength(0);
                     printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
-    
-                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
                             sb.toString());
                 }
             }
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
+                            "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+                }
+            }
         }
         
         BatteryStatsHelper helper = new BatteryStatsHelper(context);
@@ -2111,6 +2160,18 @@
             pw.println(sb.toString());
         }
 
+        final long mobileActiveAdjustedTime = getMobileRadioActiveAdjustedTime(which);
+        if (mobileActiveAdjustedTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Mobile radio active adjusted time: ");
+            formatTimeMs(sb, mobileActiveAdjustedTime / 1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(mobileActiveAdjustedTime, whichBatteryRealtime));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
         pw.print(prefix);
                 pw.print("  Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
                 pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
@@ -2346,24 +2407,49 @@
                     pw.println();
                 }
             }
-        }
 
-        if (timers.size() > 0) {
-            Collections.sort(timers, timerComparator);
-            pw.print(prefix); pw.println("  All partial wake locks:");
-            for (int i=0; i<timers.size(); i++) {
-                TimerEntry timer = timers.get(i);
-                sb.setLength(0);
-                sb.append("  Wake lock ");
-                UserHandle.formatUid(sb, timer.mId);
-                sb.append(" ");
-                sb.append(timer.mName);
-                printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
-                sb.append(" realtime");
-                pw.println(sb.toString());
+            if (timers.size() > 0) {
+                Collections.sort(timers, timerComparator);
+                pw.print(prefix); pw.println("  All partial wake locks:");
+                for (int i=0; i<timers.size(); i++) {
+                    TimerEntry timer = timers.get(i);
+                    sb.setLength(0);
+                    sb.append("  Wake lock ");
+                    UserHandle.formatUid(sb, timer.mId);
+                    sb.append(" ");
+                    sb.append(timer.mName);
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+                timers.clear();
+                pw.println();
             }
-            timers.clear();
-            pw.println();
+
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                pw.print(prefix); pw.println("  All wakeup reasons:");
+                final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    BatteryStats.LongCounter counter = ent.getValue();
+                    reasons.add(new TimerEntry(ent.getKey(), 0, null,
+                            ent.getValue().getCountLocked(which)));
+                }
+                Collections.sort(reasons, timerComparator);
+                for (int i=0; i<reasons.size(); i++) {
+                    TimerEntry timer = reasons.get(i);
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Wakeup reason ");
+                    sb.append(timer.mName);
+                    sb.append(": ");
+                    formatTimeMs(sb, timer.mTime);
+                    sb.append("realtime");
+                    pw.println(sb.toString());
+                }
+                pw.println();
+            }
         }
 
         for (int iu=0; iu<NU; iu++) {
@@ -2772,6 +2858,7 @@
 
     public static class HistoryPrinter {
         int oldState = 0;
+        int oldState2 = 0;
         int oldLevel = -1;
         int oldStatus = -1;
         int oldHealth = -1;
@@ -2780,7 +2867,8 @@
         int oldVolt = -1;
         long lastTime = -1;
 
-        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+                boolean verbose) {
             if (!checkin) {
                 pw.print("  ");
                 if (now >= 0) {
@@ -2831,16 +2919,18 @@
                     if (rec.batteryLevel < 10) pw.print("00");
                     else if (rec.batteryLevel < 100) pw.print("0");
                     pw.print(rec.batteryLevel);
-                    pw.print(" ");
-                    if (rec.states < 0) ;
-                    else 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 (verbose) {
+                        pw.print(" ");
+                        if (rec.states < 0) ;
+                        else 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));
+                    }
                 } else {
                     if (oldLevel != rec.batteryLevel) {
                         oldLevel = rec.batteryLevel;
@@ -2934,6 +3024,8 @@
                 }
                 printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
+                printBitDescriptions(pw, oldState2, rec.states2, null,
+                        HISTORY_STATE2_DESCRIPTIONS, !checkin);
                 if (rec.wakeReasonTag != null) {
                     if (checkin) {
                         pw.print(",Wr=");
@@ -3010,6 +3102,7 @@
     public static final int DUMP_CHARGED_ONLY = 1<<1;
     public static final int DUMP_HISTORY_ONLY = 1<<2;
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+    public static final int DUMP_VERBOSE = 1<<4;
 
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
@@ -3047,7 +3140,8 @@
                     while (getNextHistoryLocked(rec)) {
                         lastTime = rec.time;
                         if (rec.time >= histStart) {
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+                                    (flags&DUMP_VERBOSE) != 0);
                         }
                     }
                     if (histStart >= 0) {
@@ -3064,7 +3158,7 @@
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     while (getNextOldHistoryLocked(rec)) {
-                        hprinter.printNextItem(pw, rec, now, false);
+                        hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
                     }
                     pw.println();
                 } finally {
@@ -3150,7 +3244,7 @@
                         if (rec.time >= histStart) {
                             pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                             pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
                         }
                     }
                     if (histStart >= 0) {
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5ba5c57..32f700d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -55,7 +55,7 @@
     void noteScreenOff();
     void noteInputEvent();
     void noteUserActivity(int uid, int event);
-    void noteDataConnectionActive(int type, boolean active);
+    void noteDataConnectionActive(int type, boolean active, long timestampNs);
     void notePhoneOn();
     void notePhoneOff();
     void notePhoneSignalStrength(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 082f1a5..1d88533 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 99 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 101 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -191,7 +191,7 @@
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
-    boolean mRecordingHistory = true;
+    boolean mRecordingHistory = false;
     int mNumHistoryItems;
 
     static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
@@ -200,6 +200,7 @@
     final HistoryItem mHistoryLastWritten = new HistoryItem();
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
+    final HistoryItem mHistoryAddTmp = new HistoryItem();
     final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
@@ -209,7 +210,8 @@
     int mHistoryBufferLastPos = -1;
     boolean mHistoryOverflow = false;
     long mLastHistoryElapsedRealtime = 0;
-    long mLastHistoryUptime = 0;
+    long mTrackRunningHistoryElapsedRealtime = 0;
+    long mTrackRunningHistoryUptime = 0;
 
     final HistoryItem mHistoryCur = new HistoryItem();
 
@@ -287,6 +289,7 @@
     boolean mMobileRadioActive;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
+    LongSamplingCounter mMobileRadioActiveAdjustedTime;
     LongSamplingCounter mMobileRadioActiveUnknownTime;
     LongSamplingCounter mMobileRadioActiveUnknownCount;
 
@@ -330,12 +333,21 @@
     private final HashMap<String, SamplingTimer> mKernelWakelockStats =
             new HashMap<String, SamplingTimer>();
 
-    public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
+    public Map<String, ? extends Timer> getKernelWakelockStats() {
         return mKernelWakelockStats;
     }
 
     private static int sKernelWakelockUpdateVersion = 0;
 
+    String mLastWakeupReason = null;
+    long mLastWakeupUptimeMs = 0;
+    private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
+            new HashMap<String, LongSamplingCounter>();
+
+    public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+        return mWakeupReasonStats;
+    }
+
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
                               Process.PROC_QUOTES,
@@ -719,7 +731,7 @@
         }
     }
 
-    public static class LongSamplingCounter implements TimeBaseObs {
+    public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
         final TimeBase mTimeBase;
         long mCount;
         long mLoadedCount;
@@ -775,6 +787,14 @@
             return val;
         }
 
+        @Override
+        public void logState(Printer pw, String prefix) {
+            pw.println(prefix + "mCount=" + mCount
+                    + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+                    + " mUnpluggedCount=" + mUnpluggedCount
+                    + " mPluggedCount=" + mPluggedCount);
+        }
+
         void addCountLocked(long count) {
             mCount += count;
         }
@@ -1409,6 +1429,10 @@
             return 0;
         }
 
+        long getLastUpdateTimeMs() {
+            return mUpdateTime;
+        }
+
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -1502,6 +1526,19 @@
         }
     }
 
+    /*
+     * Get the wakeup reason counter, and create a new one if one
+     * doesn't already exist.
+     */
+    public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
+        LongSamplingCounter counter = mWakeupReasonStats.get(name);
+        if (counter == null) {
+            counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+            mWakeupReasonStats.put(name, counter);
+        }
+        return counter;
+    }
+
     private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
 
         FileInputStream is;
@@ -1759,6 +1796,10 @@
         if (stateIntChanged) {
             firstToken |= DELTA_STATE_FLAG;
         }
+        final boolean state2IntChanged = cur.states2 != last.states2;
+        if (state2IntChanged) {
+            firstToken |= DELTA_STATE2_FLAG;
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             firstToken |= DELTA_WAKELOCK_FLAG;
         }
@@ -1795,6 +1836,11 @@
                     + " batteryPlugType=" + cur.batteryPlugType
                     + " states=0x" + Integer.toHexString(cur.states));
         }
+        if (state2IntChanged) {
+            dest.writeInt(cur.states2);
+            if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             int wakeLockIndex;
             int wakeReasonIndex;
@@ -1917,6 +1963,12 @@
             cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~DELTA_STATE_MASK));
         }
 
+        if ((firstToken&DELTA_STATE2_FLAG) != 0) {
+            cur.states2 = src.readInt();
+            if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
+
         if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
             int indexes = src.readInt();
             int wakeLockIndex = indexes&0xffff;
@@ -1958,29 +2010,34 @@
         }
     }
 
-    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
+    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
         final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
-        final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
+        final int diffStates = mHistoryLastWritten.states^cur.states;
+        final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+        final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
         if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
                 + Integer.toHexString(diffStates) + " lastDiff="
-                + Integer.toHexString(lastDiffStates));
+                + Integer.toHexString(lastDiffStates) + " diff2="
+                + Integer.toHexString(diffStates2) + " lastDiff2="
+                + Integer.toHexString(lastDiffStates2));
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                 && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
-                && (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
-                && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
+                && (diffStates2&lastDiffStates2) == 0
+                && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+                && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
-                        || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
-                && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
-                && mHistoryLastWritten.batteryStatus == mHistoryCur.batteryStatus
-                && mHistoryLastWritten.batteryHealth == mHistoryCur.batteryHealth
-                && mHistoryLastWritten.batteryPlugType == mHistoryCur.batteryPlugType
-                && mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
-                && mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
+                        || cur.eventCode == HistoryItem.EVENT_NONE)
+                && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+                && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+                && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+                && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+                && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+                && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
             // We can merge this new change in with the last one.  Merging is
             // allowed as long as only the states have changed, and within those states
             // as long as no bit has changed both between now and the last entry, as
@@ -1994,23 +2051,23 @@
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakelockTag != null) {
-                mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+                cur.wakelockTag = cur.localWakelockTag;
+                cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
             }
             // If the last written history had a wake reason tag, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakeReasonTag != null) {
-                mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
-                mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+                cur.wakeReasonTag = cur.localWakeReasonTag;
+                cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
             }
             // If the last written history had an event, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have an event.
             if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
-                mHistoryCur.eventCode = mHistoryLastWritten.eventCode;
-                mHistoryCur.eventTag = mHistoryCur.localEventTag;
-                mHistoryCur.eventTag.setTo(mHistoryLastWritten.eventTag);
+                cur.eventCode = mHistoryLastWritten.eventCode;
+                cur.eventTag = cur.localEventTag;
+                cur.eventTag.setTo(mHistoryLastWritten.eventTag);
             }
             mHistoryLastWritten.setTo(mHistoryLastLastWritten);
         }
@@ -2019,8 +2076,8 @@
         if (dataSize >= MAX_HISTORY_BUFFER) {
             if (!mHistoryOverflow) {
                 mHistoryOverflow = true;
-                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
-                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
                 return;
             }
 
@@ -2028,43 +2085,64 @@
             // 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 &&
+            if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
-                            || ((mHistoryLastWritten.states^mHistoryCur.states)
+                            || ((mHistoryLastWritten.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
 
-            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
             return;
         }
 
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
     }
 
-    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
+            HistoryItem cur) {
         if (mIteratingHistory) {
             throw new IllegalStateException("Can't do this while iterating history!");
         }
         mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
         mHistoryLastLastWritten.setTo(mHistoryLastWritten);
-        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryElapsedRealtime = elapsedRealtimeMs;
-        mLastHistoryUptime = uptimeMs;
-        mHistoryCur.wakelockTag = null;
-        mHistoryCur.wakeReasonTag = null;
-        mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
-        mHistoryCur.eventTag = null;
+        cur.wakelockTag = null;
+        cur.wakeReasonTag = null;
+        cur.eventCode = HistoryItem.EVENT_NONE;
+        cur.eventTag = null;
         if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
                 + " now " + mHistoryBuffer.dataPosition()
                 + " size is now " + mHistoryBuffer.dataSize());
     }
 
     int mChangedStates = 0;
+    int mChangedStates2 = 0;
 
     void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+        if (mTrackRunningHistoryElapsedRealtime != 0) {
+            final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
+            final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
+            if (diffUptime < (diffElapsed-20)) {
+                final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+                mHistoryAddTmp.setTo(mHistoryLastWritten);
+                mHistoryAddTmp.wakelockTag = null;
+                mHistoryAddTmp.wakeReasonTag = null;
+                mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+                mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+                addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+            }
+        }
+        mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+        mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
+        mTrackRunningHistoryUptime = uptimeMs;
+        addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+    }
+
+    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
 
         if (!USE_OLD_HISTORY) {
             return;
@@ -2080,25 +2158,28 @@
         // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
                 && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
-                && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
+                && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
+                && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 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
                     && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
-                    && mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
+                    && mHistoryLastEnd.sameNonEvent(cur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
                 mHistoryCache = mHistoryEnd;
                 mHistoryEnd = mHistoryLastEnd;
                 mHistoryLastEnd = null;
             } else {
-                mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
-                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+                mChangedStates |= mHistoryEnd.states^cur.states;
+                mChangedStates2 |= mHistoryEnd.states^cur.states2;
+                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
             }
             return;
         }
 
         mChangedStates = 0;
+        mChangedStates2 = 0;
 
         if (mNumHistoryItems == MAX_HISTORY_ITEMS
                 || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
@@ -2111,9 +2192,9 @@
             // 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 &&
+                    == cur.batteryLevel &&
                     (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
-                            || ((mHistoryEnd.states^mHistoryCur.states)
+                            || ((mHistoryEnd.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
@@ -2128,17 +2209,18 @@
         mHistoryCur.eventTag = mHistoryCur.localEventTag;
         mHistoryCur.eventTag.string = name;
         mHistoryCur.eventTag.uid = uid;
-        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
+        // XXX should be calling addHistoryRecordLocked()?
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
     }
 
-    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
         HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
             rec = new HistoryItem();
         }
-        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
+        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
 
         addHistoryRecordLocked(rec);
     }
@@ -2168,7 +2250,8 @@
 
         mHistoryBaseTime = 0;
         mLastHistoryElapsedRealtime = 0;
-        mLastHistoryUptime = 0;
+        mTrackRunningHistoryElapsedRealtime = 0;
+        mTrackRunningHistoryUptime = 0;
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -2284,6 +2367,7 @@
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
+            aggregateLastWakeupUptimeLocked(uptime);
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2372,14 +2456,26 @@
         }
     }
 
-    public void noteWakeupReasonLocked(int irq, String reason) {
+    void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+        if (mLastWakeupReason != null) {
+            long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+            LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
+            timer.addCountLocked(deltaUptime);
+            mLastWakeupReason = null;
+        }
+    }
+
+    public void noteWakeupReasonLocked(String reason) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
-        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
+        aggregateLastWakeupUptimeLocked(uptime);
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
         mHistoryCur.wakeReasonTag.string = reason;
-        mHistoryCur.wakeReasonTag.uid = irq;
+        mHistoryCur.wakeReasonTag.uid = 0;
+        mLastWakeupReason = reason;
+        mLastWakeupUptimeMs = uptime;
         addHistoryRecordLocked(elapsedRealtime, uptime);
     }
 
@@ -2670,13 +2766,28 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         if (ConnectivityManager.isNetworkTypeMobile(type)) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             if (mMobileRadioActive != active) {
-                if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
-                else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                long realElapsedRealtimeMs;
+                if (active) {
+                    realElapsedRealtimeMs = elapsedRealtime;
+                    mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                } else {
+                    realElapsedRealtimeMs = timestampNs / (1000*1000);
+                    long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+                    if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+                        Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+                                + " is before start time " + lastUpdateTimeMs);
+                        realElapsedRealtimeMs = elapsedRealtime;
+                    } else if (realElapsedRealtimeMs < elapsedRealtime) {
+                        mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+                                - realElapsedRealtimeMs);
+                    }
+                    mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                }
                 if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
                         + Integer.toHexString(mHistoryCur.states));
                 addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2685,9 +2796,9 @@
                     mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
                     mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
                 } else {
-                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, elapsedRealtime);
-                    mMobileRadioActiveTimer.stopRunningLocked(elapsedRealtime);
-                    mMobileRadioActivePerAppTimer.stopRunningLocked(elapsedRealtime);
+                    mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
                 }
             }
         }
@@ -2978,7 +3089,7 @@
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
         if (!mVideoOn) {
-            mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -2993,7 +3104,7 @@
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
         if (mVideoOn) {
-            mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3398,6 +3509,10 @@
         return mMobileRadioActiveTimer.getCountLocked(which);
     }
 
+    @Override public long getMobileRadioActiveAdjustedTime(int which) {
+        return mMobileRadioActiveAdjustedTime.getCountLocked(which);
+    }
+
     @Override public long getMobileRadioActiveUnknownTime(int which) {
         return mMobileRadioActiveUnknownTime.getCountLocked(which);
     }
@@ -5458,6 +5573,7 @@
         }
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
         mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
@@ -5543,9 +5659,9 @@
                 PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                 pw.println("Histories differ!");
                 pw.println("Old history:");
-                (new HistoryPrinter()).printNextItem(pw, out, now, false);
+                (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
                 pw.println("New history:");
-                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false);
+                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
                 pw.flush();
             }
         }
@@ -5719,6 +5835,7 @@
         }
         mMobileRadioActiveTimer.reset(false);
         mMobileRadioActivePerAppTimer.reset(false);
+        mMobileRadioActiveAdjustedTime.reset(false);
         mMobileRadioActiveUnknownTime.reset(false);
         mMobileRadioActiveUnknownCount.reset(false);
         mWifiOnTimer.reset(false);
@@ -5744,7 +5861,14 @@
             }
             mKernelWakelockStats.clear();
         }
-        
+
+        if (mWakeupReasonStats.size() > 0) {
+            for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
+                mOnBatteryScreenOffTimeBase.remove(timer);
+            }
+            mWakeupReasonStats.clear();
+        }
+
         initDischarge();
 
         clearHistoryLocked();
@@ -5828,9 +5952,10 @@
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
                     + Integer.toHexString(mHistoryCur.states));
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
+            if (reset) {
+                mRecordingHistory = true;
+                startRecordingHistory(mSecRealtime, mSecUptime, reset);
+            }
             addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
             if (mScreenOn) {
@@ -5843,9 +5968,6 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
-            if (reset) {
-                initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
-            }
         } else {
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
@@ -5868,6 +5990,18 @@
         }
     }
 
+    private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+            boolean reset) {
+        mRecordingHistory = true;
+        mHistoryCur.currentTime = System.currentTimeMillis();
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+                mHistoryCur);
+        mHistoryCur.currentTime = 0;
+        if (reset) {
+            initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+        }
+    }
+
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
@@ -5895,7 +6029,15 @@
             }
             if (onBattery) {
                 mDischargeCurrentLevel = level;
-                mRecordingHistory = true;
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
+            } else if (level < 96) {
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
             }
             if (onBattery != mOnBattery) {
                 mHistoryCur.batteryLevel = (byte)level;
@@ -6497,15 +6639,14 @@
         }
 
         if (mHistoryBuffer.dataPosition() > 0) {
+            mRecordingHistory = true;
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             if (USE_OLD_HISTORY) {
-                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
+                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
             }
-            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
+            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+            startRecordingHistory(elapsedRealtime, uptime, false);
         }
     }
 
@@ -6681,6 +6822,7 @@
         mMobileRadioActive = false;
         mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
         mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
+        mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
         mWifiOn = false;
@@ -6708,6 +6850,18 @@
             }
         }
 
+        int NWR = in.readInt();
+        if (NWR > 10000) {
+            Slog.w(TAG, "File corrupt: too many wakeup reasons " + NWR);
+            return;
+        }
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+            }
+        }
+
         sNumSpeedSteps = in.readInt();
         if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
             throw new BadParcelableException("Bad speed steps in data: " + sNumSpeedSteps);
@@ -6915,6 +7069,7 @@
         }
         mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
         mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -6933,7 +7088,19 @@
             if (kwlt != null) {
                 out.writeInt(1);
                 out.writeString(ent.getKey());
-                ent.getValue().writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
+        out.writeInt(mWakeupReasonStats.size());
+        for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+            LongSamplingCounter counter = ent.getValue();
+            if (counter != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                counter.writeSummaryFromParcelLocked(out);
             } else {
                 out.writeInt(0);
             }
@@ -7171,6 +7338,7 @@
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
                 in);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mWifiOn = false;
@@ -7205,12 +7373,22 @@
         for (int ikw = 0; ikw < NKW; ikw++) {
             if (in.readInt() != 0) {
                 String wakelockName = in.readString();
-                in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel
                 SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
                 mKernelWakelockStats.put(wakelockName, kwlt);
             }
         }
 
+        mWakeupReasonStats.clear();
+        int NWR = in.readInt();
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
+                        in);
+                mWakeupReasonStats.put(reasonName, counter);
+            }
+        }
+
         mPartialTimers.clear();
         mFullTimers.clear();
         mWindowTimers.clear();
@@ -7283,6 +7461,7 @@
         }
         mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
         mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
+        mMobileRadioActiveAdjustedTime.writeToParcel(out);
         mMobileRadioActiveUnknownTime.writeToParcel(out);
         mMobileRadioActiveUnknownCount.writeToParcel(out);
         mWifiOnTimer.writeToParcel(out, uSecRealtime);
@@ -7313,7 +7492,18 @@
                 if (kwlt != null) {
                     out.writeInt(1);
                     out.writeString(ent.getKey());
-                    Timer.writeTimerToParcel(out, kwlt, uSecRealtime);
+                    kwlt.writeToParcel(out, uSecRealtime);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+            out.writeInt(mWakeupReasonStats.size());
+            for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+                LongSamplingCounter counter = ent.getValue();
+                if (counter != null) {
+                    out.writeInt(1);
+                    out.writeString(ent.getKey());
+                    counter.writeToParcel(out);
                 } else {
                     out.writeInt(0);
                 }
@@ -7383,6 +7573,8 @@
             }
             pr.println("*** Mobile network active timer:");
             mMobileRadioActiveTimer.logState(pr, "  ");
+            pr.println("*** Mobile network active adjusted timer:");
+            mMobileRadioActiveAdjustedTime.logState(pr, "  ");
             pr.println("*** Wifi timer:");
             mWifiOnTimer.logState(pr, "  ");
             pr.println("*** WifiRunning timer:");
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9de3efe..41ffdc1 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -103,6 +103,7 @@
     private long mNextNonWakeup;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
+    boolean mLastWakeLockUnimportantForLogging;
     ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
     final AlarmHandler mHandler = new AlarmHandler();
     ClockReceiver mClockReceiver;
@@ -1345,17 +1346,21 @@
      */
     void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
         try {
-            mWakeLock.setUnimportantForLogging(pi == mTimeTickSender);
+            final boolean unimportant = pi == mTimeTickSender;
+            mWakeLock.setUnimportantForLogging(unimportant);
             if (ws != null) {
-                if (first) {
+                if (first || mLastWakeLockUnimportantForLogging) {
                     mWakeLock.setHistoryTag(pi.getTag(
                             type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
                                     ? "*walarm*:" : "*alarm*:"));
                 } else {
                     mWakeLock.setHistoryTag(null);
                 }
+                mLastWakeLockUnimportantForLogging = unimportant;
                 mWakeLock.setWorkSource(ws);
                 return;
+            } else {
+                mLastWakeLockUnimportantForLogging = false;
             }
 
             final int uid = ActivityManagerNative.getDefault()
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a09d605..5273cec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -348,7 +348,7 @@
      */
     private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) {
         try {
-            getBatteryStats().noteDataConnectionActive(type, active);
+            getBatteryStats().noteDataConnectionActive(type, active, tsNanos);
         } catch (RemoteException e) {
         }
 
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index fa4a9d1..0ddb827 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -248,10 +248,10 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteDataConnectionActive(type, active);
+            mStats.noteDataConnectionActive(type, active, timestampNs);
         }
     }
 
@@ -570,9 +570,12 @@
                 int num;
                 while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
                     synchronized (mStats) {
-                        for (int i=0; i<num; i++) {
-                            //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]);
-                            mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]);
+                        if (num > 0) {
+                            for (int i=0; i<num; i++) {
+                                mStats.noteWakeupReasonLocked(mReasons[i]);
+                            }
+                        } else {
+                            mStats.noteWakeupReasonLocked("unknown");
                         }
                     }
                 }
@@ -653,7 +656,7 @@
                     dumpHelp(pw);
                     return;
                 } else if ("-a".equals(arg)) {
-                    // fall through
+                    flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                     pw.println("Unknown option: " + arg);
                     dumpHelp(pw);
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 22cc519..da4cc48 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -139,16 +139,21 @@
             }
             endpos++;
         }
-        if (i == 0) {
+        // For now we are not separating out the first irq.
+        // This is because in practice there are always multiple
+        // lines of wakeup reasons, so it is better to just treat
+        // them all together as a single string.
+        if (false && i == 0) {
             firstirq = irq;
         } else {
-            int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+            int len = snprintf(mergedreasonpos, remainreasonlen,
+                    i == 0 ? "%d" : ":%d", irq);
             if (len >= 0 && len < remainreasonlen) {
                 mergedreasonpos += len;
                 remainreasonlen -= len;
             }
         }
-        int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+        int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
         if (len >= 0 && len < remainreasonlen) {
             mergedreasonpos += len;
             remainreasonlen -= len;