incidentd: Adding proto dump for AlarmManager.

Test: flash device and check incident.proto output.
Bug: 65750801
Change-Id: If1e42c70908a0b11358cf48766c028e37c0329ae
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 2813e8b..55f9e28 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -33,6 +33,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.proto.ProtoOutputStream;
 
 import libcore.util.ZoneInfoDB;
 
@@ -48,7 +49,7 @@
  * if it is not already running.  Registered alarms are retained while the
  * device is asleep (and can optionally wake the device up if they go off
  * during that time), but will be cleared if it is turned off and rebooted.
- * 
+ *
  * <p>The Alarm Manager holds a CPU wake lock as long as the alarm receiver's
  * onReceive() method is executing. This guarantees that the phone will not sleep
  * until you have finished handling the broadcast. Once onReceive() returns, the
@@ -296,7 +297,7 @@
      * {@link Intent#EXTRA_ALARM_COUNT Intent.EXTRA_ALARM_COUNT} that indicates
      * how many past alarm events have been accumulated into this intent
      * broadcast.  Recurring alarms that have gone undelivered because the
-     * phone was asleep may have a count greater than one when delivered.  
+     * phone was asleep may have a count greater than one when delivered.
      *
      * <div class="note">
      * <p>
@@ -396,10 +397,10 @@
      * set a recurring alarm for the top of every hour but the phone was asleep
      * from 7:45 until 8:45, an alarm will be sent as soon as the phone awakens,
      * then the next alarm will be sent at 9:00.
-     * 
-     * <p>If your application wants to allow the delivery times to drift in 
+     *
+     * <p>If your application wants to allow the delivery times to drift in
      * order to guarantee that at least a certain time interval always elapses
-     * between alarms, then the approach to take is to use one-time alarms, 
+     * between alarms, then the approach to take is to use one-time alarms,
      * scheduling the next one yourself when handling each alarm delivery.
      *
      * <p class="note">
@@ -1056,7 +1057,7 @@
         /**
          * Creates a new alarm clock description.
          *
-         * @param triggerTime time at which the underlying alarm is triggered in wall time 
+         * @param triggerTime time at which the underlying alarm is triggered in wall time
          *                    milliseconds since the epoch
          * @param showIntent an intent that can be used to show or edit details of
          *                        the alarm clock.
@@ -1089,7 +1090,7 @@
          * Returns an intent that can be used to show or edit details of the alarm clock in
          * the application that scheduled it.
          *
-         * <p class="note">Beware that any application can retrieve and send this intent, 
+         * <p class="note">Beware that any application can retrieve and send this intent,
          * potentially with additional fields filled in. See
          * {@link PendingIntent#send(android.content.Context, int, android.content.Intent)
          * PendingIntent.send()} and {@link android.content.Intent#fillIn Intent.fillIn()}
@@ -1121,5 +1122,13 @@
                 return new AlarmClockInfo[size];
             }
         };
+
+        /** @hide */
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+            proto.write(AlarmClockInfoProto.TRIGGER_TIME_MS, mTriggerTime);
+            mShowIntent.writeToProto(proto, AlarmClockInfoProto.SHOW_INTENT);
+            proto.end(token);
+        }
     }
 }
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index a25c226..baeabc3 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -33,6 +33,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.AndroidException;
+import android.util.proto.ProtoOutputStream;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1081,7 +1082,16 @@
         sb.append('}');
         return sb.toString();
     }
-    
+
+    /** @hide */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+        if (mTarget != null) {
+            proto.write(PendingIntentProto.TARGET, mTarget.asBinder().toString());
+        }
+        proto.end(token);
+    }
+
     public int describeContents() {
         return 0;
     }
