From 6b51414154e621a073e898835d9ffaa671c862d8 Mon Sep 17 00:00:00 2001 From: Yi Jin Date: Mon, 30 Oct 2017 14:54:12 -0700 Subject: [PATCH] Implement Activity Manager Dumpsys --service option Bug: 66729158 Test: out/host/linux-x86/bin/incident_report -w amservices Change-Id: I72015b9744bc8028001306283f169fca4797c700 --- core/java/android/app/Notification.java | 25 ++++ core/java/android/util/proto/ProtoUtils.java | 12 ++ core/proto/android/app/notification.proto | 45 ++++++ core/proto/android/os/incident.proto | 6 +- .../android/server/activitymanagerservice.proto | 161 ++++++++++++++++++++- core/proto/android/util/common.proto | 10 ++ .../java/com/android/server/am/ActiveServices.java | 28 +++- .../android/server/am/ActivityManagerService.java | 28 ++++ .../java/com/android/server/am/AppBindRecord.java | 17 +++ .../com/android/server/am/ConnectionRecord.java | 69 +++++++++ .../com/android/server/am/IntentBindRecord.java | 32 ++++ .../java/com/android/server/am/ServiceRecord.java | 143 +++++++++++++++++- .../com/android/server/am/UriPermissionOwner.java | 23 +++ 13 files changed, 587 insertions(+), 12 deletions(-) create mode 100644 core/proto/android/app/notification.proto diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index d5d95fb85a6a..42c1347e265d 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -68,6 +68,7 @@ import android.text.style.TextAppearanceSpan; 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 @@ public class Notification implements Parcelable 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 449baca5c844..85b7ec8265d1 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 @@ public class ProtoUtils { 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 000000000000..5376b0ed5c4c --- /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 f68f3a4ad7a4..6c5206c3dd96 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -143,7 +143,11 @@ message IncidentProto { (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 c57cb72c8b68..889842ccc99e 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 @@ message ActivityManagerServiceProto { 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 @@ message KeyguardControllerProto { optional bool keyguard_occluded = 2; } +// "dumpsys activity --proto broadcasts" message BroadcastProto { repeated ReceiverListProto receiver_list = 1; @@ -164,10 +169,158 @@ message StickyBroadcastProto { 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 429c3cadc89a..308ef703bddb 100644 --- a/core/proto/android/util/common.proto +++ b/core/proto/android/util/common.proto @@ -30,3 +30,13 @@ message AggStats { 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 701c574f64ea..3cd2f6ae3b31 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.os.TransferPipe; 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.PrintWriterPrinter; 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { 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 alls = smap.mServicesByName; + for (int i=0; i bindings = new ArrayMap(); // All active bindings to the service. @@ -103,7 +106,7 @@ final class ServiceRecord extends Binder { 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 @@ final class ServiceRecord extends Binder { } } + 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 @@ final class ServiceRecord extends Binder { } } + 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 0) { + final int N = connections.size(); + for (int conni=0; conni c = connections.valueAt(conni); + for (int i=0; i