Implement Activity Manager Dumpsys --service option

Bug: 66729158
Test: out/host/linux-x86/bin/incident_report -w amservices
Change-Id: I72015b9744bc8028001306283f169fca4797c700
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d5d95fb..42c1347 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -68,6 +68,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.SparseArray;
+import android.util.proto.ProtoOutputStream;
 import android.view.Gravity;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -2447,6 +2448,30 @@
         notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, ai);
     }
 
+    /**
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(NotificationProto.CHANNEL_ID, getChannelId());
+        proto.write(NotificationProto.HAS_TICKER_TEXT, this.tickerText != null);
+        proto.write(NotificationProto.FLAGS, this.flags);
+        proto.write(NotificationProto.COLOR, this.color);
+        proto.write(NotificationProto.CATEGORY, this.category);
+        proto.write(NotificationProto.GROUP_KEY, this.mGroupKey);
+        proto.write(NotificationProto.SORT_KEY, this.mSortKey);
+        if (this.actions != null) {
+            proto.write(NotificationProto.ACTION_LENGTH, this.actions.length);
+        }
+        if (this.visibility >= VISIBILITY_SECRET && this.visibility <= VISIBILITY_PUBLIC) {
+            proto.write(NotificationProto.VISIBILITY, this.visibility);
+        }
+        if (publicVersion != null) {
+            publicVersion.writeToProto(proto, NotificationProto.PUBLIC_VERSION);
+        }
+        proto.end(token);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
diff --git a/core/java/android/util/proto/ProtoUtils.java b/core/java/android/util/proto/ProtoUtils.java
index 449baca..85b7ec8 100644
--- a/core/java/android/util/proto/ProtoUtils.java
+++ b/core/java/android/util/proto/ProtoUtils.java
@@ -17,6 +17,7 @@
 package android.util.proto;
 
 import android.util.AggStats;
+import android.util.Duration;
 
 /**
  * This class contains a list of helper functions to write common proto in
@@ -36,4 +37,15 @@
         proto.write(AggStats.MAX, max);
         proto.end(aggStatsToken);
     }
+
+    /**
+     * Dump Duration to ProtoOutputStream
+     * @hide
+     */
+    public static void toDuration(ProtoOutputStream proto, long fieldId, long startMs, long endMs) {
+        final long token = proto.start(fieldId);
+        proto.write(Duration.START_MS, startMs);
+        proto.write(Duration.END_MS, endMs);
+        proto.end(token);
+    }
 }
diff --git a/core/proto/android/app/notification.proto b/core/proto/android/app/notification.proto
new file mode 100644
index 0000000..5376b0e
--- /dev/null
+++ b/core/proto/android/app/notification.proto
@@ -0,0 +1,45 @@
+/*
+ * 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_package = "android.app";
+option java_multiple_files = true;
+
+package android.app;
+
+/**
+ * An android.app.Notification object.
+ * Deprecated fields are not included in the proto.
+ */
+message NotificationProto {
+    optional string channel_id = 1;
+    optional bool has_ticker_text = 2;
+    optional int32 flags = 3;
+    optional int32 color = 4;
+    optional string category = 5;
+    optional string group_key = 6;
+    optional string sort_key = 7;
+    optional int32 action_length = 8;
+
+    // If this field is not set, then the value is unknown.
+    enum Visibility {
+        VISIBILITY_SECRET = -1;
+        VISIBILITY_PRIVATE = 0;
+        VISIBILITY_PUBLIC = 1;
+    }
+    optional Visibility visibility = 9;
+    optional NotificationProto public_version = 10;
+}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index f68f3a4..6c5206c 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -143,7 +143,11 @@
         (section).args = "activity --proto broadcasts"
     ];
 