diff --git a/core/java/com/android/internal/util/LocalLog.java b/core/java/com/android/internal/util/LocalLog.java
index f0e6171..8edb739 100644
--- a/core/java/com/android/internal/util/LocalLog.java
+++ b/core/java/com/android/internal/util/LocalLog.java
@@ -20,6 +20,7 @@
 import java.util.ArrayList;
 
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 /**
  * Helper class for logging serious issues, which also keeps a small
@@ -63,4 +64,16 @@
             return true;
         }
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        final long token = proto.start(fieldId);
+
+        synchronized (mLines) {
+            for (int i = 0; i < mLines.size(); ++i) {
+                proto.write(LocalLogProto.LINES, mLines.get(i));
+            }
+        }
+
+        proto.end(token);
+    }
 }
diff --git a/core/proto/android/app/alarmmanager.proto b/core/proto/android/app/alarmmanager.proto
new file mode 100644
index 0000000..789e3d6
--- /dev/null
+++ b/core/proto/android/app/alarmmanager.proto
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/pendingintent.proto";
+
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.AlarmManager object.
+ */
+message AlarmManagerProto {
+  enum AlarmType {
+    // Alarm time in System.currentTimeMillis() (wall clock time in UTC), which
+    // will wake up the device when it goes off.
+    RTC_WAKEUP = 0;
+    // Alarm time in System.currentTimeMillis() (wall clock time in UTC).  This
+    // alarm does not wake the device up; if it goes off while the device is
+    // asleep, it will not be delivered until the next time the device wakes up.
+    RTC = 1;
+    // Alarm time in SystemClock.elapsedRealtime() (time since boot, including
+    // sleep), which will wake up the device when it goes off.
+    ELAPSED_REALTIME_WAKEUP = 2;
+    // Alarm time in SystemClock.elapsedRealtime() (time since boot, including
+    // sleep). This alarm does not wake the device up; if it goes off while the
+    // device is asleep, it will not be delivered until the next time the device
+    // wakes up.
+    ELAPSED_REALTIME = 3;
+  }
+}
+
+// An android.app.AlarmManager.AlarmClockInfo object.
+message AlarmClockInfoProto {
+  // This value is UTC wall clock time in milliseconds, as returned by
+  // System#currentTimeMillis() for example.
+  optional int64 trigger_time_ms = 1;
+  optional android.app.PendingIntentProto show_intent = 2;
+}
diff --git a/core/proto/android/app/pendingintent.proto b/core/proto/android/app/pendingintent.proto
new file mode 100644
index 0000000..b562c0b
--- /dev/null
+++ b/core/proto/android/app/pendingintent.proto
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.PendingIntent object.
+ */
+message PendingIntentProto {
+  optional string target = 1;
+}
diff --git a/core/proto/android/internal/locallog.proto b/core/proto/android/internal/locallog.proto
new file mode 100644
index 0000000..51f6c1c
--- /dev/null
+++ b/core/proto/android/internal/locallog.proto
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+package com.android.internal.util;
+
+option java_multiple_files = true;
+
+message LocalLogProto {
+  repeated string lines = 1;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 5a5454e..0887f03 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -32,6 +32,7 @@
 import "frameworks/base/core/proto/android/service/print.proto";
 import "frameworks/base/core/proto/android/service/procstats.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
+import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/os/incidentheader.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
@@ -125,4 +126,9 @@
 
     optional com.android.server.am.proto.ServiceProto amservices = 3014;
     optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
+
+    optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "alarm --proto"
+    ];
 }
