blob: d029482ac52888aca6b54a368fc3762af7a93119 [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
Christopher Tatebb271a12019-03-08 17:43:30 -0800453 final boolean someQueued = !mOrderedBroadcasts.isEmpty();
454
Christopher Tate2f558d22019-01-17 16:58:31 -0800455 BroadcastRecord next = null;
456 if (!mAlarmBroadcasts.isEmpty()) {
457 next = popLocked(mAlarmBroadcasts);
458 if (DEBUG_BROADCAST_DEFERRAL && next != null) {
459 Slog.i(TAG, "Next broadcast from alarm targets: " + next);
460 }
461 }
462
463 if (next == null && !mDeferredBroadcasts.isEmpty()) {
Christopher Tatebb271a12019-03-08 17:43:30 -0800464 // We're going to deliver either:
465 // 1. the next "overdue" deferral; or
466 // 2. the next ordinary ordered broadcast; *or*
467 // 3. the next not-yet-overdue deferral.
468
Christopher Tate2f558d22019-01-17 16:58:31 -0800469 for (int i = 0; i < mDeferredBroadcasts.size(); i++) {
470 Deferrals d = mDeferredBroadcasts.get(i);
Christopher Tatebb271a12019-03-08 17:43:30 -0800471 if (now < d.deferUntil && someQueued) {
472 // stop looking when we haven't hit the next time-out boundary
473 // but only if we have un-deferred broadcasts waiting,
474 // otherwise we can deliver whatever deferred broadcast
475 // is next available.
Christopher Tate2f558d22019-01-17 16:58:31 -0800476 break;
477 }
478
479 if (d.broadcasts.size() > 0) {
480 next = d.broadcasts.remove(0);
481 // apply deferral-interval decay policy and move this uid's
482 // deferred broadcasts down in the delivery queue accordingly
483 mDeferredBroadcasts.remove(i); // already 'd'
484 d.deferredBy = calculateDeferral(d.deferredBy);
485 d.deferUntil += d.deferredBy;
486 insertLocked(mDeferredBroadcasts, d);
487 if (DEBUG_BROADCAST_DEFERRAL) {
488 Slog.i(TAG, "Next broadcast from deferrals " + next
489 + ", deferUntil now " + d.deferUntil);
490 }
491 break;
492 }
493 }
494 }
495
Christopher Tatebb271a12019-03-08 17:43:30 -0800496 if (next == null && someQueued) {
Christopher Tate2f558d22019-01-17 16:58:31 -0800497 next = mOrderedBroadcasts.remove(0);
498 if (DEBUG_BROADCAST_DEFERRAL) {
499 Slog.i(TAG, "Next broadcast from main queue: " + next);
500 }
501 }
502
503 mCurrentBroadcast = next;
504 return next;
505 }
506
507 /**
508 * Called after the broadcast queue finishes processing the currently
509 * active broadcast (obtained by calling getNextBroadcastLocked()).
510 */
511 public void retireBroadcastLocked(final BroadcastRecord r) {
512 // ERROR if 'r' is not the active broadcast
513 if (r != mCurrentBroadcast) {
514 Slog.wtf(TAG, "Retiring broadcast " + r
515 + " doesn't match current outgoing " + mCurrentBroadcast);
516 }
517 mCurrentBroadcast = null;
518 }
519
520 /**
521 * Called prior to broadcast dispatch to check whether the intended
522 * recipient is currently subject to deferral policy.
523 */
524 public boolean isDeferringLocked(final int uid) {
525 Deferrals d = findUidLocked(uid);
526 if (d != null && d.broadcasts.isEmpty()) {
527 // once we've caught up with deferred broadcasts to this uid
528 // and time has advanced sufficiently that we wouldn't be
529 // deferring newly-enqueued ones, we're back to normal policy.
530 if (SystemClock.uptimeMillis() >= d.deferUntil) {
531 if (DEBUG_BROADCAST_DEFERRAL) {
532 Slog.i(TAG, "No longer deferring broadcasts to uid " + d.uid);
533 }
534 removeDeferral(d);
535 return false;
536 }
537 }
538 return (d != null);
539 }
540
541 /**
542 * Defer broadcasts for the given app. If 'br' is non-null, this also makes
543 * sure that broadcast record is enqueued as the next upcoming broadcast for
544 * the app.
545 */
546 public void startDeferring(final int uid) {
547 synchronized (mLock) {
548 Deferrals d = findUidLocked(uid);
549
550 // If we're not yet tracking this app, set up that bookkeeping
551 if (d == null) {
552 // Start a new deferral
553 final long now = SystemClock.uptimeMillis();
554 d = new Deferrals(uid,
555 now,
556 mConstants.DEFERRAL,
557 mAlarmUids.get(uid, 0));
558 if (DEBUG_BROADCAST_DEFERRAL) {
559 Slog.i(TAG, "Now deferring broadcasts to " + uid
560 + " until " + d.deferUntil);
561 }
562 // where it goes depends on whether it is coming into an alarm-related situation
563 if (d.alarmCount == 0) {
564 // common case, put it in the ordinary priority queue
565 insertLocked(mDeferredBroadcasts, d);
566 scheduleDeferralCheckLocked(true);
567 } else {
568 // alarm-related: strict order-encountered
569 mAlarmBroadcasts.add(d);
570 }
571 } else {
572 // We're already deferring, but something was slow again. Reset the
573 // deferral decay progression.
574 d.deferredBy = mConstants.DEFERRAL;
575 if (DEBUG_BROADCAST_DEFERRAL) {
576 Slog.i(TAG, "Uid " + uid + " slow again, deferral interval reset to "
577 + d.deferredBy);
578 }
579 }
580 }
581 }
582
583 /**
584 * Key entry point when a broadcast about to be delivered is instead
585 * set aside for deferred delivery
586 */
587 public void addDeferredBroadcast(final int uid, BroadcastRecord br) {
588 if (DEBUG_BROADCAST_DEFERRAL) {
589 Slog.i(TAG, "Enqueuing deferred broadcast " + br);
590 }
591 synchronized (mLock) {
592 Deferrals d = findUidLocked(uid);
593 if (d == null) {
594 Slog.wtf(TAG, "Adding deferred broadcast but not tracking " + uid);
595 } else {
596 if (br == null) {
597 Slog.wtf(TAG, "Deferring null broadcast to " + uid);
598 } else {
599 br.deferred = true;
600 d.add(br);
601 }
602 }
603 }
604 }
605
606 /**
607 * When there are deferred broadcasts, we need to make sure to recheck the
608 * dispatch queue when they come due. Alarm-sensitive deferrals get dispatched
609 * aggressively, so we only need to use the ordinary deferrals timing to figure
610 * out when to recheck.
611 */
612 public void scheduleDeferralCheckLocked(boolean force) {
613 if ((force || !mRecheckScheduled) && !mDeferredBroadcasts.isEmpty()) {
614 final Deferrals d = mDeferredBroadcasts.get(0);
615 if (!d.broadcasts.isEmpty()) {
616 mHandler.removeCallbacks(mScheduleRunnable);
617 mHandler.postAtTime(mScheduleRunnable, d.deferUntil);
618 mRecheckScheduled = true;
619 if (DEBUG_BROADCAST_DEFERRAL) {
620 Slog.i(TAG, "Scheduling deferred broadcast recheck at " + d.deferUntil);
621 }
622 }
623 }
624 }
625
Christopher Tateb486f412019-02-12 18:05:17 -0800626 /**
627 * Cancel all current deferrals; that is, make all currently-deferred broadcasts
628 * immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
629 */
Christopher Tate01c3ae12019-02-27 16:51:10 -0800630 public void cancelDeferralsLocked() {
631 zeroDeferralTimes(mAlarmBroadcasts);
632 zeroDeferralTimes(mDeferredBroadcasts);
Christopher Tateb486f412019-02-12 18:05:17 -0800633 }
634
635 private static void zeroDeferralTimes(ArrayList<Deferrals> list) {
636 final int num = list.size();
637 for (int i = 0; i < num; i++) {
638 Deferrals d = list.get(i);
639 // Safe to do this in-place because it won't break ordering
640 d.deferUntil = d.deferredBy = 0;
641 }
642 }
643
Christopher Tate2f558d22019-01-17 16:58:31 -0800644 // ----------------------------------
645
646 /**
647 * If broadcasts to this uid are being deferred, find the deferrals record about it.
648 * @return null if this uid's broadcasts are not being deferred
649 */
650 private Deferrals findUidLocked(final int uid) {
651 // The common case is that they it isn't also an alarm target...
652 Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
653 // ...but if not there, also check alarm-prioritized deferrals
654 if (d == null) {
655 d = findUidLocked(uid, mAlarmBroadcasts);
656 }
657 return d;
658 }
659
660 /**
661 * Remove the given deferral record from whichever queue it might be in at present
662 * @return true if the deferral was in fact found, false if this made no changes
663 */
664 private boolean removeDeferral(Deferrals d) {
665 boolean didRemove = mDeferredBroadcasts.remove(d);
666 if (!didRemove) {
667 didRemove = mAlarmBroadcasts.remove(d);
668 }
669 return didRemove;
670 }
671
672 /**
673 * Find the deferrals record for the given uid in the given list
674 */
675 private static Deferrals findUidLocked(final int uid, ArrayList<Deferrals> list) {
676 final int numElements = list.size();
677 for (int i = 0; i < numElements; i++) {
678 Deferrals d = list.get(i);
679 if (uid == d.uid) {
680 return d;
681 }
682 }
683 return null;
684 }
685
686 /**
687 * Pop the next broadcast record from the head of the given deferrals list,
688 * if one exists.
689 */
690 private static BroadcastRecord popLocked(ArrayList<Deferrals> list) {
691 final Deferrals d = list.get(0);
692 return d.broadcasts.isEmpty() ? null : d.broadcasts.remove(0);
693 }
694
695 /**
696 * Insert the given Deferrals into the priority queue, sorted by defer-until milestone
697 */
698 private static void insertLocked(ArrayList<Deferrals> list, Deferrals d) {
699 // Simple linear search is appropriate here because we expect to
700 // have very few entries in the deferral lists (i.e. very few badly-
701 // behaving apps currently facing deferral)
702 int i;
703 final int numElements = list.size();
704 for (i = 0; i < numElements; i++) {
705 if (d.deferUntil < list.get(i).deferUntil) {
706 break;
707 }
708 }
709 list.add(i, d);
710 }
711
712 /**
713 * Calculate a new deferral time based on the previous time. This should decay
714 * toward zero, though a small nonzero floor is an option.
715 */
716 private long calculateDeferral(long previous) {
717 return Math.max(mConstants.DEFERRAL_FLOOR,
718 (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR));
719 }
720
721 // ----------------------------------
722
723 boolean dumpLocked(PrintWriter pw, String dumpPackage, String queueName,
724 SimpleDateFormat sdf) {
725 final Dumper dumper = new Dumper(pw, queueName, dumpPackage, sdf);
726 boolean printed = false;
727
728 dumper.setHeading("Active ordered broadcasts");
729 dumper.setLabel("Active Ordered Broadcast");
730 for (Deferrals d : mAlarmBroadcasts) {
731 d.dumpLocked(dumper);
732 }
733 printed |= dumper.didPrint();
734
735 for (BroadcastRecord br : mOrderedBroadcasts) {
736 dumper.dump(br);
737 }
738 printed |= dumper.didPrint();
739
740 dumper.setHeading("Deferred ordered broadcasts");
741 dumper.setLabel("Deferred Ordered Broadcast");
742 for (Deferrals d : mDeferredBroadcasts) {
743 d.dumpLocked(dumper);
744 }
745 printed |= dumper.didPrint();
746
747 return printed;
748 }
749}