Start recording wakeup reasons in battery history.

Depends on a modification to libsuspend so that we can get
a callback each time the device wakes up, to read the current
wakeup reasons from the kernel.  These are then stuffed in
to a new field in the battery history.

Also add new dump options --history-start and --charged
to better control what is dumped.

Finally the alarm manager uses a "*walarm*" tag for history
item wake locks that are coming from a wakeup alarm.

Change-Id: I457571973d5b2b5fdc4e4b63ab16275db20d7edd
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc4bb51..0da77ea 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -578,6 +578,9 @@
         // The wake lock that was acquired at this point.
         public HistoryTag wakelockTag;
 
+        // Kernel wakeup reason at this point.
+        public HistoryTag wakeReasonTag;
+
         public static final int EVENT_FLAG_START = 0x8000;
         public static final int EVENT_FLAG_FINISH = 0x4000;
 
@@ -612,6 +615,7 @@
 
         // Pre-allocated objects.
         public final HistoryTag localWakelockTag = new HistoryTag();
+        public final HistoryTag localWakeReasonTag = new HistoryTag();
         public final HistoryTag localEventTag = new HistoryTag();
 
         public HistoryItem() {
@@ -645,6 +649,12 @@
             } else {
                 dest.writeInt(0);
             }
+            if (wakeReasonTag != null) {
+                dest.writeInt(1);
+                wakeReasonTag.writeToParcel(dest, flags);
+            } else {
+                dest.writeInt(0);
+            }
             dest.writeInt(eventCode);
             if (eventCode != EVENT_NONE) {
                 dest.writeInt(eventCode);
@@ -670,6 +680,12 @@
             } else {
                 wakelockTag = null;
             }
+            if (src.readInt() != 0) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.readFromParcel(src);
+            } else {
+                wakeReasonTag = null;
+            }
             eventCode = src.readInt();
             if (eventCode != EVENT_NONE) {
                 eventTag = localEventTag;
@@ -689,6 +705,7 @@
             batteryVoltage = 0;
             states = 0;
             wakelockTag = null;
+            wakeReasonTag = null;
             eventCode = EVENT_NONE;
             eventTag = null;
         }
@@ -719,6 +736,12 @@
             } else {
                 wakelockTag = null;
             }
+            if (o.wakeReasonTag != null) {
+                wakeReasonTag = localWakeReasonTag;
+                wakeReasonTag.setTo(o.wakeReasonTag);
+            } else {
+                wakeReasonTag = null;
+            }
             eventCode = o.eventCode;
             if (o.eventTag != null) {
                 eventTag = localEventTag;
@@ -750,6 +773,14 @@
                     return false;
                 }
             }
+            if (wakeReasonTag != o.wakeReasonTag) {
+                if (wakeReasonTag == null || o.wakeReasonTag == null) {
+                    return false;
+                }
+                if (!wakeReasonTag.equals(o.wakeReasonTag)) {
+                    return false;
+                }
+            }
             if (eventTag != o.eventTag) {
                 if (eventTag == null || o.eventTag == null) {
                     return false;
@@ -1916,51 +1947,6 @@
         long fullWakeLockTimeTotalMicros = 0;
         long partialWakeLockTimeTotalMicros = 0;
 
-        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
-            @Override
-            public int compare(TimerEntry lhs, TimerEntry rhs) {
-                long lhsTime = lhs.mTime;
-                long rhsTime = rhs.mTime;
-                if (lhsTime < rhsTime) {
-                    return 1;
-                }
-                if (lhsTime > rhsTime) {
-                    return -1;
-                }
-                return 0;
-            }
-        };
-
-        if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
-            if (kernelWakelocks.size() > 0) {
-                final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
-                    BatteryStats.Timer timer = ent.getValue();
-                    long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
-                    if (totalTimeMillis > 0) {
-                        timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
-                    }
-                }
-                Collections.sort(timers, timerComparator);
-                for (int i=0; i<timers.size(); i++) {
-                    TimerEntry timer = timers.get(i);
-                    String linePrefix = ": ";
-                    sb.setLength(0);
-                    sb.append(prefix);
-                    sb.append("  Kernel Wake lock ");
-                    sb.append(timer.mName);
-                    linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
-                            which, linePrefix);
-                    if (!linePrefix.equals(": ")) {
-                        sb.append(" realtime");
-                        // Only print out wake locks that were held
-                        pw.println(sb.toString());
-                    }
-                }
-            }
-        }
-
         final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
 
         for (int iu = 0; iu < NU; iu++) {
@@ -2292,6 +2278,55 @@
             pw.println();
         }
 