diff --git a/core/proto/android/server/alarmmanagerservice.proto b/core/proto/android/server/alarmmanagerservice.proto
new file mode 100644
index 0000000..d2cd190
--- /dev/null
+++ b/core/proto/android/server/alarmmanagerservice.proto
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/alarmmanager.proto";
+import "frameworks/base/core/proto/android/app/pendingintent.proto";
+import "frameworks/base/core/proto/android/internal/locallog.proto";
+import "frameworks/base/core/proto/android/os/worksource.proto";
+
+package com.android.server;
+
+option java_multiple_files = true;
+
+message AlarmManagerServiceProto {
+  optional int64 current_time = 1;
+  optional int64 elapsed_realtime = 2;
+  optional int64 last_time_change_clock_time = 3;
+  optional int64 last_time_change_realtime = 4;
+  // Current settings
+  optional ConstantsProto settings = 5;
+  // UIDs currently in the foreground.
+  repeated int32 foreground_uids = 6;
+  // Packages forced into app standby.
+  repeated string forced_app_standby_packages = 7;
+
+  optional bool is_interactive = 8;
+  // Only valid if is_interactive is false.
+  optional int64 time_since_non_interactive_ms = 9;
+  // Only valid if is_interactive is false.
+  optional int64 max_wakeup_delay_ms = 10;
+  // Only valid if is_interactive is false.
+  optional int64 time_since_last_dispatch_ms = 11;
+  // Only valid if is_interactive is false.
+  optional int64 time_until_next_non_wakeup_delivery_ms = 12;
+
+  optional int64 time_until_next_non_wakeup_alarm_ms = 13;
+  optional int64 time_until_next_wakeup_ms = 14;
+  optional int64 time_since_last_wakeup_ms = 15;
+  // Time since the last wakeup was set.
+  optional int64 time_since_last_wakeup_set_ms = 16;
+  optional int64 time_change_event_count = 17;
+  // The current set of user whitelisted apps for device idle mode, meaning
+  // these are allowed to freely schedule alarms. These are app IDs, not UIDs.
+  repeated int32 device_idle_user_whitelist_app_ids = 18;
+
+  repeated AlarmClockMetadataProto next_alarm_clock_metadata = 19;
+
+  repeated BatchProto pending_alarm_batches = 20;
+
+  // List of alarms per uid deferred due to user applied background restrictions
+  // on the source app.
+  repeated AlarmProto pending_user_blocked_background_alarms = 21;
+
+  // When idling mode will end. Will be empty if the device is not currently
+  // idling.
+  optional AlarmProto pending_idle_until = 22;
+
+  // Any alarms that we don't want to run during idle mode. Will be empty if the
+  // device is not currently idling.
+  repeated AlarmProto pending_while_idle_alarms = 23;
+
+  // This is a special alarm that will put the system into idle until it goes
+  // off. The caller has given the time they want this to happen at.
+  optional AlarmProto next_wake_from_idle = 24;
+
+  repeated AlarmProto past_due_non_wakeup_alarms = 25;
+
+  // Number of delayed alarms.
+  optional int32 delayed_alarm_count = 26;
+  // The total amount of time alarms had been delayed. Overlapping alarms are
+  // only counted once (ie. If two alarms were meant to trigger at the same time
+  // but were delayed by 5 seconds, the total time would be 5 seconds).
+  optional int64 total_delay_time_ms = 27;
+  optional int64 max_delay_duration_ms = 28;
+  optional int64 max_non_interactive_duration_ms = 29;
+
+  optional int32 broadcast_ref_count = 30;
+  // Canonical count of (operation.send() - onSendFinished()) and listener
+  // send/complete/timeout invocations.
+  optional int32 pending_intent_send_count = 31;
+  optional int32 pending_intent_finish_count = 32;
+  optional int32 listener_send_count = 33;
+  optional int32 listener_finish_count = 34;
+
+  repeated InFlightProto outstanding_deliveries = 35;
+
+  // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling. It
+  // should be either CosntantsProto.allow_while_idle_short_duration_ms or
+  // ConstantsProto.allow_while_idle_long_duration_ms.
+  optional int64 allow_while_idle_min_duration_ms = 36;
+
+  message LastAllowWhileIdleDispatch {
+    optional int32 uid = 1;
+    // In the 'elapsed' timebase.
+    optional int64 time_ms = 2;
+  }
+  // For each uid, this is the last time we dispatched an "allow while idle"
+  // alarm, used to determine the earliest we can dispatch the next such alarm.
+  repeated LastAllowWhileIdleDispatch last_allow_while_idle_dispatch_times = 37;
+
+  optional com.android.internal.util.LocalLogProto recent_problems = 38;
+
+  message TopAlarm {
+    optional int32 uid = 1;
+    optional string package_name = 2;
+    optional FilterStatsProto filter = 3;
+  }
+  repeated TopAlarm top_alarms = 39;
+
+  message AlarmStat {
+    optional BroadcastStatsProto broadcast = 1;
+    repeated FilterStatsProto filters = 2;
+  }
+  repeated AlarmStat alarm_stats = 40;
+
+  repeated IdleDispatchEntryProto allow_while_idle_dispatches = 41;
+  repeated WakeupEventProto recent_wakeup_history = 42;
+}
+
+// This is a soft wrapper for alarm clock information. It is not representative
+// of an android.app.AlarmManager.AlarmClockInfo object.
+message AlarmClockMetadataProto {
+  optional int32 user = 1;
+  optional bool is_pending_send = 2;
+  // This value is UTC wall clock time in milliseconds, as returned by
+  // System#currentTimeMillis() for example.
+  optional int64 trigger_time_ms = 3;
+}
+
+// A com.android.server.AlarmManagerService.Alarm object.
+message AlarmProto {
+  optional string tag = 1;
+  optional .android.app.AlarmManagerProto.AlarmType type = 2;
+  // How long until the alarm goes off, in the 'elapsed' timebase.
+  optional int64 when_elapsed_ms = 3;
+  optional int64 window_length_ms = 4;
+  optional int64 repeat_interval_ms = 5;
+  optional int32 count = 6;
+  optional int32 flags = 7;
+  optional .android.app.AlarmClockInfoProto alarm_clock = 8;
+  optional .android.app.PendingIntentProto operation = 9;
+  optional string listener = 10;
+}
+
+// A com.android.server.AlarmManagerService.Batch object.
+message BatchProto {
+  // Start time in terms of elapsed realtime.
+  optional int64 start_realtime = 1;
+  // End time in terms of elapsed realtime.
+  optional int64 end_realtime = 2;
+  optional int32 flags = 3;
+  repeated AlarmProto alarms = 4;
+}
+
+// A com.android.server.AlarmManagerService.BroadcastStats object.
+message BroadcastStatsProto {
+  optional int32 uid = 1;
+  optional string package_name = 2;
+  // The total amount of time this broadcast was in flight.
+  optional int64 total_flight_duration_ms = 3;
+  optional int32 count = 4;
+  optional int32 wakeup_count = 5;
+  // The last time this first became active (when nesting changed from 0 to 1)
+  // in terms of elapsed realtime.
+  optional int64 start_time_realtime = 6;
+  // The broadcast is active if nesting > 0.
+  optional int32 nesting = 7;
+}
+
+// A com.android.server.AlarmManagerService.Constants object.
+message ConstantsProto {
+  // Minimum futurity of a new alarm.
+  optional int64 min_futurity_duration_ms = 1;
+  // Minimum alarm recurrence interval.
+  optional int64 min_interval_duration_ms = 2;
+  // Direct alarm listener callback timeout.
+  optional int64 listener_timeout_duration_ms = 3;
+  // Minimum time between ALLOW_WHILE_IDLE alarms when system is not idle.
+  optional int64 allow_while_idle_short_duration_ms = 4;
+  // Minimum time between ALLOW_WHILE_IDLE alarms when system is idling.
+  optional int64 allow_while_idle_long_duration_ms = 5;
+  // BroadcastOptions.setTemporaryAppWhitelistDuration() to use for FLAG_ALLOW_WHILE_IDLE.
+  optional int64 allow_while_idle_whitelist_duration_ms = 6;
+}
+
+// A com.android.server.AlarmManagerService.FilterStats object.
+message FilterStatsProto {
+  optional string tag = 1;
+  // The last time this filter when in flight, in terms of elapsed realtime.
+  optional int64 last_flight_time_realtime = 2;
+  // The total amount of time this filter was in flight.
+  optional int64 total_flight_duration_ms = 3;
+  optional int32 count = 4;
+  optional int32 wakeup_count = 5;
+  // The last time this first became active (when nesting changed from 0 to 1)
+  // in terms of elapsed realtime.
+  optional int64 start_time_realtime = 6;
+  // The filter is active if nesting > 0.
+  optional int32 nesting = 7;
+}
+
+// A com.android.server.AlarmManagerService.IdleDispatchEntry object.
+message IdleDispatchEntryProto {
+  optional int32 uid = 1;
+  optional string pkg = 2;
+  optional string tag = 3;
+  optional string op = 4;
+  // Time when this entry was created, in terms of elapsed realtime.
+  optional int64 entry_creation_realtime = 5;
+  // For a RESCHEDULED op, this is the last time we dispatched an "allow while
+  // idle" alarm for the UID. For a SET op, this is when the alarm was
+  // triggered. Times are in the 'elapsed' timebase.
+  optional int64 arg_realtime = 6;
+}
+
+// A com.android.server.AlarmManagerService.InFlight object.
+message InFlightProto {
+  optional int32 uid = 1;
+  optional string tag = 2;
+  optional int64 when_elapsed_ms = 3;
+  optional .android.app.AlarmManagerProto.AlarmType alarm_type = 4;
+  optional .android.app.PendingIntentProto pending_intent = 5;
+  optional BroadcastStatsProto broadcast_stats = 6;
+  optional FilterStatsProto filter_stats = 7;
+  optional .android.os.WorkSourceProto work_source = 8;
+}
+
+// A com.android.server.AlarmManagerService.WakeupEvent object.
+message WakeupEventProto {
+  optional int32 uid = 1;
+  optional string action = 2;
+  optional int64 when = 3;
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 4c08f62..3904fc9 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -63,6 +63,7 @@
 import android.util.SparseBooleanArray;
 import android.util.SparseLongArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileDescriptor;
@@ -190,7 +191,8 @@
 
     /**
      * For each uid, this is the last time we dispatched an "allow while idle" alarm,
-     * used to determine the earliest we can dispatch the next such alarm.
+     * used to determine the earliest we can dispatch the next such alarm. Times are in the
+     * 'elapsed' timebase.
      */
     final SparseLongArray mLastAllowWhileIdleDispatch = new SparseLongArray();
 
@@ -355,6 +357,22 @@
             TimeUtils.formatDuration(ALLOW_WHILE_IDLE_WHITELIST_DURATION, pw);
             pw.println();
         }
