Add dumpsys of recent wakeup patterns, for analysis

Keeps one day of alarm history, and for each one puts a line in dumpsys:
RTC timestamp, millis since previous wakeup alarm, uid, and intent action

Bug 9532215

Change-Id: Ib4fb41aa6291a4cf8f3301bfebf7974e99a9d52c
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index bc06561..0be9ca5 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -53,7 +53,7 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
-import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.Map;
 import java.util.TimeZone;
 
@@ -69,6 +69,7 @@
     private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; 
     private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
     private static final int TIME_CHANGED_MASK = 1 << 16;
+    private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
 
     // Alignment quantum for inexact repeating alarms
     private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
@@ -82,6 +83,8 @@
     private static final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
     
+    private static final boolean WAKEUP_STATS = true;
+
     private final Context mContext;
 
     private final LocalLog mLog = new LocalLog(TAG);
@@ -106,6 +109,21 @@
     private final PendingIntent mTimeTickSender;
     private final PendingIntent mDateChangeSender;
 
+    class WakeupEvent {
+        public long when;
+        public int uid;
+        public String action;
+
+        public WakeupEvent(long theTime, int theUid, String theAction) {
+            when = theTime;
+            uid = theUid;
+            action = theAction;
+        }
+    }
+
+    private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
+    private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
+
     private static final class InFlight extends Intent {
         final PendingIntent mPendingIntent;
         final Pair<String, ComponentName> mTarget;
@@ -640,6 +658,27 @@
                             pw.println();
                 }
             }
+
+            if (WAKEUP_STATS) {
+                pw.println();
+                pw.println("  Recent Wakeup History:");
+                final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS");
+                long last = -1;
+                for (WakeupEvent event : mRecentWakeups) {
+                    pw.print("    "); pw.print(sdf.format(new Date(event.when)));
+                    pw.print('|');
+                    if (last < 0) {
+                        pw.print('0');
+                    } else {
+                        pw.print(event.when - last);
+                    }
+                    last = event.when;
+                    pw.print('|'); pw.print(event.uid);
+                    pw.print('|'); pw.print(event.action);
+                    pw.println();
+                }
+                pw.println();
+            }
         }
     }
 
@@ -775,7 +814,20 @@
             pw.print(prefix); pw.print("operation="); pw.println(operation);
         }
     }
-    
+
+    void recordWakeupAlarms(ArrayList<Alarm> alarms, long now, long skewToRTC) {
+        for (Alarm a : alarms) {
+            if (a.when > now) {
+                break;
+            }
+
+            WakeupEvent e = new WakeupEvent(now + skewToRTC,
+                    a.operation.getCreatorUid(),
+                    a.operation.getIntent().getAction());
+            mRecentWakeups.add(e);
+        }
+    }
+
     private class AlarmThread extends Thread
     {
         public AlarmThread()
@@ -809,6 +861,27 @@
                         TAG, "Checking for alarms... rtc=" + nowRTC
                         + ", elapsed=" + nowELAPSED);
 
+                    if (WAKEUP_STATS) {
+                        if ((result & IS_WAKEUP_MASK) != 0) {
+                            long newEarliest = nowRTC - RECENT_WAKEUP_PERIOD;
+                            int n = 0;
+                            for (WakeupEvent event : mRecentWakeups) {
+                                if (event.when > newEarliest) break;
+                                n++; // number of now-stale entries at the list head
+                            }
+                            for (int i = 0; i < n; i++) {
+                                mRecentWakeups.remove();
+                            }
+
+                            recordWakeupAlarms(mRtcWakeupAlarms,
+                                    nowRTC,
+                                    0);
+                            recordWakeupAlarms(mElapsedRealtimeWakeupAlarms,
+                                    nowELAPSED,
+                                    nowRTC - nowELAPSED);
+                        }
+                    }
+
                     if ((result & RTC_WAKEUP_MASK) != 0)
                         triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);