+        final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
+            @Override
+            public int compare(TimerEntry lhs, TimerEntry rhs) {
+                long lhsTime = lhs.mTime;
+                long rhsTime = rhs.mTime;
+                if (lhsTime < rhsTime) {
+                    return 1;
+                }
+                if (lhsTime > rhsTime) {
+                    return -1;
+                }
+                return 0;
+            }
+        };
+
+        if (reqUid < 0) {
+            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            if (kernelWakelocks.size() > 0) {
+                final ArrayList<TimerEntry> ktimers = new ArrayList<TimerEntry>();
+                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                    BatteryStats.Timer timer = ent.getValue();
+                    long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+                    if (totalTimeMillis > 0) {
+                        ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
+                    }
+                }
+                if (ktimers.size() > 0) {
+                    Collections.sort(ktimers, timerComparator);
+                    pw.print(prefix); pw.println("  All kernel wake locks:");
+                    for (int i=0; i<ktimers.size(); i++) {
+                        TimerEntry timer = ktimers.get(i);
+                        String linePrefix = ": ";
+                        sb.setLength(0);
+                        sb.append(prefix);
+                        sb.append("  Kernel Wake lock ");
+                        sb.append(timer.mName);
+                        linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
+                                which, linePrefix);
+                        if (!linePrefix.equals(": ")) {
+                            sb.append(" realtime");
+                            // Only print out wake locks that were held
+                            pw.println(sb.toString());
+                        }
+                    }
+                    pw.println();
+                }
+            }
+        }
+
         if (timers.size() > 0) {
             Collections.sort(timers, timerComparator);
             pw.print(prefix); pw.println("  All partial wake locks:");
@@ -2727,14 +2762,22 @@
         public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
             if (!checkin) {
                 pw.print("  ");
-                TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                if (now >= 0) {
+                    TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                } else {
+                    TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                }
                 pw.print(" (");
                 pw.print(rec.numReadInts);
                 pw.print(") ");
             } else {
                 if (lastTime < 0) {
-                    pw.print("@");
-                    pw.print(rec.time-now);
+                    if (now >= 0) {
+                        pw.print("@");
+                        pw.print(rec.time-now);
+                    } else {
+                        pw.print(rec.time);
+                    }
                 } else {
                     pw.print(rec.time-lastTime);
                 }
@@ -2858,6 +2901,18 @@
                 }
                 printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
+                if (rec.wakeReasonTag != null) {
+                    if (checkin) {
+                        pw.print(",Wr=");
+                        pw.print(rec.wakeReasonTag.poolIdx);
+                    } else {
+                        pw.print(" wake_reason=");
+                        pw.print(rec.wakeReasonTag.uid);
+                        pw.print(":\"");
+                        pw.print(rec.wakeReasonTag.string);
+                        pw.print("\"");
+                    }
+                }
                 if (rec.eventCode != HistoryItem.EVENT_NONE) {
                     pw.print(checkin ? "," : " ");
                     if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
@@ -2918,108 +2973,130 @@
         pw.print(suffix);
     }
 
+    public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
+    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;
+
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
      *
      * @param pw a Printer to receive the dump output.
      */
     @SuppressWarnings("unused")
-    public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
-            boolean historyOnly) {
+    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         prepareForDumpLocked();
 
-        long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+        final boolean filtering =
+                (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
 
-        final HistoryItem rec = new HistoryItem();
-        final long historyTotalSize = getHistoryTotalSize();
-        final long historyUsedSize = getHistoryUsedSize();
-        if (startIteratingHistoryLocked()) {
-            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);
+        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()) {
+                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();
+                    long lastTime = -1;
+                    while (getNextHistoryLocked(rec)) {
+                        lastTime = rec.time;
+                        if (rec.time >= histStart) {
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+                        }
+                    }
+                    if (histStart >= 0) {
+                        pw.print("  NEXT: "); pw.println(lastTime+1);
+                    }
+                    pw.println();
+                } finally {
+                    finishIteratingHistoryLocked();
                 }
-                pw.println();
-            } finally {
-                finishIteratingHistoryLocked();
+            }
+
+            if (startIteratingOldHistoryLocked()) {
+                try {
+                    pw.println("Old battery History:");
+                    HistoryPrinter hprinter = new HistoryPrinter();
+                    while (getNextOldHistoryLocked(rec)) {
+                        hprinter.printNextItem(pw, rec, now, false);
+                    }
+                    pw.println();
+                } finally {
+                    finishIteratingOldHistoryLocked();
+                }
             }
         }
 