+
+        void dumpProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(ConstantsProto.MIN_FUTURITY_DURATION_MS, MIN_FUTURITY);
+            proto.write(ConstantsProto.MIN_INTERVAL_DURATION_MS, MIN_INTERVAL);
+            proto.write(ConstantsProto.LISTENER_TIMEOUT_DURATION_MS, LISTENER_TIMEOUT);
+            proto.write(ConstantsProto.ALLOW_WHILE_IDLE_SHORT_DURATION_MS,
+                    ALLOW_WHILE_IDLE_SHORT_TIME);
+            proto.write(ConstantsProto.ALLOW_WHILE_IDLE_LONG_DURATION_MS,
+                    ALLOW_WHILE_IDLE_LONG_TIME);
+            proto.write(ConstantsProto.ALLOW_WHILE_IDLE_WHITELIST_DURATION_MS,
+                    ALLOW_WHILE_IDLE_WHITELIST_DURATION);
+
+            proto.end(token);
+        }
     }
 
     final Constants mConstants;
@@ -632,6 +650,20 @@
             b.append('}');
             return b.toString();
         }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId, long nowElapsed,
+                long nowRTC) {
+            final long token = proto.start(fieldId);
+
+            proto.write(BatchProto.START_REALTIME, start);
+            proto.write(BatchProto.END_REALTIME, end);
+            proto.write(BatchProto.FLAGS, flags);
+            for (Alarm a : alarms) {
+                a.writeToProto(proto, BatchProto.ALARMS, nowElapsed, nowRTC);
+            }
+
+            proto.end(token);
+        }
     }
 
     static class BatchTimeOrder implements Comparator<Batch> {
@@ -1007,6 +1039,29 @@
                     + ", alarmType=" + mAlarmType
                     + "}";
         }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(InFlightProto.UID, mUid);
+            proto.write(InFlightProto.TAG, mTag);
+            proto.write(InFlightProto.WHEN_ELAPSED_MS, mWhenElapsed);
+            proto.write(InFlightProto.ALARM_TYPE, mAlarmType);
+            if (mPendingIntent != null) {
+                mPendingIntent.writeToProto(proto, InFlightProto.PENDING_INTENT);
+            }
+            if (mBroadcastStats != null) {
+                mBroadcastStats.writeToProto(proto, InFlightProto.BROADCAST_STATS);
+            }
+            if (mFilterStats != null) {
+                mFilterStats.writeToProto(proto, InFlightProto.FILTER_STATS);
+            }
+            if (mWorkSource != null) {
+                mWorkSource.writeToProto(proto, InFlightProto.WORK_SOURCE);
+            }
+
+            proto.end(token);
+        }
     }
 
     static final class FilterStats {
@@ -1037,6 +1092,20 @@
                     + ", nesting=" + nesting
                     + "}";
         }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(FilterStatsProto.TAG, mTag);