-    optional com.android.server.am.proto.ServiceProto amservices = 3014;
+    optional com.android.server.am.proto.ActiveServicesProto amservices = 3014 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "activity --proto service"
+    ];
+
     optional com.android.server.am.proto.ProcessProto amprocesses = 3015;
 
     optional com.android.server.AlarmManagerServiceProto alarm = 3016 [
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index c57cb72..889842c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -15,11 +15,14 @@
  */
 
 syntax = "proto2";
+
+import "frameworks/base/core/proto/android/app/notification.proto";
 import "frameworks/base/core/proto/android/content/intent.proto";
 import "frameworks/base/core/proto/android/server/intentresolver.proto";
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
 import "frameworks/base/core/proto/android/os/looper.proto";
+import "frameworks/base/core/proto/android/util/common.proto";
 
 package com.android.server.am.proto;
 
@@ -30,11 +33,12 @@
 
   optional BroadcastProto broadcasts = 2;
 
-  optional ServiceProto services = 3;
+  optional ActiveServicesProto services = 3;
 
   optional ProcessProto processes = 4;
 }
 
+// "dumpsys activity --proto activities"
 message ActivityStackSupervisorProto {
   optional .com.android.server.wm.proto.ConfigurationContainerProto configuration_container = 1;
   repeated ActivityDisplayProto displays = 2;
@@ -90,6 +94,7 @@
   optional bool keyguard_occluded = 2;
 }
 
+// "dumpsys activity --proto broadcasts"
 message BroadcastProto {
   repeated ReceiverListProto  receiver_list = 1;
 
@@ -164,10 +169,158 @@
   repeated StickyAction actions = 2;
 }
 