-        if (startIteratingOldHistoryLocked()) {
-            try {
-                pw.println("Old battery History:");
-                HistoryPrinter hprinter = new HistoryPrinter();
-                while (getNextOldHistoryLocked(rec)) {
-                    hprinter.printNextItem(pw, rec, now, false);
-                }
-                pw.println();
-            } finally {
-                finishIteratingOldHistoryLocked();
-            }
-        }
-
-        if (historyOnly) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
             return;
         }
 
-        SparseArray<? extends Uid> uidStats = getUidStats();
-        final int NU = uidStats.size();
-        boolean didPid = false;
-        long nowRealtime = SystemClock.elapsedRealtime();
-        for (int i=0; i<NU; i++) {
-            Uid uid = uidStats.valueAt(i);
-            SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
-            if (pids != null) {
-                for (int j=0; j<pids.size(); j++) {
-                    Uid.Pid pid = pids.valueAt(j);
-                    if (!didPid) {
-                        pw.println("Per-PID Stats:");
-                        didPid = true;
+        if (!filtering) {
+            SparseArray<? extends Uid> uidStats = getUidStats();
+            final int NU = uidStats.size();
+            boolean didPid = false;
+            long nowRealtime = SystemClock.elapsedRealtime();
+            for (int i=0; i<NU; i++) {
+                Uid uid = uidStats.valueAt(i);
+                SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+                if (pids != null) {
+                    for (int j=0; j<pids.size(); j++) {
+                        Uid.Pid pid = pids.valueAt(j);
+                        if (!didPid) {
+                            pw.println("Per-PID Stats:");
+                            didPid = true;
+                        }
+                        long time = pid.mWakeSum + (pid.mWakeStart != 0
+                                ? (nowRealtime - pid.mWakeStart) : 0);
+                        pw.print("  PID "); pw.print(pids.keyAt(j));
+                                pw.print(" wake time: ");
+                                TimeUtils.formatDuration(time, pw);
+                                pw.println("");
                     }
-                    long time = pid.mWakeSum + (pid.mWakeStart != 0
-                            ? (nowRealtime - pid.mWakeStart) : 0);
-                    pw.print("  PID "); pw.print(pids.keyAt(j));
-                            pw.print(" wake time: ");
-                            TimeUtils.formatDuration(time, pw);
-                            pw.println("");
                 }
             }
-        }
-        if (didPid) {
-            pw.println("");
+            if (didPid) {
+                pw.println("");
+            }
         }
 
-        if (!isUnpluggedOnly) {
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
             pw.println("Statistics since last charge:");
             pw.println("  System starts: " + getStartCount()
                     + ", currently on battery: " + getIsOnBattery());
             dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid);
             pw.println("");
         }
-        pw.println("Statistics since last unplugged:");
-        dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
+            pw.println("Statistics since last unplugged:");
+            dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+        }
     }
     
     @SuppressWarnings("unused")
-    public void dumpCheckinLocked(Context context,
-            PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly,
-            boolean includeHistory, boolean historyOnly) {
+    public void dumpCheckinLocked(Context context, PrintWriter pw,
+            List<ApplicationInfo> apps, int flags, long histStart) {
         prepareForDumpLocked();
         
         long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
 
-        if (includeHistory || historyOnly) {
+        final boolean filtering =
+                (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 {
@@ -3034,10 +3111,17 @@
                         pw.println();
                     }
                     HistoryPrinter hprinter = new HistoryPrinter();
+                    long lastTime = -1;
                     while (getNextHistoryLocked(rec)) {
-                        pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
-                        pw.print(HISTORY_DATA); pw.print(',');
-                        hprinter.printNextItem(pw, rec, now, true);
+                        lastTime = rec.time;
+                        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);
+                        }
+                    }
+                    if (histStart >= 0) {
+                        pw.print("NEXT: "); pw.println(lastTime+1);
                     }
                 } finally {
                     finishIteratingHistoryLocked();
@@ -3045,7 +3129,7 @@
             }
         }
 
-        if (historyOnly) {
+        if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
             return;
         }
 
@@ -3076,11 +3160,10 @@
                 }
             }
         }