+            proto.write(FilterStatsProto.LAST_FLIGHT_TIME_REALTIME, lastTime);
+            proto.write(FilterStatsProto.TOTAL_FLIGHT_DURATION_MS, aggregateTime);
+            proto.write(FilterStatsProto.COUNT, count);
+            proto.write(FilterStatsProto.WAKEUP_COUNT, numWakeup);
+            proto.write(FilterStatsProto.START_TIME_REALTIME, startTime);
+            proto.write(FilterStatsProto.NESTING, nesting);
+
+            proto.end(token);
+        }
     }
 
     static final class BroadcastStats {
@@ -1067,6 +1136,20 @@
                     + ", nesting=" + nesting
                     + "}";
         }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            final long token = proto.start(fieldId);
+
+            proto.write(BroadcastStatsProto.UID, mUid);
+            proto.write(BroadcastStatsProto.PACKAGE_NAME, mPackageName);
+            proto.write(BroadcastStatsProto.TOTAL_FLIGHT_DURATION_MS, aggregateTime);
+            proto.write(BroadcastStatsProto.COUNT, count);
+            proto.write(BroadcastStatsProto.WAKEUP_COUNT, numWakeup);
+            proto.write(BroadcastStatsProto.START_TIME_REALTIME, startTime);
+            proto.write(BroadcastStatsProto.NESTING, nesting);
+
+            proto.end(token);
+        }
     }
 
     final SparseArray<ArrayMap<String, BroadcastStats>> mBroadcastStats
@@ -1128,14 +1211,14 @@
                 | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
         mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent,
                 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL);
-        
+
         // now that we have initied the driver schedule the alarm
         mClockReceiver = new ClockReceiver();
         mClockReceiver.scheduleTimeTickEvent();
         mClockReceiver.scheduleDateChangedEvent();
         mInteractiveStateReceiver = new InteractiveStateReceiver();
         mUninstallReceiver = new UninstallReceiver();
-        
+
         if (mNativeData != 0) {
             AlarmThread waitThread = new AlarmThread();
             waitThread.start();
@@ -1568,7 +1651,12 @@
         @Override
         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
-            dumpImpl(pw);
+
+            if (args.length > 0 && "--proto".equals(args[0])) {
+                dumpProto(fd);
+            } else {
+                dumpImpl(pw);
+            }
         }
     };
 
@@ -1681,7 +1769,7 @@
                 pw.print("      Idling until: ");
                 if (mPendingIdleUntil != null) {
                     pw.println(mPendingIdleUntil);
-                    mPendingIdleUntil.dump(pw, "        ", nowRTC, nowELAPSED, sdf);
+                    mPendingIdleUntil.dump(pw, "        ", nowELAPSED, nowRTC, sdf);
                 } else {
                     pw.println("null");
                 }
@@ -1691,7 +1779,7 @@
             if (mNextWakeFromIdle != null) {
                 pw.println();
                 pw.print("  Next wake from idle: "); pw.println(mNextWakeFromIdle);
-                mNextWakeFromIdle.dump(pw, "    ", nowRTC, nowELAPSED, sdf);
+                mNextWakeFromIdle.dump(pw, "    ", nowELAPSED, nowRTC, sdf);
             }
 
             pw.println();
@@ -1760,6 +1848,7 @@
                 }
             };
             int len = 0;
+            // Get the top 10 FilterStats, ordered by aggregateTime.
             for (int iu=0; iu<mBroadcastStats.size(); iu++) {
                 ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
                 for (int ip=0; ip<uidStats.size(); ip++) {
@@ -1886,6 +1975,244 @@
         }
     }
 
