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