• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.am;
18 
19 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
20 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
21 
22 import android.content.Intent;
23 import android.os.Handler;
24 import android.os.SystemClock;
25 import android.util.Slog;
26 import android.util.SparseIntArray;
27 import android.util.proto.ProtoOutputStream;
28 
29 import com.android.server.AlarmManagerInternal;
30 import com.android.server.LocalServices;
31 
32 import java.io.PrintWriter;
33 import java.text.SimpleDateFormat;
34 import java.util.ArrayList;
35 import java.util.Set;
36 
37 /**
38  * Manages ordered broadcast delivery, applying policy to mitigate the effects of
39  * slow receivers.
40  */
41 public 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 
Deferrals(int uid, long now, long backoff, int count)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 
add(BroadcastRecord br)64         void add(BroadcastRecord br) {
65             broadcasts.add(br);
66         }
67 
size()68         int size() {
69             return broadcasts.size();
70         }
71 
isEmpty()72         boolean isEmpty() {
73             return broadcasts.isEmpty();
74         }
75 
writeToProto(ProtoOutputStream proto, long fieldId)76         void writeToProto(ProtoOutputStream proto, long fieldId) {
77             for (BroadcastRecord br : broadcasts) {
78                 br.writeToProto(proto, fieldId);
79             }
80         }
81 
dumpLocked(Dumper d)82         void dumpLocked(Dumper d) {
83             for (BroadcastRecord br : broadcasts) {
84                 d.dump(br);
85             }
86         }
87 
88         @Override
toString()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 
Dumper(PrintWriter pw, String queueName, String dumpPackage, SimpleDateFormat sdf)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 
setHeading(String heading)124         void setHeading(String heading) {
125             mHeading = heading;
126             mPrinted = false;
127         }
128 
setLabel(String label)129         void setLabel(String label) {
130             //"  Active Ordered Broadcast " + mQueueName + " #" + i + ":"
131             mLabel = "  " + label + " " + mQueueName + " #";
132             mOrdinal = 0;
133         }
134 
didPrint()135         boolean didPrint() {
136             return mPrinted;
137         }
138 
dump(BroadcastRecord br)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      */
BroadcastDispatcher(BroadcastQueue queue, BroadcastConstants constants, Handler handler, Object lock)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      */
start()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      */
isEmpty()259     public boolean isEmpty() {
260         synchronized (mLock) {
261             return mCurrentBroadcast == null
262                     && mOrderedBroadcasts.isEmpty()
263                     && isDeferralsListEmpty(mDeferredBroadcasts)
264                     && isDeferralsListEmpty(mAlarmBroadcasts);
265         }
266     }
267 
pendingInDeferralsList(ArrayList<Deferrals> list)268     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();
273         }
274         return pending;
275     }
276 
isDeferralsListEmpty(ArrayList<Deferrals> list)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      */
describeStateLocked()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();
305     }
306 
307     // ----------------------------------
308     // BroadcastQueue operation support
309 
enqueueOrderedBroadcastLocked(BroadcastRecord r)310     void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
311         mOrderedBroadcasts.add(r);
312     }
313 
314     // Returns the now-replaced broadcast record, or null if none
replaceBroadcastLocked(BroadcastRecord r, String typeForLogging)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 
replaceDeferredBroadcastLocked(ArrayList<Deferrals> list, BroadcastRecord r, String typeForLogging)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 
replaceBroadcastLocked(ArrayList<BroadcastRecord> list, BroadcastRecord r, String typeForLogging)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)
349         for (int i = list.size() - 1; i >= 0; i--) {
350             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 
cleanupDisabledPackageReceiversLocked(final String packageName, Set<String> filterByClasses, final int userId, final boolean doit)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 
cleanupDeferralsListDisabledReceiversLocked(ArrayList<Deferrals> list, final String packageName, Set<String> filterByClasses, final int userId, final boolean doit)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 
cleanupBroadcastListDisabledReceiversLocked(ArrayList<BroadcastRecord> list, final String packageName, Set<String> filterByClasses, final int userId, final boolean doit)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      */
writeToProto(ProtoOutputStream proto, long fieldId)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 
getActiveBroadcastLocked()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      */
getNextBroadcastLocked(final long now)448     public BroadcastRecord getNextBroadcastLocked(final long now) {
449         if (mCurrentBroadcast != null) {
450             return mCurrentBroadcast;
451         }
452 
453         final boolean someQueued = !mOrderedBroadcasts.isEmpty();
454 
455         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()) {
464             // 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 
469             for (int i = 0; i < mDeferredBroadcasts.size(); i++) {
470                 Deferrals d = mDeferredBroadcasts.get(i);
471                 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.
476                     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 
496         if (next == null && someQueued) {
497             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      */
retireBroadcastLocked(final BroadcastRecord r)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      */
isDeferringLocked(final int uid)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      */
startDeferring(final int uid)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      */
addDeferredBroadcast(final int uid, BroadcastRecord br)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      */
scheduleDeferralCheckLocked(boolean force)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 
626     /**
627      * Cancel all current deferrals; that is, make all currently-deferred broadcasts
628      * immediately deliverable.  Used by the wait-for-broadcast-idle mechanism.
629      */
cancelDeferralsLocked()630     public void cancelDeferralsLocked() {
631         zeroDeferralTimes(mAlarmBroadcasts);
632         zeroDeferralTimes(mDeferredBroadcasts);
633     }
634 
zeroDeferralTimes(ArrayList<Deferrals> list)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 
644     // ----------------------------------
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      */
findUidLocked(final int uid)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      */
removeDeferral(Deferrals d)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      */
findUidLocked(final int uid, ArrayList<Deferrals> list)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      */
popLocked(ArrayList<Deferrals> list)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      */
insertLocked(ArrayList<Deferrals> list, Deferrals d)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      */
calculateDeferral(long previous)716     private long calculateDeferral(long previous) {
717         return Math.max(mConstants.DEFERRAL_FLOOR,
718                 (long) (previous * mConstants.DEFERRAL_DECAY_FACTOR));
719     }
720 
721     // ----------------------------------
722 
dumpLocked(PrintWriter pw, String dumpPackage, String queueName, SimpleDateFormat sdf)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("Currently in flight");
729         dumper.setLabel("In-Flight Ordered Broadcast");
730         if (mCurrentBroadcast != null) {
731             dumper.dump(mCurrentBroadcast);
732         } else {
733             pw.println("  (null)");
734         }
735 
736         dumper.setHeading("Active ordered broadcasts");
737         dumper.setLabel("Active Ordered Broadcast");
738         for (Deferrals d : mAlarmBroadcasts) {
739             d.dumpLocked(dumper);
740         }
741         printed |= dumper.didPrint();
742 
743         for (BroadcastRecord br : mOrderedBroadcasts) {
744             dumper.dump(br);
745         }
746         printed |= dumper.didPrint();
747 
748         dumper.setHeading("Deferred ordered broadcasts");
749         dumper.setLabel("Deferred Ordered Broadcast");
750         for (Deferrals d : mDeferredBroadcasts) {
751             d.dumpLocked(dumper);
752         }
753         printed |= dumper.didPrint();
754 
755         return printed;
756     }
757 }
758