+    void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+
+        synchronized (mLock) {
+            final long nowRTC = System.currentTimeMillis();
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            proto.write(AlarmManagerServiceProto.CURRENT_TIME, nowRTC);
+            proto.write(AlarmManagerServiceProto.ELAPSED_REALTIME, nowElapsed);
+            proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_CLOCK_TIME,
+                    mLastTimeChangeClockTime);
+            proto.write(AlarmManagerServiceProto.LAST_TIME_CHANGE_REALTIME,
+                    mLastTimeChangeRealtime);
+
+            mConstants.dumpProto(proto, AlarmManagerServiceProto.SETTINGS);
+
+            final int foregroundUidsSize = mForegroundUids.size();
+            for (int i = 0; i < foregroundUidsSize; i++) {
+                if (mForegroundUids.valueAt(i)) {
+                    proto.write(AlarmManagerServiceProto.FOREGROUND_UIDS, mForegroundUids.keyAt(i));
+                }
+            }
+            for (String pkg : mForcedAppStandbyPackages) {
+                proto.write(AlarmManagerServiceProto.FORCED_APP_STANDBY_PACKAGES, pkg);
+            }
+
+            proto.write(AlarmManagerServiceProto.IS_INTERACTIVE, mInteractive);
+            if (!mInteractive) {
+                // Durations
+                proto.write(AlarmManagerServiceProto.TIME_SINCE_NON_INTERACTIVE_MS,
+                        nowElapsed - mNonInteractiveStartTime);
+                proto.write(AlarmManagerServiceProto.MAX_WAKEUP_DELAY_MS,
+                        currentNonWakeupFuzzLocked(nowElapsed));
+                proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_DISPATCH_MS,
+                        nowElapsed - mLastAlarmDeliveryTime);
+                proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_DELIVERY_MS,
+                        nowElapsed - mNextNonWakeupDeliveryTime);
+            }
+
+            proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_NON_WAKEUP_ALARM_MS,
+                    mNextNonWakeup - nowElapsed);
+            proto.write(AlarmManagerServiceProto.TIME_UNTIL_NEXT_WAKEUP_MS,
+                    mNextWakeup - nowElapsed);
+            proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_MS,
+                    nowElapsed - mLastWakeup);
+            proto.write(AlarmManagerServiceProto.TIME_SINCE_LAST_WAKEUP_SET_MS,
+                    nowElapsed - mLastWakeupSet);
+            proto.write(AlarmManagerServiceProto.TIME_CHANGE_EVENT_COUNT, mNumTimeChanged);
+            for (int i : mDeviceIdleUserWhitelist) {
+                proto.write(AlarmManagerServiceProto.DEVICE_IDLE_USER_WHITELIST_APP_IDS, i);
+            }
+
+            final TreeSet<Integer> users = new TreeSet<>();
+            final int nextAlarmClockForUserSize = mNextAlarmClockForUser.size();
+            for (int i = 0; i < nextAlarmClockForUserSize; i++) {
+                users.add(mNextAlarmClockForUser.keyAt(i));
+            }
+            final int pendingSendNextAlarmClockChangedForUserSize =
+                    mPendingSendNextAlarmClockChangedForUser.size();
+            for (int i = 0; i < pendingSendNextAlarmClockChangedForUserSize; i++) {
+                users.add(mPendingSendNextAlarmClockChangedForUser.keyAt(i));
+            }
+            for (int user : users) {
+                final AlarmManager.AlarmClockInfo next = mNextAlarmClockForUser.get(user);
+                final long time = next != null ? next.getTriggerTime() : 0;
+                final boolean pendingSend = mPendingSendNextAlarmClockChangedForUser.get(user);
+                final long aToken = proto.start(AlarmManagerServiceProto.NEXT_ALARM_CLOCK_METADATA);
+                proto.write(AlarmClockMetadataProto.USER, user);
+                proto.write(AlarmClockMetadataProto.IS_PENDING_SEND, pendingSend);
+                proto.write(AlarmClockMetadataProto.TRIGGER_TIME_MS, time);
+                proto.end(aToken);
+            }
+            for (Batch b : mAlarmBatches) {
+                b.writeToProto(proto, AlarmManagerServiceProto.PENDING_ALARM_BATCHES,
+                        nowElapsed, nowRTC);
+            }
+            for (int i = 0; i < mPendingBackgroundAlarms.size(); i++) {
+                final ArrayList<Alarm> blockedAlarms = mPendingBackgroundAlarms.valueAt(i);
+                if (blockedAlarms != null) {
+                    for (Alarm a : blockedAlarms) {
+                        a.writeToProto(proto,
+                                AlarmManagerServiceProto.PENDING_USER_BLOCKED_BACKGROUND_ALARMS,
+                                nowElapsed, nowRTC);
+                    }
+                }
+            }
+            if (mPendingIdleUntil != null) {
+                mPendingIdleUntil.writeToProto(
+                        proto, AlarmManagerServiceProto.PENDING_IDLE_UNTIL, nowElapsed, nowRTC);
+            }
+            for (Alarm a : mPendingWhileIdleAlarms) {
+                a.writeToProto(proto, AlarmManagerServiceProto.PENDING_WHILE_IDLE_ALARMS,
+                        nowElapsed, nowRTC);
+            }
+            if (mNextWakeFromIdle != null) {
+                mNextWakeFromIdle.writeToProto(proto, AlarmManagerServiceProto.NEXT_WAKE_FROM_IDLE,
+                        nowElapsed, nowRTC);
+            }
+
+            for (Alarm a : mPendingNonWakeupAlarms) {
+                a.writeToProto(proto, AlarmManagerServiceProto.PAST_DUE_NON_WAKEUP_ALARMS,
+                        nowElapsed, nowRTC);
+            }
+
+            proto.write(AlarmManagerServiceProto.DELAYED_ALARM_COUNT, mNumDelayedAlarms);
+            proto.write(AlarmManagerServiceProto.TOTAL_DELAY_TIME_MS, mTotalDelayTime);
+            proto.write(AlarmManagerServiceProto.MAX_DELAY_DURATION_MS, mMaxDelayTime);
+            proto.write(AlarmManagerServiceProto.MAX_NON_INTERACTIVE_DURATION_MS,
+                    mNonInteractiveTime);
+
+            proto.write(AlarmManagerServiceProto.BROADCAST_REF_COUNT, mBroadcastRefCount);
+            proto.write(AlarmManagerServiceProto.PENDING_INTENT_SEND_COUNT, mSendCount);
+            proto.write(AlarmManagerServiceProto.PENDING_INTENT_FINISH_COUNT, mSendFinishCount);
+            proto.write(AlarmManagerServiceProto.LISTENER_SEND_COUNT, mListenerCount);
+            proto.write(AlarmManagerServiceProto.LISTENER_FINISH_COUNT, mListenerFinishCount);
+
+            for (InFlight f : mInFlight) {
+                f.writeToProto(proto, AlarmManagerServiceProto.OUTSTANDING_DELIVERIES);
+            }
+
+            proto.write(AlarmManagerServiceProto.ALLOW_WHILE_IDLE_MIN_DURATION_MS,
+                    mAllowWhileIdleMinTime);
+            for (int i = 0; i < mLastAllowWhileIdleDispatch.size(); ++i) {
+                final long token = proto.start(
+                        AlarmManagerServiceProto.LAST_ALLOW_WHILE_IDLE_DISPATCH_TIMES);
+                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.UID,
+                        mLastAllowWhileIdleDispatch.keyAt(i));
+                proto.write(AlarmManagerServiceProto.LastAllowWhileIdleDispatch.TIME_MS,
+                        mLastAllowWhileIdleDispatch.valueAt(i));
+                proto.end(token);
+            }
+
+            mLog.writeToProto(proto, AlarmManagerServiceProto.RECENT_PROBLEMS);
+
+            final FilterStats[] topFilters = new FilterStats[10];
+            final Comparator<FilterStats> comparator = new Comparator<FilterStats>() {
+                @Override
+                public int compare(FilterStats lhs, FilterStats rhs) {
+                    if (lhs.aggregateTime < rhs.aggregateTime) {
+                        return 1;
+                    } else if (lhs.aggregateTime > rhs.aggregateTime) {
+                        return -1;
+                    }
+                    return 0;
+                }
+            };
+            int len = 0;
+            // Get the top 10 FilterStats, ordered by aggregateTime.
+            for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
+                ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+                for (int ip = 0; ip < uidStats.size(); ++ip) {
+                    BroadcastStats bs = uidStats.valueAt(ip);
+                    for (int is = 0; is < bs.filterStats.size(); ++is) {
+                        FilterStats fs = bs.filterStats.valueAt(is);
+                        int pos = len > 0
+                                ? Arrays.binarySearch(topFilters, 0, len, fs, comparator) : 0;
+                        if (pos < 0) {
+                            pos = -pos - 1;
+                        }
+                        if (pos < topFilters.length) {
+                            int copylen = topFilters.length - pos - 1;
+                            if (copylen > 0) {
+                                System.arraycopy(topFilters, pos, topFilters, pos+1, copylen);
+                            }
+                            topFilters[pos] = fs;
+                            if (len < topFilters.length) {
+                                len++;
+                            }
+                        }
+                    }
+                }
+            }
+            for (int i = 0; i < len; ++i) {
+                final long token = proto.start(AlarmManagerServiceProto.TOP_ALARMS);
+                FilterStats fs = topFilters[i];
+
+                proto.write(AlarmManagerServiceProto.TopAlarm.UID, fs.mBroadcastStats.mUid);
+                proto.write(AlarmManagerServiceProto.TopAlarm.PACKAGE_NAME,
+                        fs.mBroadcastStats.mPackageName);
+                fs.writeToProto(proto, AlarmManagerServiceProto.TopAlarm.FILTER);
+
+                proto.end(token);
+            }
+
+            final ArrayList<FilterStats> tmpFilters = new ArrayList<FilterStats>();
+            for (int iu = 0; iu < mBroadcastStats.size(); ++iu) {
+                ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(iu);
+                for (int ip = 0; ip < uidStats.size(); ++ip) {
+                    final long token = proto.start(AlarmManagerServiceProto.ALARM_STATS);
+
+                    BroadcastStats bs = uidStats.valueAt(ip);
+                    bs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.BROADCAST);
+
+                    // uidStats is an ArrayMap, which we can't sort.
+                    tmpFilters.clear();
+                    for (int is = 0; is < bs.filterStats.size(); ++is) {
+                        tmpFilters.add(bs.filterStats.valueAt(is));
+                    }
+                    Collections.sort(tmpFilters, comparator);
+                    for (FilterStats fs : tmpFilters) {
+                        fs.writeToProto(proto, AlarmManagerServiceProto.AlarmStat.FILTERS);
+                    }
+
+                    proto.end(token);
+                }
+            }
+
+            if (RECORD_DEVICE_IDLE_ALARMS) {
+                for (int i = 0; i < mAllowWhileIdleDispatches.size(); i++) {
+                    IdleDispatchEntry ent = mAllowWhileIdleDispatches.get(i);
+                    final long token = proto.start(
+                            AlarmManagerServiceProto.ALLOW_WHILE_IDLE_DISPATCHES);
+
+                    proto.write(IdleDispatchEntryProto.UID, ent.uid);
+                    proto.write(IdleDispatchEntryProto.PKG, ent.pkg);
+                    proto.write(IdleDispatchEntryProto.TAG, ent.tag);
+                    proto.write(IdleDispatchEntryProto.OP, ent.op);
+                    proto.write(IdleDispatchEntryProto.ENTRY_CREATION_REALTIME,
+                            ent.elapsedRealtime);
+                    proto.write(IdleDispatchEntryProto.ARG_REALTIME, ent.argRealtime);
+
+                    proto.end(token);
+                }
+            }
+
+            if (WAKEUP_STATS) {
+                for (WakeupEvent event : mRecentWakeups) {
+                    final long token = proto.start(AlarmManagerServiceProto.RECENT_WAKEUP_HISTORY);
+                    proto.write(WakeupEventProto.UID, event.uid);
+                    proto.write(WakeupEventProto.ACTION, event.action);
+                    proto.write(WakeupEventProto.WHEN, event.when);
+                    proto.end(token);
+                }
+            }
+        }
+
+        proto.flush();
+    }
+
     private void logBatchesLocked(SimpleDateFormat sdf) {
         ByteArrayOutputStream bs = new ByteArrayOutputStream(2048);
         PrintWriter pw = new PrintWriter(bs);
@@ -2328,24 +2655,24 @@
                 alarmSeconds = when / 1000;
                 alarmNanoseconds = (when % 1000) * 1000 * 1000;
             }