-        if (isUnpluggedOnly) {
-            dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
-        }
-        else {
+        if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
             dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1);
+        }
+        if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
             dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
         }
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fd93604..db21906 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 = 97 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 98 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1712,7 +1712,7 @@
     static final int DELTA_STATE_FLAG           = 0x00100000;
     // Flag in delta int: a new full state2 int follows.
     static final int DELTA_STATE2_FLAG          = 0x00200000;
-    // Flag in delta int: contains a wakelock tag.
+    // Flag in delta int: contains a wakelock or wakeReason tag.
     static final int DELTA_WAKELOCK_FLAG        = 0x00400000;
     // Flag in delta int: contains an event description.
     static final int DELTA_EVENT_FLAG           = 0x00800000;
@@ -1759,7 +1759,7 @@
         if (stateIntChanged) {
             firstToken |= DELTA_STATE_FLAG;
         }
-        if (cur.wakelockTag != null) {
+        if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             firstToken |= DELTA_WAKELOCK_FLAG;
         }
         if (cur.eventCode != HistoryItem.EVENT_NONE) {
@@ -1795,11 +1795,24 @@
                     + " batteryPlugType=" + cur.batteryPlugType
                     + " states=0x" + Integer.toHexString(cur.states));
         }
-        if (cur.wakelockTag != null) {
-            int index = writeHistoryTag(cur.wakelockTag);
-            dest.writeInt(index);
-            if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
-                + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+        if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+            int wakeLockIndex;
+            int wakeReasonIndex;
+            if (cur.wakelockTag != null) {
+                wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+                    + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+            } else {
+                wakeLockIndex = 0xffff;
+            }
+            if (cur.wakeReasonTag != null) {
+                wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+                if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+                    + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+            } else {
+                wakeReasonIndex = 0xffff;
+            }
+            dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
         }
         if (cur.eventCode != HistoryItem.EVENT_NONE) {
             int index = writeHistoryTag(cur.eventTag);
@@ -1905,14 +1918,29 @@
         }
 
         if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
-            cur.wakelockTag = cur.localWakelockTag;
-            int index = src.readInt();
-            readHistoryTag(index, cur.wakelockTag);
+            int indexes = src.readInt();
+            int wakeLockIndex = indexes&0xffff;
+            int wakeReasonIndex = (indexes>>16)&0xffff;
+            if (wakeLockIndex != 0xffff) {
+                cur.wakelockTag = cur.localWakelockTag;
+                readHistoryTag(wakeLockIndex, cur.wakelockTag);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+                    + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+            } else {
+                cur.wakelockTag = null;
+            }
+            if (wakeReasonIndex != 0xffff) {
+                cur.wakeReasonTag = cur.localWakeReasonTag;
+                readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
+                if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+                    + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+            } else {
+                cur.wakeReasonTag = null;
+            }
             cur.numReadInts += 1;