-message ServiceProto {
-  // TODO: "dumpsys activity --proto services"
+// "dumpsys activity --proto service"
+message ActiveServicesProto {
+
+  message ServicesByUser {
+    optional int32 user_id = 1;
+    repeated ServiceRecordProto service_records = 2;
+  }
+  repeated ServicesByUser services_by_users = 1;
 }
 
+// corresponds to ActivityManagerService.GrantUri Java class
+message GrantUriProto {
+  optional int32 source_user_id = 1;
+  optional string uri = 2;
+}
+
+message NeededUriGrantsProto {
+  optional string target_package = 1;
+  optional int32 target_uid = 2;
+  optional int32 flags = 3;
+
+  repeated GrantUriProto grants = 4;
+}
+
+message UriPermissionOwnerProto {
+  optional string owner = 1;
+  repeated GrantUriProto read_perms = 2;
+  repeated GrantUriProto write_perms = 3;
+}
+
+message ServiceRecordProto {
+  optional string short_name = 1;
+  optional string hex_hash = 2;
+  optional bool is_running = 3; // false if the application service is null
+  optional int32 pid = 4;
+  optional .android.content.IntentProto intent = 5;
+  optional string package_name = 6;
+  optional string process_name = 7;
+  optional string permission = 8;
+
+  message AppInfo {
+    optional string base_dir = 1;
+    optional string res_dir = 2;
+    optional string data_dir = 3;
+  }
+  optional AppInfo appinfo = 9;
+  optional ProcessRecordProto app = 10;
+  optional ProcessRecordProto isolated_proc = 11;
+  optional bool whitelist_manager = 12;
+  optional bool delayed = 13;
+
+  message Foreground {
+    optional int32 id = 1;
+    optional .android.app.NotificationProto notification = 2;
+  }
+  optional Foreground foreground = 14;
+
+  optional .android.util.Duration create_real_time = 15;
+  optional .android.util.Duration starting_bg_timeout = 16;
+  optional .android.util.Duration last_activity_time = 17;
+  optional .android.util.Duration restart_time = 18;
+  optional bool created_from_fg = 19;
+
+  // variables used to track states related to service start
+  message Start {
+    optional bool start_requested = 1;
+    optional bool delayed_stop = 2;
+    optional bool stop_if_killed = 3;
+    optional bool call_start = 4;
+    optional int32 last_start_id = 5;
+  }
+  optional Start start = 20;
+
+  message ExecuteNesting {
+    optional int32 execute_nesting = 1;
+    optional bool execute_fg = 2;
+    optional .android.util.Duration executing_start = 3;
+  }
+  optional ExecuteNesting execute = 21;
+
+  optional .android.util.Duration destory_time = 22;
+
+  message Crash {
+    optional int32 restart_count = 1;
+    optional .android.util.Duration restart_delay = 2;
+    optional .android.util.Duration next_restart_time = 3;
+    optional int32 crash_count = 4;
+  }
+  optional Crash crash = 23;
+
+  message StartItemProto {
+    optional int32 id = 1;
+    optional .android.util.Duration duration = 2;
+    optional int32 delivery_count = 3;
+    optional int32 done_executing_count = 4;
+    optional .android.content.IntentProto intent = 5;
+    optional NeededUriGrantsProto needed_grants = 6;
+    optional UriPermissionOwnerProto uri_permissions = 7;
+  }
+  repeated StartItemProto delivered_starts = 24;
+  repeated StartItemProto pending_starts = 25;
+
+  repeated IntentBindRecordProto bindings = 26;
+  repeated ConnectionRecordProto connections = 27;
+}
+
+message ConnectionRecordProto {
+  optional string hex_hash = 1;
+  optional int32 user_id = 2;
+
+  enum Flag {
+    AUTO_CREATE = 0;
+    DEBUG_UNBIND = 1;
+    NOT_FG = 2;
+    IMPORTANT_BG = 3;
+    ABOVE_CLIENT = 4;
+    ALLOW_OOM_MANAGEMENT = 5;
+    WAIVE_PRIORITY = 6;
+    IMPORTANT = 7;
+    ADJUST_WITH_ACTIVITY = 8;
+    FG_SERVICE_WHILE_WAKE = 9;
+    FG_SERVICE = 10;
+    TREAT_LIKE_ACTIVITY = 11;
+    VISIBLE = 12;
+    SHOWING_UI = 13;
+    NOT_VISIBLE = 14;
+    DEAD = 15;
+  }
+  repeated Flag flags = 3;
+  optional string service_name = 4;
+  optional string conn_hex_hash = 5;
+}
+
+message AppBindRecordProto {
+  optional string hex_hash = 1;
+  optional ProcessRecordProto client = 2;
+  repeated ConnectionRecordProto connections = 3;
+}
+
+message IntentBindRecordProto {
+  optional string hex_hash = 1;
+  optional bool is_create = 2;
+  optional .android.content.IntentProto intent = 3;
+  optional string binder = 4;
+  optional bool requested = 5;
+  optional bool received = 6;
+  optional bool has_bound = 7;
+  optional bool do_rebind = 8;
+
+  repeated AppBindRecordProto apps = 9;
+}
+
+// TODO: "dumpsys activity --proto processes"
 message ProcessProto {
-  // TODO: "dumpsys activity --proto processes"
 }
diff --git a/core/proto/android/util/common.proto b/core/proto/android/util/common.proto
index 429c3cad..308ef70 100644
--- a/core/proto/android/util/common.proto
+++ b/core/proto/android/util/common.proto
@@ -30,3 +30,13 @@
 
     optional int64 max = 3;
 }
