• 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.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