-            if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
-                + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
         } else {
             cur.wakelockTag = null;
+            cur.wakeReasonTag = null;
         }
 
         if ((firstToken&DELTA_EVENT_FLAG) != 0) {
@@ -1944,6 +1972,7 @@
         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)
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
                         || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
                 && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
@@ -1968,6 +1997,13 @@
                 mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                 mHistoryCur.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);
+            }
             // 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.
@@ -2016,6 +2052,7 @@
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
         mLastHistoryTime = curTime;
         mHistoryCur.wakelockTag = null;
+        mHistoryCur.wakeReasonTag = null;
         mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
         mHistoryCur.eventTag = null;
         if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
@@ -2304,6 +2341,16 @@
         }
     }
 
+    public void noteWakeupReasonLocked(int irq, String reason) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+                + Integer.toHexString(mHistoryCur.states));
+        mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+        mHistoryCur.wakeReasonTag.string = reason;
+        mHistoryCur.wakeReasonTag.uid = irq;
+        addHistoryRecordLocked(elapsedRealtime);
+    }
+
     public int startAddingCpuLocked() {
         mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
 
@@ -7219,8 +7266,7 @@
         pullPendingStateUpdatesLocked();
     }
 
-    public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
-            boolean historyOnly) {
+    public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
         if (DEBUG) {
             pw.println("mOnBatteryTimeBase:");
             mOnBatteryTimeBase.dump(pw, "  ");
@@ -7264,6 +7310,6 @@
                 mBluetoothStateTimer[i].logState(pr, "  ");
             }
         }
-        super.dumpLocked(context, pw, isUnpluggedOnly, reqUid, historyOnly);
+        super.dumpLocked(context, pw, flags, reqUid, histStart);
     }
 }
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index f03a8e0..bda0183 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1283,7 +1283,10 @@
                                 setWakelockWorkSource(alarm.operation, alarm.workSource);
                                 mWakeLock.setUnimportantForLogging(
                                         alarm.operation == mTimeTickSender);
-                                mWakeLock.setHistoryTag(alarm.operation.getTag("*alarm*:"));
+                                mWakeLock.setHistoryTag(alarm.operation.getTag(
+                                        alarm.type == ELAPSED_REALTIME_WAKEUP
+                                                || alarm.type == RTC_WAKEUP
+                                                ? "*walarm*:" : "*alarm*:"));
                                 mWakeLock.acquire();
                             }
                             final InFlight inflight = new InFlight(AlarmManagerService.this,
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index fc7aac2..39bfc23 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -48,6 +48,8 @@
  * battery life.
  */
 public final class BatteryStatsService extends IBatteryStats.Stub {
+    static final String TAG = "BatteryStatsService";
+
     static IBatteryStats sService;
     
     final BatteryStatsImpl mStats;
@@ -66,7 +68,8 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
-    }
+        (new WakeupReasonThread()).start();
+     }
     
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
@@ -538,14 +541,45 @@
         mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
-    
+
+    final class WakeupReasonThread extends Thread {
+        final int[] mIrqs = new int[32];
+        final String[] mReasons = new String[32];
+
+        WakeupReasonThread() {
+            super("BatteryStats_wakeupReason");
+        }
+
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
+
+            try {
+                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]);
+                        }
+                    }
+                }
+            } catch (RuntimeException e) {
+                Slog.e(TAG, "Failure reading wakeup reasons", e);
+            }
+        }
+    }
+
+    private static native int nativeWaitWakeup(int[] outIrqs, String[] outReasons);
+
     private void dumpHelp(PrintWriter pw) {
         pw.println("Battery stats (batterystats) dump options:");
-        pw.println("  [--checkin] [--history] [-c] [--unplugged] [--reset] [--write]");
-        pw.println("  [-h] [<package.name>]");
+        pw.println("  [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]");
+        pw.println("  [--reset] [--write] [-h] [<package.name>]");
         pw.println("  --checkin: format output for a checkin report.");
         pw.println("  --history: show only history data.");
+        pw.println("  --history-start <num>: show only history data starting at given time offset.");
         pw.println("  --unplugged: only output data since last unplugged.");
+        pw.println("  --charged: only output data since last charged.");
         pw.println("  --reset: reset the stats, clearing all current data.");
         pw.println("  --write: force write current collected stats to disk.");
         pw.println("  -h: print this help text.");
@@ -562,23 +596,34 @@
             return;
         }
 