+
+/**
+ * Very basic data structure to represent Duration.
+ */
+message Duration {
+
+    optional int64 start_ms = 1;
+
+    optional int64 end_ms = 2;
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 701c574..3cd2f6a 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -58,6 +58,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.server.am.ActivityManagerService.ItemMatcher;
 import com.android.server.am.ActivityManagerService.NeededUriGrants;
+import com.android.server.am.proto.ActiveServicesProto;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
@@ -85,6 +86,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
 import android.webkit.WebViewZygote;
 
 public final class ActiveServices {
@@ -633,7 +635,7 @@
                         sb.append("Stopping service due to app idle: ");
                         UserHandle.formatUid(sb, service.appInfo.uid);
                         sb.append(" ");
-                        TimeUtils.formatDuration(service.createTime
+                        TimeUtils.formatDuration(service.createRealTime
                                 - SystemClock.elapsedRealtime(), sb);
                         sb.append(" ");
                         sb.append(compName);
@@ -3220,7 +3222,7 @@
         info.uid = r.appInfo.uid;
         info.process = r.processName;
         info.foreground = r.isForeground;
-        info.activeSince = r.createTime;
+        info.activeSince = r.createRealTime;
         info.started = r.startRequested;
         info.clientCount = r.connections.size();
         info.crashCount = r.crashCount;
@@ -3574,7 +3576,7 @@
                 pw.print("    app=");
                 pw.println(r.app);
                 pw.print("    created=");
-                TimeUtils.formatDuration(r.createTime, nowReal, pw);
+                TimeUtils.formatDuration(r.createRealTime, nowReal, pw);
                 pw.print(" started=");
                 pw.print(r.startRequested);
                 pw.print(" connections=");
@@ -3840,6 +3842,26 @@
         return new ServiceDumper(fd, pw, args, opti, dumpAll, dumpPackage);
     }
 
+    protected void writeToProto(ProtoOutputStream proto) {
+        synchronized (mAm) {
+            int[] users = mAm.mUserController.getUsers();
+            for (int user : users) {
+                ServiceMap smap = mServiceMap.get(user);
+                if (smap == null) {
+                    continue;
+                }
+                long token = proto.start(ActiveServicesProto.SERVICES_BY_USERS);
+                proto.write(ActiveServicesProto.ServicesByUser.USER_ID, user);
+                ArrayMap<ComponentName, ServiceRecord> alls = smap.mServicesByName;
+                for (int i=0; i<alls.size(); i++) {
+                    alls.valueAt(i).writeToProto(proto,
+                            ActiveServicesProto.ServicesByUser.SERVICE_RECORDS);
+                }
+                proto.end(token);
+            }
+        }
+    }
+
     /**
      * There are three ways to call this:
      *  - no service specified: dump all the services
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fe5c789..b3b831e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -415,6 +415,8 @@
 import com.android.server.am.ActivityStack.ActivityState;
 import com.android.server.am.proto.ActivityManagerServiceProto;
 import com.android.server.am.proto.BroadcastProto;
+import com.android.server.am.proto.GrantUriProto;
+import com.android.server.am.proto.NeededUriGrantsProto;
 import com.android.server.am.proto.StickyBroadcastProto;
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
@@ -1191,6 +1193,13 @@
             return result;
         }
 
+        public void writeToProto(ProtoOutputStream proto, long fieldId) {
+            long token = proto.start(fieldId);
+            proto.write(GrantUriProto.URI, uri.toString());
+            proto.write(GrantUriProto.SOURCE_USER_ID, sourceUserId);
+            proto.end(token);
+        }
+
         public static GrantUri resolve(int defaultSourceUserHandle, Uri uri) {
             return new GrantUri(ContentProvider.getUserIdFromUri(uri, defaultSourceUserHandle),
                     ContentProvider.getUriWithoutUserId(uri), false);
@@ -9044,6 +9053,19 @@
             this.targetUid = targetUid;
             this.flags = flags;
         }
+
+        void writeToProto(ProtoOutputStream proto, long fieldId) {
+            long token = proto.start(fieldId);
+            proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg);
+            proto.write(NeededUriGrantsProto.TARGET_UID, targetUid);
+            proto.write(NeededUriGrantsProto.FLAGS, flags);
+
+            final int N = this.size();
+            for (int i=0; i<N; i++) {
+                this.get(i).writeToProto(proto, NeededUriGrantsProto.GRANTS);
+            }
+            proto.end(token);
+        }
     }
 
     /**
@@ -14939,6 +14961,8 @@
                     pw.println("No providers match: " + name);
                     pw.println("Use -h for help.");
                 }
+            } else if ("service".equals(cmd)) {
+                mServices.writeToProto(proto);
             } else {
                 // default option, dump everything, output is ActivityManagerServiceProto
                 synchronized (this) {
@@ -14949,6 +14973,10 @@
                     long broadcastToken = proto.start(ActivityManagerServiceProto.BROADCASTS);
                     writeBroadcastsToProtoLocked(proto);
                     proto.end(broadcastToken);
+
+                    long serviceToken = proto.start(ActivityManagerServiceProto.SERVICES);
+                    mServices.writeToProto(proto);
+                    proto.end(serviceToken);
                 }
             }
             proto.flush();
diff --git a/services/core/java/com/android/server/am/AppBindRecord.java b/services/core/java/com/android/server/am/AppBindRecord.java
index df833ad..7b38597 100644
--- a/services/core/java/com/android/server/am/AppBindRecord.java
+++ b/services/core/java/com/android/server/am/AppBindRecord.java
@@ -17,6 +17,9 @@
 package com.android.server.am;
 
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
 
 import java.io.PrintWriter;
 
@@ -60,4 +63,18 @@
             + Integer.toHexString(System.identityHashCode(this))
             + " " + service.shortName + ":" + client.processName + "}";
     }
+
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(AppBindRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        if (client != null) {
+            client.writeToProto(proto, AppBindRecordProto.CLIENT);
+        }
+        final int N = connections.size();
+        for (int i=0; i<N; i++) {
+            connections.valueAt(i).writeToProto(proto, AppBindRecordProto.CONNECTIONS);
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index 9b7a0c4..6df283c 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -19,6 +19,9 @@
 import android.app.IServiceConnection;
 import android.app.PendingIntent;
 import android.content.Context;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.ConnectionRecordProto;
 
 import java.io.PrintWriter;
 
@@ -119,4 +122,70 @@
         sb.append('}');
         return stringName = sb.toString();
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        if (binding == null) return; // if binding is null, don't write data, something is wrong.
+        long token = proto.start(fieldId);
+        proto.write(ConnectionRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        if (binding.client != null) {
+            proto.write(ConnectionRecordProto.USER_ID, binding.client.userId);
+        }
+        if ((flags&Context.BIND_AUTO_CREATE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.AUTO_CREATE);
+        }
+        if ((flags&Context.BIND_DEBUG_UNBIND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEBUG_UNBIND);
+        }
+        if ((flags&Context.BIND_NOT_FOREGROUND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_FG);
+        }
+        if ((flags&Context.BIND_IMPORTANT_BACKGROUND) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT_BG);
+        }
+        if ((flags&Context.BIND_ABOVE_CLIENT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ABOVE_CLIENT);
+        }
+        if ((flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ALLOW_OOM_MANAGEMENT);
+        }
+        if ((flags&Context.BIND_WAIVE_PRIORITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.WAIVE_PRIORITY);
+        }
+        if ((flags&Context.BIND_IMPORTANT) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.IMPORTANT);
+        }
+        if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.ADJUST_WITH_ACTIVITY);
+        }
+        if ((flags&Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE_WHILE_WAKE);
+        }
+        if ((flags&Context.BIND_FOREGROUND_SERVICE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.FG_SERVICE);
+        }
+        if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.TREAT_LIKE_ACTIVITY);
+        }
+        if ((flags&Context.BIND_VISIBLE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.VISIBLE);
+        }
+        if ((flags&Context.BIND_SHOWING_UI) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.SHOWING_UI);
+        }
+        if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.NOT_VISIBLE);
+        }
+        if (serviceDead) {
+            proto.write(ConnectionRecordProto.FLAGS, ConnectionRecordProto.DEAD);
+        }
+        if (binding.service != null) {
+            proto.write(ConnectionRecordProto.SERVICE_NAME, binding.service.shortName);
+        }
+        if (conn != null) {
+            proto.write(ConnectionRecordProto.CONN_HEX_HASH,
+                    Integer.toHexString(System.identityHashCode(conn.asBinder())));
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/IntentBindRecord.java b/services/core/java/com/android/server/am/IntentBindRecord.java
index be290e9..01ce64c 100644
--- a/services/core/java/com/android/server/am/IntentBindRecord.java
+++ b/services/core/java/com/android/server/am/IntentBindRecord.java
@@ -21,6 +21,10 @@
 import android.os.IBinder;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.AppBindRecordProto;
+import com.android.server.am.proto.IntentBindRecordProto;
 
 import java.io.PrintWriter;
 
@@ -106,4 +110,32 @@
         sb.append('}');
         return stringName = sb.toString();
     }
+
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(IntentBindRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        proto.write(IntentBindRecordProto.IS_CREATE,
+                (collectFlags()&Context.BIND_AUTO_CREATE) != 0);
+        if (intent != null) {
+            intent.getIntent().writeToProto(proto,
+                    IntentBindRecordProto.INTENT, false, true, false, false);
+        }
+        if (binder != null) {
+            proto.write(IntentBindRecordProto.BINDER, binder.toString());
+        }
+        proto.write(IntentBindRecordProto.REQUESTED, requested);
+        proto.write(IntentBindRecordProto.RECEIVED, received);
+        proto.write(IntentBindRecordProto.HAS_BOUND, hasBound);
+        proto.write(IntentBindRecordProto.DO_REBIND, doRebind);
+
+        final int N = apps.size();
+        for (int i=0; i<N; i++) {
+            AppBindRecord a = apps.valueAt(i);
+            if (a != null) {
+                a.writeToProto(proto, IntentBindRecordProto.APPS);
+            }
+        }
+        proto.end(token);
+    }
 }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 16995e5..b6eff00 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -19,6 +19,7 @@
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.server.LocalServices;
+import com.android.server.am.proto.ServiceRecordProto;
 import com.android.server.notification.NotificationManagerInternal;
 
 import android.app.INotificationManager;
@@ -42,6 +43,8 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+import android.util.proto.ProtoUtils;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -79,7 +82,7 @@
     final String permission;// permission needed to access service
     final boolean exported; // from ServiceInfo.exported
     final Runnable restarter; // used to schedule retries of starting the service
-    final long createTime;  // when this service was created
+    final long createRealTime;  // when this service was created
     final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
             = new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
                             // All active bindings to the service.
@@ -103,7 +106,7 @@
     boolean startRequested; // someone explicitly called start?
     boolean delayedStop;    // service has been stopped but is in a delayed start?
     boolean stopIfKilled;   // last onStart() said to stop if service killed?
-    boolean callStart;      // last onStart() has asked to alway be called on restart.
+    boolean callStart;      // last onStart() has asked to always be called on restart.
     int executeNesting;     // number of outstanding operations keeping foreground.
     boolean executeFg;      // should we be executing in the foreground?
     long executingStart;    // start time of last execute request.
@@ -159,6 +162,27 @@
             }
         }
 
+        public void writeToProto(ProtoOutputStream proto, long fieldId, long now) {
+            long token = proto.start(fieldId);
+            proto.write(ServiceRecordProto.StartItemProto.ID, id);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.StartItemProto.DURATION, deliveredTime, now);
+            proto.write(ServiceRecordProto.StartItemProto.DELIVERY_COUNT, deliveryCount);
+            proto.write(ServiceRecordProto.StartItemProto.DONE_EXECUTING_COUNT, doneExecutingCount);
+            if (intent != null) {
+                intent.writeToProto(proto, ServiceRecordProto.StartItemProto.INTENT, true, true,
+                        true, false);
+            }
+            if (neededGrants != null) {
+                neededGrants.writeToProto(proto, ServiceRecordProto.StartItemProto.NEEDED_GRANTS);
+            }
+            if (uriPermissions != null) {
+                uriPermissions.writeToProto(proto,
+                        ServiceRecordProto.StartItemProto.URI_PERMISSIONS);
+            }
+            proto.end(token);
+        }
+
         public String toString() {
             if (stringName != null) {
                 return stringName;
@@ -209,6 +233,117 @@
         }
     }
 
+    void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(ServiceRecordProto.SHORT_NAME, this.shortName);
+        proto.write(ServiceRecordProto.HEX_HASH,
+                Integer.toHexString(System.identityHashCode(this)));
+        proto.write(ServiceRecordProto.IS_RUNNING, app != null);
+        if (app != null) {
+            proto.write(ServiceRecordProto.PID, app.pid);
+        }
+        if (intent != null) {
+            intent.getIntent().writeToProto(proto, ServiceRecordProto.INTENT, false, true, false,
+                    true);
+        }
+        proto.write(ServiceRecordProto.PACKAGE_NAME, packageName);
+        proto.write(ServiceRecordProto.PROCESS_NAME, processName);
+        proto.write(ServiceRecordProto.PERMISSION, permission);
+
+        long now = SystemClock.uptimeMillis();
+        long nowReal = SystemClock.elapsedRealtime();
+        if (appInfo != null) {
+            long appInfoToken = proto.start(ServiceRecordProto.APPINFO);
+            proto.write(ServiceRecordProto.AppInfo.BASE_DIR, appInfo.sourceDir);
+            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+                proto.write(ServiceRecordProto.AppInfo.RES_DIR, appInfo.publicSourceDir);
+            }
+            proto.write(ServiceRecordProto.AppInfo.DATA_DIR, appInfo.dataDir);
+            proto.end(appInfoToken);
+        }
+        if (app != null) {
+            app.writeToProto(proto, ServiceRecordProto.APP);
+        }
+        if (isolatedProc != null) {
+            isolatedProc.writeToProto(proto, ServiceRecordProto.ISOLATED_PROC);
+        }
+        proto.write(ServiceRecordProto.WHITELIST_MANAGER, whitelistManager);
+        proto.write(ServiceRecordProto.DELAYED, delayed);
+        if (isForeground || foregroundId != 0) {
+            long fgToken = proto.start(ServiceRecordProto.FOREGROUND);
+            proto.write(ServiceRecordProto.Foreground.ID, foregroundId);
+            foregroundNoti.writeToProto(proto, ServiceRecordProto.Foreground.NOTIFICATION);
+            proto.end(fgToken);
+        }
+        ProtoUtils.toDuration(proto, ServiceRecordProto.CREATE_REAL_TIME, createRealTime, nowReal);
+        ProtoUtils.toDuration(proto,
+                ServiceRecordProto.STARTING_BG_TIMEOUT, startingBgTimeout, now);
+        ProtoUtils.toDuration(proto, ServiceRecordProto.LAST_ACTIVITY_TIME, lastActivity, now);
+        ProtoUtils.toDuration(proto, ServiceRecordProto.RESTART_TIME, restartTime, now);
+        proto.write(ServiceRecordProto.CREATED_FROM_FG, createdFromFg);
+
+        if (startRequested || delayedStop || lastStartId != 0) {
+            long startToken = proto.start(ServiceRecordProto.START);
+            proto.write(ServiceRecordProto.Start.START_REQUESTED, startRequested);
+            proto.write(ServiceRecordProto.Start.DELAYED_STOP, delayedStop);
+            proto.write(ServiceRecordProto.Start.STOP_IF_KILLED, stopIfKilled);
+            proto.write(ServiceRecordProto.Start.LAST_START_ID, lastStartId);
+            proto.end(startToken);
+        }
+
+        if (executeNesting != 0) {
+            long executNestingToken = proto.start(ServiceRecordProto.EXECUTE);
+            proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_NESTING, executeNesting);
+            proto.write(ServiceRecordProto.ExecuteNesting.EXECUTE_FG, executeFg);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.ExecuteNesting.EXECUTING_START, executingStart, now);
+            proto.end(executNestingToken);
+        }
+        if (destroying || destroyTime != 0) {
+            ProtoUtils.toDuration(proto, ServiceRecordProto.DESTORY_TIME, destroyTime, now);
+        }
+        if (crashCount != 0 || restartCount != 0 || restartDelay != 0 || nextRestartTime != 0) {
+            long crashToken = proto.start(ServiceRecordProto.CRASH);
+            proto.write(ServiceRecordProto.Crash.RESTART_COUNT, restartCount);
+            ProtoUtils.toDuration(proto, ServiceRecordProto.Crash.RESTART_DELAY, restartDelay, now);
+            ProtoUtils.toDuration(proto,
+                    ServiceRecordProto.Crash.NEXT_RESTART_TIME, nextRestartTime, now);
+            proto.write(ServiceRecordProto.Crash.CRASH_COUNT, crashCount);
+            proto.end(crashToken);
+        }
+
+        if (deliveredStarts.size() > 0) {
+            final int N = deliveredStarts.size();
+            for (int i = 0; i < N; i++) {
+                deliveredStarts.get(i).writeToProto(proto,
+                        ServiceRecordProto.DELIVERED_STARTS, now);
+            }
+        }
+        if (pendingStarts.size() > 0) {
+            final int N = pendingStarts.size();
+            for (int i = 0; i < N; i++) {
+                pendingStarts.get(i).writeToProto(proto, ServiceRecordProto.PENDING_STARTS, now);
+            }
+        }
+        if (bindings.size() > 0) {
+            final int N = bindings.size();
+            for (int i=0; i<N; i++) {
+                IntentBindRecord b = bindings.valueAt(i);
+                b.writeToProto(proto, ServiceRecordProto.BINDINGS);
+            }
+        }
+        if (connections.size() > 0) {
+            final int N = connections.size();
+            for (int conni=0; conni<N; conni++) {
+                ArrayList<ConnectionRecord> c = connections.valueAt(conni);
+                for (int i=0; i<c.size(); i++) {
+                    c.get(i).writeToProto(proto, ServiceRecordProto.CONNECTIONS);
+                }
+            }
+        }
+        proto.end(token);
+    }
+
     void dump(PrintWriter pw, String prefix) {
         pw.print(prefix); pw.print("intent={");
                 pw.print(intent.getIntent().toShortString(false, true, false, true));
@@ -243,7 +378,7 @@
                     pw.print(" foregroundNoti="); pw.println(foregroundNoti);
         }
         pw.print(prefix); pw.print("createTime=");
-                TimeUtils.formatDuration(createTime, nowReal, pw);
+                TimeUtils.formatDuration(createRealTime, nowReal, pw);
                 pw.print(" startingBgTimeout=");
                 TimeUtils.formatDuration(startingBgTimeout, now, pw);
                 pw.println();
@@ -329,7 +464,7 @@
         permission = sInfo.permission;
         exported = sInfo.exported;
         this.restarter = restarter;
-        createTime = SystemClock.elapsedRealtime();
+        createRealTime = SystemClock.elapsedRealtime();
         lastActivity = SystemClock.uptimeMillis();
         userId = UserHandle.getUserId(appInfo.uid);
         createdFromFg = callerIsFg;
diff --git a/services/core/java/com/android/server/am/UriPermissionOwner.java b/services/core/java/com/android/server/am/UriPermissionOwner.java
index 28344df..fc07c1a 100644
--- a/services/core/java/com/android/server/am/UriPermissionOwner.java
+++ b/services/core/java/com/android/server/am/UriPermissionOwner.java
@@ -20,6 +20,9 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.util.ArraySet;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.am.proto.UriPermissionOwnerProto;
 
 import com.google.android.collect.Sets;
 
@@ -139,6 +142,26 @@
         }
     }
 
+    public void writeToProto(ProtoOutputStream proto, long fieldId) {
+        long token = proto.start(fieldId);
+        proto.write(UriPermissionOwnerProto.OWNER, owner.toString());
+        if (mReadPerms != null) {
+            synchronized (mReadPerms) {
+                for (UriPermission p : mReadPerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.READ_PERMS);
+                }
+            }
+        }
+        if (mWritePerms != null) {
+            synchronized (mWritePerms) {
+                for (UriPermission p : mWritePerms) {
+                    p.uri.writeToProto(proto, UriPermissionOwnerProto.WRITE_PERMS);
+                }
+            }
+        }
+        proto.end(token);
+    }
+
     @Override
     public String toString() {
         return owner.toString();