blob: b0b1840214f1c8537961fbf67c8177b471cdbf59 [file] [log] [blame]
Christopher Tate2f558d22019-01-17 16:58:31 -08001/*
2 * Copyright (C) 2018 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
17package com.android.server.am;
18
19import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
20import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
21
22import android.content.Intent;
23import android.os.Handler;
24import android.os.SystemClock;
25import android.util.Slog;
26import android.util.SparseIntArray;
27import android.util.proto.ProtoOutputStream;
28
29import com.android.server.AlarmManagerInternal;
30import com.android.server.LocalServices;
31
32import java.io.PrintWriter;
33import java.text.SimpleDateFormat;
34import java.util.ArrayList;
35import java.util.Set;
36
37/**
38 * Manages ordered broadcast delivery, applying policy to mitigate the effects of
39 * slow receivers.
40 */
41public class BroadcastDispatcher {
42 private static final String TAG = "BroadcastDispatcher";
43
44 // Deferred broadcasts to one app; times are all uptime time base like
45 // other broadcast-related timekeeping
46 static class Deferrals {
47 final int uid;
48 long deferredAt; // when we started deferring
49 long deferredBy; // how long did we defer by last time?
50 long deferUntil; // when does the next element become deliverable?
51 int alarmCount;
52
53 final ArrayList<BroadcastRecord> broadcasts;
54
55 Deferrals(int uid, long now, long backoff, int count) {
56 this.uid = uid;
57 this.deferredAt = now;
58 this.deferredBy = backoff;
59 this.deferUntil = now + backoff;
60 this.alarmCount = count;
61 broadcasts = new ArrayList<>();
62 }
63
64 void add(BroadcastRecord br) {
65 broadcasts.add(br);
66 }
67
Christopher Tateb486f412019-02-12 18:05:17 -080068 int size() {
69 return broadcasts.size();
70 }
71
72 boolean isEmpty() {
73 return broadcasts.isEmpty();
74 }
75
Christopher Tate2f558d22019-01-17 16:58:31 -080076 void writeToProto(ProtoOutputStream proto, long fieldId) {
77 for (BroadcastRecord br : broadcasts) {
78 br.writeToProto(proto, fieldId);
79 }
80 }
81
82 void dumpLocked(Dumper d) {
83 for (BroadcastRecord br : broadcasts) {
84 d.dump(br);
85 }
86 }
87
88 @Override
89 public String toString() {
90 StringBuilder sb = new StringBuilder(128);
91 sb.append("Deferrals{uid=");
92 sb.append(uid);
93 sb.append(", deferUntil=");
94 sb.append(deferUntil);
95 sb.append(", #broadcasts=");
96 sb.append(broadcasts.size());
97 sb.append("}");
98 return sb.toString();
99 }
100 }
101
102 // Carrying dump formatting state across multiple concatenated datasets
103 class Dumper {
104 final PrintWriter mPw;
105 final String mQueueName;
106 final String mDumpPackage;
107 final SimpleDateFormat mSdf;
108 boolean mPrinted;
109 boolean mNeedSep;
110 String mHeading;
111 String mLabel;
112 int mOrdinal;
113
114 Dumper(PrintWriter pw, String queueName, String dumpPackage, SimpleDateFormat sdf) {
115 mPw = pw;
116 mQueueName = queueName;
117 mDumpPackage = dumpPackage;
118 mSdf = sdf;
119
120 mPrinted = false;
121 mNeedSep = true;
122 }
123
124 void setHeading(String heading) {
125 mHeading = heading;
126 mPrinted = false;
127 }
128
129 void setLabel(String label) {
130 //" Active Ordered Broadcast " + mQueueName + " #" + i + ":"
131 mLabel = " " + label + " " + mQueueName + " #";
132 mOrdinal = 0;
133 }
134
135 boolean didPrint() {
136 return mPrinted;
137 }
138
139 void dump(BroadcastRecord br) {
140 if (mDumpPackage == null || mDumpPackage.equals(br.callerPackage)) {
141 if (!mPrinted) {
142 if (mNeedSep) {
143 mPw.println();
144 }
145 mPrinted = true;
146 mNeedSep = true;
147 mPw.println(" " + mHeading + " [" + mQueueName + "]:");
148 }
149 mPw.println(mLabel + mOrdinal + ":");
150 mOrdinal++;
151
152 br.dump(mPw, " ", mSdf);
153 }
154 }
155 }
156
157 private final Object mLock;
158 private final BroadcastQueue mQueue;
159 private final BroadcastConstants mConstants;
160 private final Handler mHandler;
161 private AlarmManagerInternal mAlarm;
162
163 // Current alarm targets; mapping uid -> in-flight alarm count
164 final SparseIntArray mAlarmUids = new SparseIntArray();
165 final AlarmManagerInternal.InFlightListener mAlarmListener =
166 new AlarmManagerInternal.InFlightListener() {
167 @Override
168 public void broadcastAlarmPending(final int recipientUid) {
169 synchronized (mLock) {
170 final int newCount = mAlarmUids.get(recipientUid, 0) + 1;
171 mAlarmUids.put(recipientUid, newCount);
172 // any deferred broadcasts to this app now get fast-tracked
173 final int numEntries = mDeferredBroadcasts.size();
174 for (int i = 0; i < numEntries; i++) {
175 if (recipientUid == mDeferredBroadcasts.get(i).uid) {
176 Deferrals d = mDeferredBroadcasts.remove(i);
177 mAlarmBroadcasts.add(d);
178 break;
179 }
180 }
181 }
182 }
183
184 @Override
185 public void broadcastAlarmComplete(final int recipientUid) {
186 synchronized (mLock) {
187 final int newCount = mAlarmUids.get(recipientUid, 0) - 1;
188 if (newCount >= 0) {
189 mAlarmUids.put(recipientUid, newCount);
190 } else {
191 Slog.wtf(TAG, "Undercount of broadcast alarms in flight for " + recipientUid);
192 mAlarmUids.put(recipientUid, 0);
193 }
194
195 // No longer an alarm target, so resume ordinary deferral policy
196 if (newCount <= 0) {
197 final int numEntries = mAlarmBroadcasts.size();
198 for (int i = 0; i < numEntries; i++) {
199 if (recipientUid == mAlarmBroadcasts.get(i).uid) {
200 Deferrals d = mAlarmBroadcasts.remove(i);
201 insertLocked(mDeferredBroadcasts, d);
202 break;
203 }
204 }
205 }
206 }
207 }
208 };
209
210 // Queue recheck operation used to tickle broadcast delivery when appropriate
211 final Runnable mScheduleRunnable = new Runnable() {
212 @Override
213 public void run() {
214 synchronized (mLock) {
215 if (DEBUG_BROADCAST_DEFERRAL) {
216 Slog.v(TAG, "Deferral recheck of pending broadcasts");
217 }
218 mQueue.scheduleBroadcastsLocked();
219 mRecheckScheduled = false;
220 }
221 }
222 };
223 private boolean mRecheckScheduled = false;
224
225 // Usual issuance-order outbound queue
226 private final ArrayList<BroadcastRecord> mOrderedBroadcasts = new ArrayList<>();
227 // General deferrals not holding up alarms
228 private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
229 // Deferrals that *are* holding up alarms; ordered by alarm dispatch time
230 private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>();
231
232 // Next outbound broadcast, established by getNextBroadcastLocked()
233 private BroadcastRecord mCurrentBroadcast;
234
235 /**
236 * Constructed & sharing a lock with its associated BroadcastQueue instance
237 */
238 public BroadcastDispatcher(BroadcastQueue queue, BroadcastConstants constants,
239 Handler handler, Object lock) {
240 mQueue = queue;
241 mConstants = constants;
242 mHandler = handler;
243 mLock = lock;
244 }
245
246 /**
247 * Spin up the integration with the alarm manager service; done lazily to manage
248 * service availability ordering during boot.
249 */
250 public void start() {
251 // Set up broadcast alarm tracking
252 mAlarm = LocalServices.getService(AlarmManagerInternal.class);
253 mAlarm.registerInFlightListener(mAlarmListener);
254 }
255
256 /**
257 * Standard contents-are-empty check
258 */
259 public boolean isEmpty() {
260 synchronized (mLock) {
261 return mCurrentBroadcast == null
262 && mOrderedBroadcasts.isEmpty()
Christopher Tateb486f412019-02-12 18:05:17 -0800263 && isDeferralsListEmpty(mDeferredBroadcasts)
264 && isDeferralsListEmpty(mAlarmBroadcasts);
Christopher Tate2f558d22019-01-17 16:58:31 -0800265 }
266 }
267
Christopher Tateb486f412019-02-12 18:05:17 -0800268 private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
269 int pending = 0;
270 final int numEntries = list.size();
271 for (int i = 0; i < numEntries; i++) {
272 pending += list.get(i).size();
Christopher Tate2f558d22019-01-17 16:58:31 -0800273 }
Christopher Tateb486f412019-02-12 18:05:17 -0800274 return pending;
275 }
276
277 private static boolean isDeferralsListEmpty(ArrayList<Deferrals> list) {
278 return pendingInDeferralsList(list) == 0;
279 }
280
281 /**
282 * Strictly for logging, describe the currently pending contents in a human-
283 * readable way
284 */
285 public String describeStateLocked() {
286 final StringBuilder sb = new StringBuilder(128);
287 if (mCurrentBroadcast != null) {
288 sb.append("1 in flight, ");
289 }
290 sb.append(mOrderedBroadcasts.size());
291 sb.append(" ordered");
292 int n = pendingInDeferralsList(mAlarmBroadcasts);
293 if (n > 0) {
294 sb.append(", ");
295 sb.append(n);
296 sb.append(" deferrals in alarm recipients");
297 }
298 n = pendingInDeferralsList(mDeferredBroadcasts);
299 if (n > 0) {
300 sb.append(", ");
301 sb.append(n);
302 sb.append(" deferred");
303 }
304 return sb.toString();
Christopher Tate2f558d22019-01-17 16:58:31 -0800305 }
306
307 // ----------------------------------
308 // BroadcastQueue operation support
309
310 void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
311 mOrderedBroadcasts.add(r);
312 }
313
314 // Returns the now-replaced broadcast record, or null if none
315 BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
316 // Simple case, in the ordinary queue.
317 BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);
318
319 // If we didn't find it, less-simple: in a deferral queue?
320 if (old == null) {
321 old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging);
322 }
323 if (old == null) {
324 old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
325 }
326 return old;
327 }
328
329 private BroadcastRecord replaceDeferredBroadcastLocked(ArrayList<Deferrals> list,
330 BroadcastRecord r, String typeForLogging) {
331 BroadcastRecord old;
332 final int numEntries = list.size();
333 for (int i = 0; i < numEntries; i++) {
334 final Deferrals d = list.get(i);
335 old = replaceBroadcastLocked(d.broadcasts, r, typeForLogging);
336 if (old != null) {
337 return old;
338 }
339 }
340 return null;
341 }
342
343 private BroadcastRecord replaceBroadcastLocked(ArrayList<BroadcastRecord> list,
344 BroadcastRecord r, String typeForLogging) {
345 BroadcastRecord old;
346 final Intent intent = r.intent;
347 // Any in-flight broadcast has already been popped, and cannot be replaced.
348 // (This preserves existing behavior of the replacement API)
Christopher Tated4caf852019-01-29 12:32:31 -0800349 for (int i = list.size() - 1; i >= 0; i--) {
Christopher Tate2f558d22019-01-17 16:58:31 -0800350 old = list.get(i);
351 if (old.userId == r.userId && intent.filterEquals(old.intent)) {
352 if (DEBUG_BROADCAST) {
353 Slog.v(TAG, "***** Replacing " + typeForLogging
354 + " [" + mQueue.mQueueName + "]: " + intent);
355 }
356 // Clone deferral state too if any
357 r.deferred = old.deferred;
358 list.set(i, r);
359 return old;
360 }
361 }
362 return null;
363 }
364
365 boolean cleanupDisabledPackageReceiversLocked(final String packageName,
366 Set<String> filterByClasses, final int userId, final boolean doit) {
367 // Note: fast short circuits when 'doit' is false, as soon as we hit any
368 // "yes we would do something" circumstance
369 boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
370 packageName, filterByClasses, userId, doit);
371 if (doit || !didSomething) {
372 didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
373 packageName, filterByClasses, userId, doit);
374 }
375 if (doit || !didSomething) {
376 didSomething |= cleanupDeferralsListDisabledReceiversLocked(mDeferredBroadcasts,
377 packageName, filterByClasses, userId, doit);
378 }
379 if ((doit || !didSomething) && mCurrentBroadcast != null) {
380 didSomething |= mCurrentBroadcast.cleanupDisabledPackageReceiversLocked(
381 packageName, filterByClasses, userId, doit);
382 }
383
384 return didSomething;
385 }
386
387 private boolean cleanupDeferralsListDisabledReceiversLocked(ArrayList<Deferrals> list,
388 final String packageName, Set<String> filterByClasses, final int userId,
389 final boolean doit) {
390 boolean didSomething = false;
391 for (Deferrals d : list) {
392 didSomething = cleanupBroadcastListDisabledReceiversLocked(d.broadcasts,
393 packageName, filterByClasses, userId, doit);
394 if (!doit && didSomething) {
395 return true;
396 }
397 }
398 return didSomething;
399 }
400
401 private boolean cleanupBroadcastListDisabledReceiversLocked(ArrayList<BroadcastRecord> list,
402 final String packageName, Set<String> filterByClasses, final int userId,
403 final boolean doit) {
404 boolean didSomething = false;
405 for (BroadcastRecord br : list) {
406 didSomething |= br.cleanupDisabledPackageReceiversLocked(packageName,
407 filterByClasses, userId, doit);
408 if (!doit && didSomething) {
409 return true;
410 }
411 }
412 return didSomething;
413 }
414
415 /**
416 * Standard proto dump entry point
417 */
418 public void writeToProto(ProtoOutputStream proto, long fieldId) {
419 if (mCurrentBroadcast != null) {
420 mCurrentBroadcast.writeToProto(proto, fieldId);
421 }
422 for (Deferrals d : mAlarmBroadcasts) {
423 d.writeToProto(proto, fieldId);
424 }
425 for (BroadcastRecord br : mOrderedBroadcasts) {
426 br.writeToProto(proto, fieldId);
427 }
428 for (Deferrals d : mDeferredBroadcasts) {
429 d.writeToProto(proto, fieldId);
430 }
431 }
432
433 // ----------------------------------
434 // Dispatch & deferral management
435
436 public BroadcastRecord getActiveBroadcastLocked() {
437 return mCurrentBroadcast;
438 }
439
440 /**
441 * If there is a deferred broadcast that is being sent to an alarm target, return
442 * that one. If there's no deferred alarm target broadcast but there is one
443 * that has reached the end of its deferral, return that.
444 *
445 * This stages the broadcast internally until it is retired, and returns that
446 * staged record if this is called repeatedly, until retireBroadcast(r) is called.
447 */
448 public BroadcastRecord getNextBroadcastLocked(final long now) {
449 if (mCurrentBroadcast != null) {
450 return mCurrentBroadcast;
451 }
452
453 BroadcastRecord next = null;
454 if (!mAlarmBroadcasts.isEmpty()) {
455 next = popLocked(mAlarmBroadcasts);
456 if (DEBUG_BROADCAST_DEFERRAL && next != null) {
457 Slog.i(TAG, "Next broadcast from alarm targets: " + next);
458 }
459 }
460
461 if (next == null && !mDeferredBroadcasts.isEmpty()) {
462 for (int i = 0; i < mDeferredBroadcasts.size(); i++) {
463 Deferrals d = mDeferredBroadcasts.get(i);
464 if (now < d.deferUntil) {
465 // No more deferrals due
466 break;
467 }
468
469 if (d.broadcasts.size() > 0) {
470 next = d.broadcasts.remove(0);
471 // apply deferral-interval decay policy and move this uid's
472 // deferred broadcasts down in the delivery queue accordingly
473 mDeferredBroadcasts.remove(i); // already 'd'
474 d.deferredBy = calculateDeferral(d.deferredBy);
475 d.deferUntil += d.deferredBy;
476 insertLocked(mDeferredBroadcasts, d);
477 if (DEBUG_BROADCAST_DEFERRAL) {
478 Slog.i(TAG, "Next broadcast from deferrals " + next
479 + ", deferUntil now " + d.deferUntil);
480 }
481 break;
482 }
483 }
484 }
485
486 if (next == null && !mOrderedBroadcasts.isEmpty()) {
487 next = mOrderedBroadcasts.remove(0);
488 if (DEBUG_BROADCAST_DEFERRAL) {
489 Slog.i(TAG, "Next broadcast from main queue: " + next);
490 }
491 }
492
493 mCurrentBroadcast = next;
494 return next;
495 }
496
497 /**
498 * Called after the broadcast queue finishes processing the currently
499 * active broadcast (obtained by calling getNextBroadcastLocked()).
500 */
501 public void retireBroadcastLocked(final BroadcastRecord r) {
502 // ERROR if 'r' is not the active broadcast
503 if (r != mCurrentBroadcast) {
504 Slog.wtf(TAG, "Retiring broadcast " + r
505 + " doesn't match current outgoing " + mCurrentBroadcast);
506 }
507 mCurrentBroadcast = null;
508 }
509
510 /**
511 * Called prior to broadcast dispatch to check whether the intended
512 * recipient is currently subject to deferral policy.
513 */
514 public boolean isDeferringLocked(final int uid) {
515 Deferrals d = findUidLocked(uid);
516 if (d != null && d.broadcasts.isEmpty()) {
517 // once we've caught up with deferred broadcasts to this uid
518 // and time has advanced sufficiently that we wouldn't be
519 // deferring newly-enqueued ones, we're back to normal policy.
520 if (SystemClock.uptimeMillis() >= d.deferUntil) {
521 if (DEBUG_BROADCAST_DEFERRAL) {
522 Slog.i(TAG, "No longer deferring broadcasts to uid " + d.uid);
523 }
524 removeDeferral(d);
525 return false;
526 }
527 }
528 return (d != null);
529 }
530
531 /**
532 * Defer broadcasts for the given app. If 'br' is non-null, this also makes
533 * sure that broadcast record is enqueued as the next upcoming broadcast for
534 * the app.
535 */
536 public void startDeferring(final int uid) {
537 synchronized (mLock) {
538 Deferrals d = findUidLocked(uid);
539
540 // If we're not yet tracking this app, set up that bookkeeping
541 if (d == null) {
542 // Start a new deferral
543 final long now = SystemClock.uptimeMillis();
544 d = new Deferrals(uid,
545 now,
546 mConstants.DEFERRAL,
547 mAlarmUids.get(uid, 0));
548 if (DEBUG_BROADCAST_DEFERRAL) {
549 Slog.i(TAG, "Now deferring broadcasts to " + uid
550 + " until " + d.deferUntil);
551 }
552 // where it goes depends on whether it is coming into an alarm-related situation
553 if (d.alarmCount == 0) {
554 // common case, put it in the ordinary priority queue
555 insertLocked(mDeferredBroadcasts, d);
556 scheduleDeferralCheckLocked(true);
557 } else {
558 // alarm-related: strict order-encountered
559 mAlarmBroadcasts.add(d);
560 }
561 } else {
562 // We're already deferring, but something was slow again. Reset the
563 // deferral decay progression.
564 d.deferredBy = mConstants.DEFERRAL;
565 if (DEBUG_BROADCAST_DEFERRAL) {
566 Slog.i(TAG, "Uid " + uid + " slow again, deferral interval reset to "
567 + d.deferredBy);
568 }
569 }
570 }
571 }
572
573 /**
574 * Key entry point when a broadcast about to be delivered is instead
575 * set aside for deferred delivery
576 */
577 public void addDeferredBroadcast(final int uid, BroadcastRecord br) {
578 if (DEBUG_BROADCAST_DEFERRAL) {
579 Slog.i(TAG, "Enqueuing deferred broadcast " + br);
580 }
581 synchronized (mLock) {
582 Deferrals d = findUidLocked(uid);
583 if (d == null) {
584 Slog.wtf(TAG, "Adding deferred broadcast but not tracking " + uid);
585 } else {
586 if (br == null) {
587 Slog.wtf(TAG, "Deferring null broadcast to " + uid);
588 } else {
589 br.deferred = true;
590 d.add(br);
591 }
592 }
593 }
594 }
595
596 /**
597 * When there are deferred broadcasts, we need to make sure to recheck the
598 * dispatch queue when they come due. Alarm-sensitive deferrals get dispatched
599 * aggressively, so we only need to use the ordinary deferrals timing to figure
600 * out when to recheck.
601 */
602 public void scheduleDeferralCheckLocked(boolean force) {
603 if ((force || !mRecheckScheduled) && !mDeferredBroadcasts.isEmpty()) {
604 final Deferrals d = mDeferredBroadcasts.get(0);
605 if (!d.broadcasts.isEmpty()) {
606 mHandler.removeCallbacks(mScheduleRunnable);
607 mHandler.postAtTime(mScheduleRunnable, d.deferUntil);
608 mRecheckScheduled = true;
609 if (DEBUG_BROADCAST_DEFERRAL) {
610 Slog.i(TAG, "Scheduling deferred broadcast recheck at " + d.deferUntil);
611 }
612 }
613 }
614 }
615
Christopher Tateb486f412019-02-12 18:05:17 -0800616 /**
617 * Cancel all current deferrals; that is, make all currently-deferred broadcasts
618 * immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
619 */
Christopher Tate01c3ae12019-02-27 16:51:10 -0800620 public void cancelDeferralsLocked() {
621 zeroDeferralTimes(mAlarmBroadcasts);
622 zeroDeferralTimes(mDeferredBroadcasts);
Christopher Tateb486f412019-02-12 18:05:17 -0800623 }
624
625 private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
626 final int num = list.size();
627 for (int i = 0; i < num; i++) {
628 Deferrals d = list.get(i);
629 // Safe to do this in-place because it won't break ordering
630 d.deferUntil = d.deferredBy = 0;
631 }
632 }
633
Christopher Tate2f558d22019-01-17 16:58:31 -0800634 // ----------------------------------
635
636 /**
637 * If broadcasts to this uid are being deferred, find the deferrals record about it.
638 * @return null if this uid's broadcasts are not being deferred
639 */
640 private Deferrals findUidLocked(final int uid) {
641 // The common case is that they it isn't also an alarm target...
642 Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
643 // ...but if not there, also check alarm-prioritized deferrals
644 if (d == null) {
645 d = findUidLocked(uid, mAlarmBroadcasts);
646 }
647 return d;
648 }
649
650 /**
651 * Remove the given deferral record from whichever queue it might be in at present
652 * @return true if the deferral was in fact found, false if this made no changes
653 */
654 private boolean removeDeferral(Deferrals d) {
655 boolean didRemove = mDeferredBroadcasts.remove(d);
656 if (!didRemove) {
657 didRemove = mAlarmBroadcasts.remove(d);
658 }
659 return didRemove;
660 }
661
662 /**
663 * Find the deferrals record for the given uid in the given list
664 */
665 private static Deferrals findUidLocked(final int uid, ArrayList<Deferrals> list) {
666 final int numElements = list.size();
667 for (int i = 0; i < numElements; i++) {
668 Deferrals d = list.get(i);
669 if (uid == d.uid) {
670 return d;
671 }
672 }
673 return null;
674 }
675
676 /**
677 * Pop the next broadcast record from the head of the given deferrals list,
678 * if one exists.
679 */
680 private static BroadcastRecord popLocked(ArrayList<Deferrals> list) {
681 final Deferrals d = list.get(0);
682 return d.broadcasts.isEmpty() ? null : d.broadcasts.remove(0);
683 }
684
685 /**
686 * Insert the given Deferrals into the priority queue, sorted by defer-until milestone
687 */
688 private static void insertLocked(ArrayList<Deferrals> list, Deferrals d) {
689 // Simple linear search is appropriate here because we expect to
690 // have very few entries in the deferral lists (i.e. very few badly-
691 // behaving apps currently facing deferral)
692 int i;
693 final int numElements = list.size();
694 for (i = 0; i < numElements; i++) {
695 if (d.deferUntil < list.get(i).deferUntil) {
696 break;
697 }
698 }
699 list.add(i, d);
700 }
701
702 /**
703 * Calculate a new deferral time based on the previous time. This should decay
704 * toward zero, though a small nonzero floor is an option.
705 */
706 private long calculateDeferral(long previous) {
707 return Math.max(mConstants.DEFERRAL_FLOOR,
708 (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR));
709 }
710
711 // ----------------------------------
712
713 boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
714 SimpleDateFormat sdf) {
715 final Dumper dumper = new Dumper(pw, queueName, dumpPackage, sdf);
716 boolean printed = false;
717
718 dumper.setHeading("Active ordered broadcasts");
719 dumper.setLabel("Active Ordered Broadcast");
720 for (Deferrals d : mAlarmBroadcasts) {
721 d.dumpLocked(dumper);
722 }
723 printed |= dumper.didPrint();
724
725 for (BroadcastRecord br : mOrderedBroadcasts) {
726 dumper.dump(br);
727 }
728 printed |= dumper.didPrint();
729
730 dumper.setHeading("Deferred ordered broadcasts");
731 dumper.setLabel("Deferred Ordered Broadcast");
732 for (Deferrals d : mDeferredBroadcasts) {
733 d.dumpLocked(dumper);
734 }
735 printed |= dumper.didPrint();
736
737 return printed;
738 }
739}