-            
+
             set(mNativeData, type, alarmSeconds, alarmNanoseconds);
         } else {
             Message msg = Message.obtain();
             msg.what = ALARM_EVENT;
-            
+
             mHandler.removeMessages(ALARM_EVENT);
             mHandler.sendMessageAtTime(msg, when);
         }
     }
 
     private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
-            String prefix, String label, long nowRTC, long nowELAPSED, SimpleDateFormat sdf) {
+            String prefix, String label, long nowELAPSED, long nowRTC, SimpleDateFormat sdf) {
         for (int i=list.size()-1; i>=0; i--) {
             Alarm a = list.get(i);
             pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
                     pw.print(": "); pw.println(a);
-            a.dump(pw, prefix + "  ", nowRTC, nowELAPSED, sdf);
+            a.dump(pw, prefix + "  ", nowELAPSED, nowRTC, sdf);
         }
     }
 
@@ -2355,8 +2682,6 @@
         case RTC_WAKEUP : return "RTC_WAKEUP";
         case ELAPSED_REALTIME : return "ELAPSED";
         case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
-        default:
-            break;
         }
         return "--unknown--";
     }
@@ -2368,7 +2693,7 @@
             final String label = labelForType(a.type);
             pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
                     pw.print(": "); pw.println(a);
-            a.dump(pw, prefix + "  ", nowRTC, nowELAPSED, sdf);
+            a.dump(pw, prefix + "  ", nowELAPSED, nowRTC, sdf);
         }
     }
 
