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 dumpDebug(ProtoOutputStream proto, long fieldId)76 void dumpDebug(ProtoOutputStream proto, long fieldId) { 77 for (BroadcastRecord br : broadcasts) { 78 br.dumpDebug(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 */ dumpDebug(ProtoOutputStream proto, long fieldId)418 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 419 if (mCurrentBroadcast != null) { 420 mCurrentBroadcast.dumpDebug(proto, fieldId); 421 } 422 for (Deferrals d : mAlarmBroadcasts) { 423 d.dumpDebug(proto, fieldId); 424 } 425 for (BroadcastRecord br : mOrderedBroadcasts) { 426 br.dumpDebug(proto, fieldId); 427 } 428 for (Deferrals d : mDeferredBroadcasts) { 429 d.dumpDebug(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