• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.usage;
18 
19 import static android.app.ActivityManager.procStateToString;
20 
21 import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_CANCELLED;
22 import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_POSTED;
23 import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_UPDATED;
24 import static com.android.server.usage.BroadcastResponseStatsTracker.TAG;
25 import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
26 
27 import android.annotation.ElapsedRealtimeLong;
28 import android.annotation.NonNull;
29 import android.annotation.UserIdInt;
30 import android.app.ActivityManager;
31 import android.app.ActivityManager.ProcessState;
32 import android.os.UserHandle;
33 import android.text.TextUtils;
34 import android.util.Slog;
35 import android.util.TimeUtils;
36 
37 import com.android.internal.annotations.GuardedBy;
38 import com.android.internal.util.IndentingPrintWriter;
39 import com.android.internal.util.RingBuffer;
40 import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
41 
42 import java.util.function.IntFunction;
43 import java.util.function.Supplier;
44 
45 public class BroadcastResponseStatsLogger {
46 
47     private static final int MAX_LOG_SIZE =
48             ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
49 
50     private final Object mLock = new Object();
51 
52     @GuardedBy("mLock")
53     private final LogBuffer mBroadcastEventsBuffer = new LogBuffer(
54             BroadcastEvent::new, BroadcastEvent[]::new, MAX_LOG_SIZE);
55     @GuardedBy("mLock")
56     private final LogBuffer mNotificationEventsBuffer = new LogBuffer(
57             NotificationEvent::new, NotificationEvent[]::new, MAX_LOG_SIZE);
58 
logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState)59     void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
60             UserHandle targetUser, long idForResponseEvent,
61             @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
62         synchronized (mLock) {
63             if (DEBUG_RESPONSE_STATS) {
64                 Slog.d(TAG, getBroadcastDispatchEventLog(sourceUid, targetPackage,
65                         targetUser.getIdentifier(), idForResponseEvent, timeStampMs,
66                         targetUidProcessState));
67             }
68             mBroadcastEventsBuffer.logBroadcastDispatchEvent(sourceUid, targetPackage,
69                     targetUser, idForResponseEvent, timeStampMs, targetUidProcessState);
70         }
71     }
72 
logNotificationEvent(@otificationEventType int event, @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)73     void logNotificationEvent(@NotificationEventType int event,
74             @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
75         synchronized (mLock) {
76             if (DEBUG_RESPONSE_STATS) {
77                 Slog.d(TAG, getNotificationEventLog(event, packageName, user.getIdentifier(),
78                         timestampMs));
79             }
80             mNotificationEventsBuffer.logNotificationEvent(event, packageName, user, timestampMs);
81         }
82     }
83 
dumpLogs(IndentingPrintWriter ipw)84     void dumpLogs(IndentingPrintWriter ipw) {
85         synchronized (mLock) {
86             ipw.println("Broadcast events (most recent first):");
87             ipw.increaseIndent();
88             mBroadcastEventsBuffer.reverseDump(ipw);
89             ipw.decreaseIndent();
90 
91             ipw.println();
92             ipw.println("Notification events (most recent first):");
93             ipw.increaseIndent();
94             mNotificationEventsBuffer.reverseDump(ipw);
95             ipw.decreaseIndent();
96         }
97     }
98 
99     private static final class LogBuffer<T extends Data> extends RingBuffer<T> {
100 
LogBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity)101         LogBuffer(Supplier<T> newItem, IntFunction<T[]> newBacking, int capacity) {
102             super(newItem, newBacking, capacity);
103         }
104 
logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState)105         void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
106                 UserHandle targetUser, long idForResponseEvent,
107                 @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
108             final Data data = getNextSlot();
109             if (data == null) return;
110 
111             data.reset();
112             final BroadcastEvent event = (BroadcastEvent) data;
113             event.sourceUid = sourceUid;
114             event.targetUserId = targetUser.getIdentifier();
115             event.targetUidProcessState = targetUidProcessState;
116             event.targetPackage = targetPackage;
117             event.idForResponseEvent = idForResponseEvent;
118             event.timestampMs = timeStampMs;
119         }
120 
logNotificationEvent(@otificationEventType int type, @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs)121         void logNotificationEvent(@NotificationEventType int type,
122                 @NonNull String packageName, UserHandle user,
123                 @ElapsedRealtimeLong long timestampMs) {
124             final Data data = getNextSlot();
125             if (data == null) return;
126 
127             data.reset();
128             final NotificationEvent event = (NotificationEvent) data;
129             event.type = type;
130             event.packageName = packageName;
131             event.userId = user.getIdentifier();
132             event.timestampMs = timestampMs;
133         }
134 
reverseDump(IndentingPrintWriter pw)135         public void reverseDump(IndentingPrintWriter pw) {
136             final Data[] allData = toArray();
137             for (int i = allData.length - 1; i >= 0; --i) {
138                 if (allData[i] == null) {
139                     continue;
140                 }
141                 pw.println(getContent(allData[i]));
142             }
143         }
144 
145         @NonNull
getContent(Data data)146         public String getContent(Data data) {
147             return data.toString();
148         }
149     }
150 
151     @NonNull
getBroadcastDispatchEventLog(int sourceUid, @NonNull String targetPackage, @UserIdInt int targetUserId, long idForResponseEvent, @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState)152     private static String getBroadcastDispatchEventLog(int sourceUid, @NonNull String targetPackage,
153             @UserIdInt int targetUserId, long idForResponseEvent,
154             @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
155         return TextUtils.formatSimple(
156                 "broadcast:%s; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, state=%s",
157                 TimeUtils.formatDuration(timestampMs), sourceUid, targetPackage, targetUserId,
158                 idForResponseEvent, procStateToString(targetUidProcState));
159     }
160 
161     @NonNull
getNotificationEventLog(@otificationEventType int event, @NonNull String packageName, @UserIdInt int userId, @ElapsedRealtimeLong long timestampMs)162     private static String getNotificationEventLog(@NotificationEventType int event,
163             @NonNull String packageName, @UserIdInt int userId,
164             @ElapsedRealtimeLong long timestampMs) {
165         return TextUtils.formatSimple("notification:%s; event=<%s>, pkg=%s, usr=%d",
166                 TimeUtils.formatDuration(timestampMs), notificationEventToString(event),
167                 packageName, userId);
168     }
169 
170     @NonNull
notificationEventToString(@otificationEventType int event)171     private static String notificationEventToString(@NotificationEventType int event) {
172         switch (event) {
173             case NOTIFICATION_EVENT_TYPE_POSTED:
174                 return "posted";
175             case NOTIFICATION_EVENT_TYPE_UPDATED:
176                 return "updated";
177             case NOTIFICATION_EVENT_TYPE_CANCELLED:
178                 return "cancelled";
179             default:
180                 return String.valueOf(event);
181         }
182     }
183 
184     private static final class BroadcastEvent implements Data {
185         public int sourceUid;
186         public int targetUserId;
187         public int targetUidProcessState;
188         public String targetPackage;
189         public long idForResponseEvent;
190         public long timestampMs;
191 
192         @Override
reset()193         public void reset() {
194             targetPackage = null;
195         }
196 
197         @Override
toString()198         public String toString() {
199             return getBroadcastDispatchEventLog(sourceUid, targetPackage, targetUserId,
200                     idForResponseEvent, timestampMs, targetUidProcessState);
201         }
202     }
203 
204     private static final class NotificationEvent implements Data {
205         public int type;
206         public String packageName;
207         public int userId;
208         public long timestampMs;
209 
210         @Override
reset()211         public void reset() {
212             packageName = null;
213         }
214 
215         @Override
toString()216         public String toString() {
217             return getNotificationEventLog(type, packageName, userId, timestampMs);
218         }
219     }
220 
221     private interface Data {
reset()222         void reset();
223     }
224 }
225