+        int flags = 0;
         boolean isCheckin = false;
-        boolean includeHistory = false;
-        boolean historyOnly = false;
-        boolean isUnpluggedOnly = false;
         boolean noOutput = false;
+        long historyStart = -1;
         int reqUid = -1;
         if (args != null) {
-            for (String arg : args) {
+            for (int i=0; i<args.length; i++) {
+                String arg = args[i];
                 if ("--checkin".equals(arg)) {
                     isCheckin = true;
                 } else if ("--history".equals(arg)) {
-                    historyOnly = true;
+                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
+                } else if ("--history-start".equals(arg)) {
+                    flags |= BatteryStats.DUMP_HISTORY_ONLY;
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Missing time argument for --history-since");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    historyStart = Long.parseLong(args[i]);
                 } else if ("-c".equals(arg)) {
                     isCheckin = true;
-                    includeHistory = true;
+                    flags |= BatteryStats.DUMP_INCLUDE_HISTORY;
                 } else if ("--unplugged".equals(arg)) {
-                    isUnpluggedOnly = true;
+                    flags |= BatteryStats.DUMP_UNPLUGGED_ONLY;
+                } else if ("--charged".equals(arg)) {
+                    flags |= BatteryStats.DUMP_CHARGED_ONLY;
                 } else if ("--reset".equals(arg)) {
                     synchronized (mStats) {
                         mStats.resetAllStatsCmdLocked();
@@ -619,12 +664,11 @@
         if (isCheckin) {
             List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0);
             synchronized (mStats) {
-                mStats.dumpCheckinLocked(mContext, pw, apps, isUnpluggedOnly, includeHistory,
-                        historyOnly);
+                mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
             }
         } else {
             synchronized (mStats) {
-                mStats.dumpLocked(mContext, pw, isUnpluggedOnly, reqUid, historyOnly);
+                mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
             }
         }
     }
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 56144b0..0607925 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -6,6 +6,7 @@
 
 LOCAL_SRC_FILES += \
     $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
new file mode 100644
index 0000000..22cc519
--- /dev/null
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "BatteryStatsService"
+//#define LOG_NDEBUG 0
+
+#include <android_runtime/AndroidRuntime.h>
+#include <jni.h>
+
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+
+#include <cutils/log.h>
+#include <utils/misc.h>
+#include <utils/Log.h>
+#include <hardware/hardware.h>
+#include <suspend/autosuspend.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+namespace android
+{
+
+#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
+#define MAX_REASON_SIZE 512
+
+static bool wakeup_init = false;
+static sem_t wakeup_sem;
+
+static void wakeup_callback(void)
+{
+    ALOGV("In wakeup_callback");
+    int ret = sem_post(&wakeup_sem);
+    if (ret < 0) {
+        char buf[80];
+        strerror_r(errno, buf, sizeof(buf));
+        ALOGE("Error posting wakeup sem: %s\n", buf);
+    }
+}
+
+static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs,
+        jobjectArray outReasons)
+{
+    bool first_time = false;
+
+    if (outIrqs == NULL || outReasons == NULL) {
+        jniThrowException(env, "java/lang/NullPointerException", "null argument");
+        return -1;
+    }
+
+    // Register our wakeup callback if not yet done.
+    if (!wakeup_init) {
+        wakeup_init = true;
+        ALOGV("Creating semaphore...");
+        int ret = sem_init(&wakeup_sem, 0, 0);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error creating semaphore: %s\n", buf);
+            jniThrowException(env, "java/lang/IllegalStateException", buf);
+            return -1;
+        }
+        ALOGV("Registering callback...");
+        set_wakeup_callback(&wakeup_callback);
+        // First time through, we will just drain the current wakeup reasons.
+        first_time = true;
+    } else {
+        // On following calls, we need to wait for wakeup.
+        ALOGV("Waiting for wakeup...");
+        int ret = sem_wait(&wakeup_sem);
+        if (ret < 0) {
+            char buf[80];
+            strerror_r(errno, buf, sizeof(buf));
+            ALOGE("Error waiting on semaphore: %s\n", buf);
+            // Return 0 here to let it continue looping but not return results.
+            return 0;
+        }
+    }
+
+    FILE *fp = fopen(LAST_RESUME_REASON, "r");
+    if (fp == NULL) {
+        ALOGE("Failed to open %s", LAST_RESUME_REASON);
+        return -1;
+    }
+
+    int numOut = env->GetArrayLength(outIrqs);
+    ScopedIntArrayRW irqs(env, outIrqs);
+
+    ALOGV("Reading up to %d wakeup reasons", numOut);
+
+    char mergedreason[MAX_REASON_SIZE];
+    char* mergedreasonpos = mergedreason;
+    int remainreasonlen = MAX_REASON_SIZE;
+    int firstirq = 0;
+    char reasonline[128];
+    int i = 0;
+    while (fgets(reasonline, sizeof(reasonline), fp) != NULL && i < numOut) {
+        char* pos = reasonline;
+        char* endPos;
+        // First field is the index.
+        int irq = (int)strtol(pos, &endPos, 10);
+        if (pos == endPos) {
+            // Ooops.
+            ALOGE("Bad reason line: %s", reasonline);
+            continue;
+        }
+        pos = endPos;
+        // Skip whitespace; rest of the buffer is the reason string.
+        while (*pos == ' ') {
+            pos++;
+        }
+        // Chop newline at end.
+        char* endpos = pos;
+        while (*endpos != 0) {
+            if (*endpos == '\n') {
+                *endpos = 0;
+                break;
+            }
+            endpos++;
+        }
+        if (i == 0) {
+            firstirq = irq;
+        } else {
+            int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+            if (len >= 0 && len < remainreasonlen) {
+                mergedreasonpos += len;
+                remainreasonlen -= len;
+            }
+        }
+        int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+        if (len >= 0 && len < remainreasonlen) {
+            mergedreasonpos += len;
+            remainreasonlen -= len;
+        }
+        // For now it is better to combine all of these in to one entry in the
+        // battery history.  In the future, it might be nice to figure out a way
+        // to efficiently store multiple lines as a single entry in the history.
+        //irqs[i] = irq;
+        //ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(pos));
+        //env->SetObjectArrayElement(outReasons, i, reasonString.get());
+        //ALOGV("Wakeup reason #%d: irw %d reason %s", i, irq, pos);
+        i++;
+    }
+
+    ALOGV("Got %d reasons", i);
+    if (first_time) {
+        i = 0;
+    }
+    if (i > 0) {
+        irqs[0] = firstirq;
+        *mergedreasonpos = 0;
+        ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason));
+        env->SetObjectArrayElement(outReasons, 0, reasonString.get());
+        i = 1;
+    }
+
+    if (fclose(fp) != 0) {
+        ALOGE("Failed to close %s", LAST_RESUME_REASON);
+        return -1;
+    }
+
+    return first_time ? 0 : i;
+}
+
+static JNINativeMethod method_table[] = {
+    { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup },
+};
+
+int register_android_server_BatteryStatsService(JNIEnv *env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
+            method_table, NELEM(method_table));
+}
+
+};
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 00986d5..ac6585b 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 int register_android_server_AlarmManagerService(JNIEnv* env);
+int register_android_server_BatteryStatsService(JNIEnv* env);
 int register_android_server_ConsumerIrService(JNIEnv *env);
 int register_android_server_InputApplicationHandle(JNIEnv* env);
 int register_android_server_InputWindowHandle(JNIEnv* env);
@@ -69,6 +70,7 @@
     register_android_server_AssetAtlasService(env);
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
+    register_android_server_BatteryStatsService(env);
 
     return JNI_VERSION_1_4;
 }