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