@@ -2533,7 +2858,7 @@
             return 0;
         }
     }
-    
+
     private static class Alarm {
         public final int type;
         public final long origWhen;
@@ -2627,7 +2952,7 @@
             return sb.toString();
         }
 
-        public void dump(PrintWriter pw, String prefix, long nowRTC, long nowELAPSED,
+        public void dump(PrintWriter pw, String prefix, long nowELAPSED, long nowRTC,
                 SimpleDateFormat sdf) {
             final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
             pw.print(prefix); pw.print("tag="); pw.println(statsTag);
@@ -2656,6 +2981,30 @@
                 pw.print(prefix); pw.print("listener="); pw.println(listener.asBinder());
             }
         }
+
+        public void writeToProto(ProtoOutputStream proto, long fieldId, long nowElapsed,
+                long nowRTC) {
+            final long token = proto.start(fieldId);
+
+            proto.write(AlarmProto.TAG, statsTag);
+            proto.write(AlarmProto.TYPE, type);
+            proto.write(AlarmProto.WHEN_ELAPSED_MS, whenElapsed - nowElapsed);
+            proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength);
+            proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval);
+            proto.write(AlarmProto.COUNT, count);
+            proto.write(AlarmProto.FLAGS, flags);
+            if (alarmClock != null) {
+                alarmClock.writeToProto(proto, AlarmProto.ALARM_CLOCK);
+            }
+            if (operation != null) {
+                operation.writeToProto(proto, AlarmProto.OPERATION);
+            }
+            if (listener != null) {
+                proto.write(AlarmProto.LISTENER, listener.asBinder().toString());
+            }
+
+            proto.end(token);
+        }
     }
 
     void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
@@ -2752,7 +3101,7 @@
         {
             super("AlarmManager");
         }
-        
+
         public void run()
         {
             ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
@@ -2918,10 +3267,10 @@
         public static final int SEND_NEXT_ALARM_CLOCK_CHANGED = 2;
         public static final int LISTENER_TIMEOUT = 3;
         public static final int REPORT_ALARMS_ACTIVE = 4;
-        
+
         public AlarmHandler() {
         }
-        
+
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case ALARM_EVENT: {
@@ -2969,7 +3318,7 @@
             }
         }
     }
-    
+
     class ClockReceiver extends BroadcastReceiver {
         public ClockReceiver() {
             IntentFilter filter = new IntentFilter();
@@ -2977,7 +3326,7 @@
             filter.addAction(Intent.ACTION_DATE_CHANGED);
             getContext().registerReceiver(this, filter);
         }
-        
+
         @Override
         public void onReceive(Context context, Intent intent) {
             if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
@@ -2996,7 +3345,7 @@
                 scheduleDateChangedEvent();
             }
         }
-        
+
         public void scheduleTimeTickEvent() {
             final long currentTime = System.currentTimeMillis();
             final long nextTime = 60000 * ((currentTime / 60000) + 1);
@@ -3026,7 +3375,7 @@
                     Process.myUid(), "android");
         }
     }
-    
+
     class InteractiveStateReceiver extends BroadcastReceiver {
         public InteractiveStateReceiver() {
             IntentFilter filter = new IntentFilter();
@@ -3059,7 +3408,7 @@
             sdFilter.addAction(Intent.ACTION_UID_REMOVED);
             getContext().registerReceiver(this, sdFilter);
         }
-        
+
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {