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.am; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Intent; 22 import android.os.Bundle; 23 import android.os.Trace; 24 import android.util.ArrayMap; 25 import android.util.SparseArray; 26 import android.util.TimeUtils; 27 import android.util.proto.ProtoOutputStream; 28 29 import dalvik.annotation.optimization.NeverCompile; 30 31 import java.io.PrintWriter; 32 import java.text.SimpleDateFormat; 33 import java.util.ArrayList; 34 import java.util.Date; 35 import java.util.Objects; 36 37 /** 38 * Collection of recent historical broadcasts that are available to be dumped 39 * for debugging purposes. Automatically trims itself over time. 40 */ 41 public class BroadcastHistory { 42 private final int MAX_BROADCAST_HISTORY; 43 private final int MAX_BROADCAST_SUMMARY_HISTORY; 44 BroadcastHistory(@onNull BroadcastConstants constants)45 public BroadcastHistory(@NonNull BroadcastConstants constants) { 46 MAX_BROADCAST_HISTORY = constants.MAX_HISTORY_COMPLETE_SIZE; 47 MAX_BROADCAST_SUMMARY_HISTORY = constants.MAX_HISTORY_SUMMARY_SIZE; 48 49 mBroadcastHistory = new BroadcastRecord[MAX_BROADCAST_HISTORY]; 50 mBroadcastSummaryHistory = new Intent[MAX_BROADCAST_SUMMARY_HISTORY]; 51 mSummaryHistoryEnqueueTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; 52 mSummaryHistoryDispatchTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; 53 mSummaryHistoryFinishTime = new long[MAX_BROADCAST_SUMMARY_HISTORY]; 54 } 55 56 /** 57 * List of broadcasts in frozen processes that are yet to be enqueued. 58 */ 59 private final ArrayList<BroadcastRecord> mFrozenBroadcasts = new ArrayList<>(); 60 61 /** 62 * List of broadcasts which are being delivered or yet to be delivered. 63 */ 64 private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>(); 65 66 /** 67 * Historical data of past broadcasts, for debugging. This is a ring buffer 68 * whose last element is at mHistoryNext. 69 */ 70 final BroadcastRecord[] mBroadcastHistory; 71 int mHistoryNext = 0; 72 73 /** 74 * Summary of historical data of past broadcasts, for debugging. This is a 75 * ring buffer whose last element is at mSummaryHistoryNext. 76 */ 77 final Intent[] mBroadcastSummaryHistory; 78 int mSummaryHistoryNext = 0; 79 80 /** 81 * Various milestone timestamps of entries in the mBroadcastSummaryHistory ring 82 * buffer, also tracked via the mSummaryHistoryNext index. These are all in wall 83 * clock time, not elapsed. 84 */ 85 final long[] mSummaryHistoryEnqueueTime; 86 final long[] mSummaryHistoryDispatchTime; 87 final long[] mSummaryHistoryFinishTime; 88 89 /** 90 * Map of uids to number of pending broadcasts it sent. 91 */ 92 private final SparseArray<ArrayMap<String, Integer>> mPendingBroadcastCountsPerUid = 93 new SparseArray<>(); 94 onBroadcastFrozenLocked(@onNull BroadcastRecord r)95 void onBroadcastFrozenLocked(@NonNull BroadcastRecord r) { 96 mFrozenBroadcasts.add(r); 97 } 98 onBroadcastEnqueuedLocked(@onNull BroadcastRecord r)99 void onBroadcastEnqueuedLocked(@NonNull BroadcastRecord r) { 100 mFrozenBroadcasts.remove(r); 101 if (mPendingBroadcasts.add(r)) { 102 updatePendingBroadcastCounterAndLogToTrace(r, /* delta= */ 1); 103 } 104 } 105 onBroadcastFinishedLocked(@onNull BroadcastRecord r)106 void onBroadcastFinishedLocked(@NonNull BroadcastRecord r) { 107 if (mPendingBroadcasts.remove(r)) { 108 updatePendingBroadcastCounterAndLogToTrace(r, /* delta= */ -1); 109 } 110 addBroadcastToHistoryLocked(r); 111 } 112 updatePendingBroadcastCounterAndLogToTrace(@onNull BroadcastRecord r, int delta)113 private void updatePendingBroadcastCounterAndLogToTrace(@NonNull BroadcastRecord r, 114 int delta) { 115 ArrayMap<String, Integer> pendingBroadcastCounts = 116 mPendingBroadcastCountsPerUid.get(r.callingUid); 117 if (pendingBroadcastCounts == null) { 118 pendingBroadcastCounts = new ArrayMap<>(); 119 mPendingBroadcastCountsPerUid.put(r.callingUid, pendingBroadcastCounts); 120 } 121 final String callerPackage = r.callerPackage == null ? "null" : r.callerPackage; 122 final Integer currentCount = pendingBroadcastCounts.get(callerPackage); 123 final int newCount = (currentCount == null ? 0 : currentCount) + delta; 124 if (newCount == 0) { 125 pendingBroadcastCounts.remove(callerPackage); 126 if (pendingBroadcastCounts.isEmpty()) { 127 mPendingBroadcastCountsPerUid.remove(r.callingUid); 128 } 129 } else { 130 pendingBroadcastCounts.put(callerPackage, newCount); 131 } 132 133 Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Broadcasts pending per uid", 134 callerPackage + "/" + r.callingUid + ":" + newCount); 135 Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Broadcasts pending", 136 mPendingBroadcasts.size()); 137 } 138 addBroadcastToHistoryLocked(@onNull BroadcastRecord original)139 public void addBroadcastToHistoryLocked(@NonNull BroadcastRecord original) { 140 // Note sometimes (only for sticky broadcasts?) we reuse BroadcastRecords, 141 // So don't change the incoming record directly. 142 final BroadcastRecord historyRecord = original.maybeStripForHistory(); 143 144 mBroadcastHistory[mHistoryNext] = historyRecord; 145 mHistoryNext = ringAdvance(mHistoryNext, 1, MAX_BROADCAST_HISTORY); 146 147 mBroadcastSummaryHistory[mSummaryHistoryNext] = historyRecord.intent; 148 mSummaryHistoryEnqueueTime[mSummaryHistoryNext] = historyRecord.enqueueClockTime; 149 mSummaryHistoryDispatchTime[mSummaryHistoryNext] = historyRecord.dispatchClockTime; 150 mSummaryHistoryFinishTime[mSummaryHistoryNext] = System.currentTimeMillis(); 151 mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY); 152 } 153 ringAdvance(int x, final int increment, final int ringSize)154 private int ringAdvance(int x, final int increment, final int ringSize) { 155 x += increment; 156 if (x < 0) return (ringSize - 1); 157 else if (x >= ringSize) return 0; 158 else return x; 159 } 160 161 @NeverCompile dumpDebug(@onNull ProtoOutputStream proto)162 public void dumpDebug(@NonNull ProtoOutputStream proto) { 163 for (int i = 0; i < mPendingBroadcasts.size(); ++i) { 164 final BroadcastRecord r = mPendingBroadcasts.get(i); 165 r.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCASTS); 166 } 167 for (int i = 0; i < mFrozenBroadcasts.size(); ++i) { 168 final BroadcastRecord r = mFrozenBroadcasts.get(i); 169 r.dumpDebug(proto, BroadcastQueueProto.FROZEN_BROADCASTS); 170 } 171 172 int lastIndex = mHistoryNext; 173 int ringIndex = lastIndex; 174 do { 175 // increasing index = more recent entry, and we want to print the most 176 // recent first and work backwards, so we roll through the ring backwards. 177 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); 178 BroadcastRecord r = mBroadcastHistory[ringIndex]; 179 if (r != null) { 180 r.dumpDebug(proto, BroadcastQueueProto.HISTORICAL_BROADCASTS); 181 } 182 } while (ringIndex != lastIndex); 183 184 lastIndex = ringIndex = mSummaryHistoryNext; 185 do { 186 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); 187 Intent intent = mBroadcastSummaryHistory[ringIndex]; 188 if (intent == null) { 189 continue; 190 } 191 long summaryToken = proto.start(BroadcastQueueProto.HISTORICAL_BROADCASTS_SUMMARY); 192 intent.dumpDebug(proto, BroadcastQueueProto.BroadcastSummary.INTENT, 193 false, true, true, false); 194 proto.write(BroadcastQueueProto.BroadcastSummary.ENQUEUE_CLOCK_TIME_MS, 195 mSummaryHistoryEnqueueTime[ringIndex]); 196 proto.write(BroadcastQueueProto.BroadcastSummary.DISPATCH_CLOCK_TIME_MS, 197 mSummaryHistoryDispatchTime[ringIndex]); 198 proto.write(BroadcastQueueProto.BroadcastSummary.FINISH_CLOCK_TIME_MS, 199 mSummaryHistoryFinishTime[ringIndex]); 200 proto.end(summaryToken); 201 } while (ringIndex != lastIndex); 202 } 203 204 @NeverCompile dumpLocked(@onNull PrintWriter pw, @Nullable String dumpPackage, @Nullable String dumpIntentAction, @NonNull SimpleDateFormat sdf, boolean dumpAll)205 public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage, 206 @Nullable String dumpIntentAction, 207 @NonNull SimpleDateFormat sdf, boolean dumpAll) { 208 boolean needSep = true; 209 dumpBroadcastList(pw, sdf, mFrozenBroadcasts, dumpIntentAction, dumpAll, "Frozen"); 210 dumpBroadcastList(pw, sdf, mPendingBroadcasts, dumpIntentAction, dumpAll, "Pending"); 211 212 int i; 213 boolean printed = false; 214 215 i = -1; 216 int lastIndex = mHistoryNext; 217 int ringIndex = lastIndex; 218 do { 219 // increasing index = more recent entry, and we want to print the most 220 // recent first and work backwards, so we roll through the ring backwards. 221 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_HISTORY); 222 BroadcastRecord r = mBroadcastHistory[ringIndex]; 223 if (r == null) { 224 continue; 225 } 226 227 i++; // genuine record of some sort even if we're filtering it out 228 if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { 229 continue; 230 } 231 if (dumpIntentAction != null && !Objects.equals(dumpIntentAction, 232 r.intent.getAction())) { 233 continue; 234 } 235 if (!printed) { 236 if (needSep) { 237 pw.println(); 238 } 239 needSep = true; 240 pw.println(" Historical broadcasts:"); 241 printed = true; 242 } 243 if (dumpIntentAction != null) { 244 pw.print(" Historical Broadcast #"); 245 pw.print(i); pw.println(":"); 246 r.dump(pw, " ", sdf); 247 if (!dumpAll) { 248 break; 249 } 250 } else if (dumpAll) { 251 pw.print(" Historical Broadcast #"); 252 pw.print(i); pw.println(":"); 253 r.dump(pw, " ", sdf); 254 } else { 255 pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); 256 pw.print(" "); 257 pw.println(r.intent.toShortString(false, true, true, false)); 258 if (r.targetComp != null && r.targetComp != r.intent.getComponent()) { 259 pw.print(" targetComp: "); pw.println(r.targetComp.toShortString()); 260 } 261 Bundle bundle = r.intent.getExtras(); 262 if (bundle != null) { 263 pw.print(" extras: "); pw.println(bundle.toString()); 264 } 265 } 266 } while (ringIndex != lastIndex); 267 268 if (dumpPackage == null && dumpIntentAction == null) { 269 lastIndex = ringIndex = mSummaryHistoryNext; 270 if (dumpAll) { 271 printed = false; 272 i = -1; 273 } else { 274 // roll over the 'i' full dumps that have already been issued 275 for (int j = i; 276 j > 0 && ringIndex != lastIndex;) { 277 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); 278 BroadcastRecord r = mBroadcastHistory[ringIndex]; 279 if (r == null) { 280 continue; 281 } 282 j--; 283 } 284 } 285 // done skipping; dump the remainder of the ring. 'i' is still the ordinal within 286 // the overall broadcast history. 287 do { 288 ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); 289 Intent intent = mBroadcastSummaryHistory[ringIndex]; 290 if (intent == null) { 291 continue; 292 } 293 if (!printed) { 294 if (needSep) { 295 pw.println(); 296 } 297 needSep = true; 298 pw.println(" Historical broadcasts summary:"); 299 printed = true; 300 } 301 if (!dumpAll && i >= 50) { 302 pw.println(" ..."); 303 break; 304 } 305 i++; 306 pw.print(" #"); pw.print(i); pw.print(": "); 307 pw.println(intent.toShortString(false, true, true, false)); 308 pw.print(" "); 309 TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex] 310 - mSummaryHistoryEnqueueTime[ringIndex], pw); 311 pw.print(" dispatch "); 312 TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex] 313 - mSummaryHistoryDispatchTime[ringIndex], pw); 314 pw.println(" finish"); 315 pw.print(" enq="); 316 pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); 317 pw.print(" disp="); 318 pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); 319 pw.print(" fin="); 320 pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); 321 Bundle bundle = intent.getExtras(); 322 if (bundle != null) { 323 pw.print(" extras: "); pw.println(bundle.toString()); 324 } 325 } while (ringIndex != lastIndex); 326 } 327 return needSep; 328 } 329 dumpBroadcastList(@onNull PrintWriter pw, @NonNull SimpleDateFormat sdf, @NonNull ArrayList<BroadcastRecord> broadcasts, @Nullable String dumpIntentAction, boolean dumpAll, @NonNull String flavor)330 private void dumpBroadcastList(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf, 331 @NonNull ArrayList<BroadcastRecord> broadcasts, @Nullable String dumpIntentAction, 332 boolean dumpAll, @NonNull String flavor) { 333 pw.print(" "); pw.print(flavor); pw.println(" broadcasts:"); 334 if (broadcasts.isEmpty()) { 335 pw.println(" <empty>"); 336 } else { 337 boolean printedAnything = false; 338 for (int idx = broadcasts.size() - 1; idx >= 0; --idx) { 339 final BroadcastRecord r = broadcasts.get(idx); 340 if (dumpIntentAction != null && !Objects.equals(dumpIntentAction, 341 r.intent.getAction())) { 342 continue; 343 } 344 pw.print(flavor); pw.print(" broadcast #"); pw.print(idx); pw.println(":"); 345 r.dump(pw, " ", sdf); 346 printedAnything = true; 347 if (dumpIntentAction != null && !dumpAll) { 348 break; 349 } 350 } 351 if (!printedAnything) { 352 pw.println(" <no-matches>"); 353 } 354 } 355 } 356 } 357