1 /* 2 * Copyright (C) 2006 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 android.os; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.SuppressLint; 23 import android.annotation.TestApi; 24 import android.app.ActivityThread; 25 import android.app.Instrumentation; 26 import android.compat.annotation.UnsupportedAppUsage; 27 import android.os.Process; 28 import android.os.UserHandle; 29 import android.ravenwood.annotation.RavenwoodKeepWholeClass; 30 import android.ravenwood.annotation.RavenwoodRedirect; 31 import android.ravenwood.annotation.RavenwoodRedirectionClass; 32 import android.ravenwood.annotation.RavenwoodReplace; 33 import android.ravenwood.annotation.RavenwoodThrow; 34 import android.util.Log; 35 import android.util.Printer; 36 import android.util.SparseArray; 37 import android.util.proto.ProtoOutputStream; 38 39 import com.android.internal.annotations.GuardedBy; 40 import com.android.internal.ravenwood.RavenwoodEnvironment; 41 42 import dalvik.annotation.optimization.NeverCompile; 43 44 import java.io.FileDescriptor; 45 import java.lang.annotation.Retention; 46 import java.lang.annotation.RetentionPolicy; 47 import java.lang.invoke.MethodHandles; 48 import java.lang.invoke.VarHandle; 49 import java.util.ArrayList; 50 import java.util.Iterator; 51 import java.util.NoSuchElementException; 52 import java.util.concurrent.ConcurrentSkipListSet; 53 import java.util.concurrent.atomic.AtomicInteger; 54 import java.util.concurrent.atomic.AtomicLong; 55 import java.util.concurrent.locks.Condition; 56 import java.util.concurrent.locks.ReentrantLock; 57 58 /** 59 * Low-level class holding the list of messages to be dispatched by a 60 * {@link Looper}. Messages are not added directly to a MessageQueue, 61 * but rather through {@link Handler} objects associated with the Looper. 62 * 63 * <p>You can retrieve the MessageQueue for the current thread with 64 * {@link Looper#myQueue() Looper.myQueue()}. 65 */ 66 @RavenwoodKeepWholeClass 67 @RavenwoodRedirectionClass("MessageQueue_ravenwood") 68 public final class MessageQueue { 69 private static final String TAG_L = "LegacyMessageQueue"; 70 private static final String TAG_C = "ConcurrentMessageQueue"; 71 private static final boolean DEBUG = false; 72 private static final boolean TRACE = false; 73 74 // True if the message queue can be quit. 75 @UnsupportedAppUsage 76 private final boolean mQuitAllowed; 77 78 @UnsupportedAppUsage 79 @SuppressWarnings("unused") 80 private long mPtr; // used by native code 81 82 @UnsupportedAppUsage 83 Message mMessages; 84 private Message mLast; 85 @UnsupportedAppUsage 86 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); 87 private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; 88 private IdleHandler[] mPendingIdleHandlers; 89 private boolean mQuitting; 90 91 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. 92 private boolean mBlocked; 93 94 // Tracks the number of async message. We use this in enqueueMessage() to avoid searching the 95 // queue for async messages when inserting a message at the tail. 96 private int mAsyncMessageCount; 97 98 /** 99 * Select between two implementations of message queue. The legacy implementation is used 100 * by default as it provides maximum compatibility with applications and tests that 101 * reach into MessageQueue via the mMessages field. The concurrent implemmentation is used for 102 * system processes and provides a higher level of concurrency and higher enqueue throughput 103 * than the legacy implementation. 104 */ 105 private final boolean mUseConcurrent; 106 107 /** 108 * Caches process-level checks that determine `mUseConcurrent`. 109 * This is to avoid redoing checks that shouldn't change during the process's lifetime. 110 */ 111 private static Boolean sIsProcessAllowedToUseConcurrent = null; 112 113 @RavenwoodRedirect nativeInit()114 private native static long nativeInit(); 115 @RavenwoodRedirect nativeDestroy(long ptr)116 private native static void nativeDestroy(long ptr); 117 @UnsupportedAppUsage 118 @RavenwoodRedirect nativePollOnce(long ptr, int timeoutMillis)119 private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ 120 @RavenwoodRedirect nativeWake(long ptr)121 private native static void nativeWake(long ptr); 122 @RavenwoodRedirect nativeIsPolling(long ptr)123 private native static boolean nativeIsPolling(long ptr); 124 @RavenwoodRedirect nativeSetFileDescriptorEvents(long ptr, int fd, int events)125 private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); 126 MessageQueue(boolean quitAllowed)127 MessageQueue(boolean quitAllowed) { 128 initIsProcessAllowedToUseConcurrent(); 129 mUseConcurrent = sIsProcessAllowedToUseConcurrent && !isInstrumenting(); 130 mQuitAllowed = quitAllowed; 131 mPtr = nativeInit(); 132 } 133 initIsProcessAllowedToUseConcurrent()134 private static void initIsProcessAllowedToUseConcurrent() { 135 if (sIsProcessAllowedToUseConcurrent != null) { 136 return; 137 } 138 139 if (RavenwoodEnvironment.getInstance().isRunningOnRavenwood()) { 140 sIsProcessAllowedToUseConcurrent = false; 141 return; 142 } 143 144 final String processName = Process.myProcessName(); 145 if (processName == null) { 146 // Assume that this is a host-side test and avoid concurrent mode for now. 147 sIsProcessAllowedToUseConcurrent = false; 148 return; 149 } 150 151 // Concurrent mode modifies behavior that is observable via reflection and is commonly 152 // used by tests. 153 // For now, we limit it to system processes to avoid breaking apps and their tests. 154 sIsProcessAllowedToUseConcurrent = UserHandle.isCore(Process.myUid()); 155 156 if (sIsProcessAllowedToUseConcurrent) { 157 // Some platform tests run in core UIDs. 158 // Use this awful heuristic to detect them. 159 if (processName.contains("test") || processName.contains("Test")) { 160 sIsProcessAllowedToUseConcurrent = false; 161 } 162 } else { 163 // Also explicitly allow SystemUI processes. 164 // SystemUI doesn't run in a core UID, but we want to give it the performance boost, 165 // and we know that it's safe to use the concurrent implementation in SystemUI. 166 sIsProcessAllowedToUseConcurrent = 167 processName.equals("com.android.systemui") 168 || processName.startsWith("com.android.systemui:"); 169 // On Android distributions where SystemUI has a different process name, 170 // the above condition may need to be adjusted accordingly. 171 } 172 173 // We can lift these restrictions in the future after we've made it possible for test 174 // authors to test Looper and MessageQueue without resorting to reflection. 175 176 // Holdback study. 177 if (sIsProcessAllowedToUseConcurrent && Flags.messageQueueForceLegacy()) { 178 sIsProcessAllowedToUseConcurrent = false; 179 } 180 } 181 182 @RavenwoodReplace throwIfNotTest()183 private static void throwIfNotTest() { 184 final ActivityThread activityThread = ActivityThread.currentActivityThread(); 185 if (activityThread == null) { 186 // Only tests can reach here. 187 return; 188 } 189 final Instrumentation instrumentation = activityThread.getInstrumentation(); 190 if (instrumentation == null) { 191 // Only tests can reach here. 192 return; 193 } 194 if (instrumentation.isInstrumenting()) { 195 return; 196 } 197 throw new IllegalStateException("Test-only API called not from a test!"); 198 } 199 throwIfNotTest$ravenwood()200 private static void throwIfNotTest$ravenwood() { 201 return; 202 } 203 isInstrumenting()204 private static boolean isInstrumenting() { 205 final ActivityThread activityThread = ActivityThread.currentActivityThread(); 206 if (activityThread == null) { 207 return false; 208 } 209 final Instrumentation instrumentation = activityThread.getInstrumentation(); 210 return instrumentation != null && instrumentation.isInstrumenting(); 211 } 212 213 @Override finalize()214 protected void finalize() throws Throwable { 215 try { 216 dispose(); 217 } finally { 218 super.finalize(); 219 } 220 } 221 222 // Disposes of the underlying message queue. 223 // Must only be called on the looper thread or the finalizer. dispose()224 private void dispose() { 225 if (mPtr != 0) { 226 nativeDestroy(mPtr); 227 mPtr = 0; 228 } 229 } 230 231 private static final class MatchDeliverableMessages extends MessageCompare { 232 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)233 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 234 long when) { 235 return n.mMessage.when <= when; 236 } 237 } 238 private final MatchDeliverableMessages mMatchDeliverableMessages = 239 new MatchDeliverableMessages(); 240 /** 241 * Returns true if the looper has no pending messages which are due to be processed. 242 * 243 * <p>This method is safe to call from any thread. 244 * 245 * @return True if the looper is idle. 246 */ isIdle()247 public boolean isIdle() { 248 if (mUseConcurrent) { 249 final long now = SystemClock.uptimeMillis(); 250 251 if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) { 252 return false; 253 } 254 255 MessageNode msgNode = null; 256 MessageNode asyncMsgNode = null; 257 258 if (!mPriorityQueue.isEmpty()) { 259 try { 260 msgNode = mPriorityQueue.first(); 261 } catch (NoSuchElementException e) { } 262 } 263 264 if (!mAsyncPriorityQueue.isEmpty()) { 265 try { 266 asyncMsgNode = mAsyncPriorityQueue.first(); 267 } catch (NoSuchElementException e) { } 268 } 269 270 if ((msgNode != null && msgNode.getWhen() <= now) 271 || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) { 272 return false; 273 } 274 275 return true; 276 } else { 277 synchronized (this) { 278 final long now = SystemClock.uptimeMillis(); 279 return mMessages == null || now < mMessages.when; 280 } 281 } 282 } 283 284 /** 285 * Add a new {@link IdleHandler} to this message queue. This may be 286 * removed automatically for you by returning false from 287 * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is 288 * invoked, or explicitly removing it with {@link #removeIdleHandler}. 289 * 290 * <p>This method is safe to call from any thread. 291 * 292 * @param handler The IdleHandler to be added. 293 */ addIdleHandler(@onNull IdleHandler handler)294 public void addIdleHandler(@NonNull IdleHandler handler) { 295 if (handler == null) { 296 throw new NullPointerException("Can't add a null IdleHandler"); 297 } 298 if (mUseConcurrent) { 299 synchronized (mIdleHandlersLock) { 300 mIdleHandlers.add(handler); 301 } 302 } else { 303 synchronized (this) { 304 mIdleHandlers.add(handler); 305 } 306 } 307 } 308 309 /** 310 * Remove an {@link IdleHandler} from the queue that was previously added 311 * with {@link #addIdleHandler}. If the given object is not currently 312 * in the idle list, nothing is done. 313 * 314 * <p>This method is safe to call from any thread. 315 * 316 * @param handler The IdleHandler to be removed. 317 */ removeIdleHandler(@onNull IdleHandler handler)318 public void removeIdleHandler(@NonNull IdleHandler handler) { 319 if (mUseConcurrent) { 320 synchronized (mIdleHandlersLock) { 321 mIdleHandlers.remove(handler); 322 } 323 } else { 324 synchronized (this) { 325 mIdleHandlers.remove(handler); 326 } 327 } 328 } 329 330 /** 331 * Returns whether this looper's thread is currently polling for more work to do. 332 * This is a good signal that the loop is still alive rather than being stuck 333 * handling a callback. Note that this method is intrinsically racy, since the 334 * state of the loop can change before you get the result back. 335 * 336 * <p>This method is safe to call from any thread. 337 * 338 * @return True if the looper is currently polling for events. 339 * @hide 340 */ isPolling()341 public boolean isPolling() { 342 if (mUseConcurrent) { 343 // If the loop is quitting then it must not be idling. 344 // We can assume mPtr != 0 when sQuitting is false. 345 return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr); 346 } else { 347 synchronized (this) { 348 return isPollingLocked(); 349 } 350 } 351 } 352 isPollingLocked()353 private boolean isPollingLocked() { 354 // If the loop is quitting then it must not be idling. 355 // We can assume mPtr != 0 when mQuitting is false. 356 return !mQuitting && nativeIsPolling(mPtr); 357 } 358 359 /** 360 * Adds a file descriptor listener to receive notification when file descriptor 361 * related events occur. 362 * <p> 363 * If the file descriptor has already been registered, the specified events 364 * and listener will replace any that were previously associated with it. 365 * It is not possible to set more than one listener per file descriptor. 366 * </p><p> 367 * It is important to always unregister the listener when the file descriptor 368 * is no longer of use. 369 * </p> 370 * 371 * @param fd The file descriptor for which a listener will be registered. 372 * @param events The set of events to receive: a combination of the 373 * {@link OnFileDescriptorEventListener#EVENT_INPUT}, 374 * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and 375 * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested 376 * set of events is zero, then the listener is unregistered. 377 * @param listener The listener to invoke when file descriptor events occur. 378 * 379 * @see OnFileDescriptorEventListener 380 * @see #removeOnFileDescriptorEventListener 381 */ 382 @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) addOnFileDescriptorEventListener(@onNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener)383 public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, 384 @OnFileDescriptorEventListener.Events int events, 385 @NonNull OnFileDescriptorEventListener listener) { 386 if (fd == null) { 387 throw new IllegalArgumentException("fd must not be null"); 388 } 389 if (listener == null) { 390 throw new IllegalArgumentException("listener must not be null"); 391 } 392 393 if (mUseConcurrent) { 394 synchronized (mFileDescriptorRecordsLock) { 395 updateOnFileDescriptorEventListenerLocked(fd, events, listener); 396 } 397 } else { 398 synchronized (this) { 399 updateOnFileDescriptorEventListenerLocked(fd, events, listener); 400 } 401 } 402 } 403 404 /** 405 * Removes a file descriptor listener. 406 * <p> 407 * This method does nothing if no listener has been registered for the 408 * specified file descriptor. 409 * </p> 410 * 411 * @param fd The file descriptor whose listener will be unregistered. 412 * 413 * @see OnFileDescriptorEventListener 414 * @see #addOnFileDescriptorEventListener 415 */ 416 @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) removeOnFileDescriptorEventListener(@onNull FileDescriptor fd)417 public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { 418 if (fd == null) { 419 throw new IllegalArgumentException("fd must not be null"); 420 } 421 if (mUseConcurrent) { 422 synchronized (mFileDescriptorRecordsLock) { 423 updateOnFileDescriptorEventListenerLocked(fd, 0, null); 424 } 425 } else { 426 synchronized (this) { 427 updateOnFileDescriptorEventListenerLocked(fd, 0, null); 428 } 429 } 430 } 431 432 @RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class) updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, OnFileDescriptorEventListener listener)433 private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, 434 OnFileDescriptorEventListener listener) { 435 final int fdNum = fd.getInt$(); 436 437 int index = -1; 438 FileDescriptorRecord record = null; 439 if (mFileDescriptorRecords != null) { 440 index = mFileDescriptorRecords.indexOfKey(fdNum); 441 if (index >= 0) { 442 record = mFileDescriptorRecords.valueAt(index); 443 if (record != null && record.mEvents == events) { 444 return; 445 } 446 } 447 } 448 449 if (events != 0) { 450 events |= OnFileDescriptorEventListener.EVENT_ERROR; 451 if (record == null) { 452 if (mFileDescriptorRecords == null) { 453 mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); 454 } 455 record = new FileDescriptorRecord(fd, events, listener); 456 mFileDescriptorRecords.put(fdNum, record); 457 } else { 458 record.mListener = listener; 459 record.mEvents = events; 460 record.mSeq += 1; 461 } 462 nativeSetFileDescriptorEvents(mPtr, fdNum, events); 463 } else if (record != null) { 464 record.mEvents = 0; 465 mFileDescriptorRecords.removeAt(index); 466 nativeSetFileDescriptorEvents(mPtr, fdNum, 0); 467 } 468 } 469 470 // Called from native code. 471 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) dispatchEvents(int fd, int events)472 private int dispatchEvents(int fd, int events) { 473 // Get the file descriptor record and any state that might change. 474 final FileDescriptorRecord record; 475 final int oldWatchedEvents; 476 final OnFileDescriptorEventListener listener; 477 final int seq; 478 if (mUseConcurrent) { 479 synchronized (mFileDescriptorRecordsLock) { 480 record = mFileDescriptorRecords.get(fd); 481 if (record == null) { 482 return 0; // spurious, no listener registered 483 } 484 485 oldWatchedEvents = record.mEvents; 486 events &= oldWatchedEvents; // filter events based on current watched set 487 if (events == 0) { 488 return oldWatchedEvents; // spurious, watched events changed 489 } 490 491 listener = record.mListener; 492 seq = record.mSeq; 493 } 494 } else { 495 synchronized (this) { 496 record = mFileDescriptorRecords.get(fd); 497 if (record == null) { 498 return 0; // spurious, no listener registered 499 } 500 501 oldWatchedEvents = record.mEvents; 502 events &= oldWatchedEvents; // filter events based on current watched set 503 if (events == 0) { 504 return oldWatchedEvents; // spurious, watched events changed 505 } 506 507 listener = record.mListener; 508 seq = record.mSeq; 509 } 510 } 511 // Invoke the listener outside of the lock. 512 int newWatchedEvents = listener.onFileDescriptorEvents( 513 record.mDescriptor, events); 514 if (newWatchedEvents != 0) { 515 newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; 516 } 517 518 // Update the file descriptor record if the listener changed the set of 519 // events to watch and the listener itself hasn't been updated since. 520 if (newWatchedEvents != oldWatchedEvents) { 521 if (mUseConcurrent) { 522 synchronized (mFileDescriptorRecordsLock) { 523 int index = mFileDescriptorRecords.indexOfKey(fd); 524 if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record 525 && record.mSeq == seq) { 526 record.mEvents = newWatchedEvents; 527 if (newWatchedEvents == 0) { 528 mFileDescriptorRecords.removeAt(index); 529 } 530 } 531 } 532 } else { 533 synchronized (this) { 534 int index = mFileDescriptorRecords.indexOfKey(fd); 535 if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record 536 && record.mSeq == seq) { 537 record.mEvents = newWatchedEvents; 538 if (newWatchedEvents == 0) { 539 mFileDescriptorRecords.removeAt(index); 540 } 541 } 542 } 543 } 544 } 545 546 // Return the new set of events to watch for native code to take care of. 547 return newWatchedEvents; 548 } 549 550 private static final AtomicLong mMessagesDelivered = new AtomicLong(); 551 552 /* This is only read/written from the Looper thread. For use with Concurrent MQ */ 553 private int mNextPollTimeoutMillis; 554 private boolean mMessageDirectlyQueued; nextMessage(boolean peek)555 private Message nextMessage(boolean peek) { 556 int i = 0; 557 558 while (true) { 559 if (DEBUG) { 560 Log.d(TAG_C, "nextMessage loop #" + i); 561 i++; 562 } 563 564 mDrainingLock.lock(); 565 mNextIsDrainingStack = true; 566 mDrainingLock.unlock(); 567 568 /* 569 * Set our state to active, drain any items from the stack into our priority queues 570 */ 571 StackNode oldTop; 572 oldTop = swapAndSetStackStateActive(); 573 drainStack(oldTop); 574 575 mDrainingLock.lock(); 576 mNextIsDrainingStack = false; 577 mDrainCompleted.signalAll(); 578 mDrainingLock.unlock(); 579 580 /* 581 * The objective of this next block of code is to: 582 * - find a message to return (if any is ready) 583 * - find a next message we would like to return, after scheduling. 584 * - we make our scheduling decision based on this next message (if it exists). 585 * 586 * We have two queues to juggle and the presence of barriers throws an additional 587 * wrench into our plans. 588 * 589 * The last wrinkle is that remove() may delete items from underneath us. If we hit 590 * that case, we simply restart the loop. 591 */ 592 593 /* Get the first node from each queue */ 594 Iterator<MessageNode> queueIter = mPriorityQueue.iterator(); 595 MessageNode msgNode = iterateNext(queueIter); 596 Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator(); 597 MessageNode asyncMsgNode = iterateNext(asyncQueueIter); 598 599 if (DEBUG) { 600 if (msgNode != null) { 601 Message msg = msgNode.mMessage; 602 Log.d(TAG_C, "Next found node what: " + msg.what + " when: " + msg.when 603 + " seq: " + msgNode.mInsertSeq + "barrier: " 604 + msgNode.isBarrier() + " now: " + SystemClock.uptimeMillis()); 605 } 606 if (asyncMsgNode != null) { 607 Message msg = asyncMsgNode.mMessage; 608 Log.d(TAG_C, "Next found async node what: " + msg.what + " when: " + msg.when 609 + " seq: " + asyncMsgNode.mInsertSeq + "barrier: " 610 + asyncMsgNode.isBarrier() + " now: " 611 + SystemClock.uptimeMillis()); 612 } 613 } 614 615 /* 616 * the node which we will return, null if none are ready 617 */ 618 MessageNode found = null; 619 /* 620 * The node from which we will determine our next wakeup time. 621 * Null indicates there is no next message ready. If we found a node, 622 * we can leave this null as Looper will call us again after delivering 623 * the message. 624 */ 625 MessageNode next = null; 626 627 long now = SystemClock.uptimeMillis(); 628 /* 629 * If we have a barrier we should return the async node (if it exists and is ready) 630 */ 631 if (msgNode != null && msgNode.isBarrier()) { 632 if (asyncMsgNode != null && now >= asyncMsgNode.getWhen()) { 633 found = asyncMsgNode; 634 } else { 635 next = asyncMsgNode; 636 } 637 } else { /* No barrier. */ 638 MessageNode earliest; 639 /* 640 * If we have two messages, pick the earliest option from either queue. 641 * Otherwise grab whichever node is non-null. If both are null we'll fall through. 642 */ 643 earliest = pickEarliestNode(msgNode, asyncMsgNode); 644 645 if (earliest != null) { 646 if (now >= earliest.getWhen()) { 647 found = earliest; 648 } else { 649 next = earliest; 650 } 651 } 652 } 653 654 if (DEBUG) { 655 if (found != null) { 656 Message msg = found.mMessage; 657 Log.d(TAG_C, " Will deliver node what: " + msg.what + " when: " + msg.when 658 + " seq: " + found.mInsertSeq + " barrier: " + found.isBarrier() 659 + " async: " + found.isAsync() + " now: " 660 + SystemClock.uptimeMillis()); 661 } else { 662 Log.d(TAG_C, "No node to deliver"); 663 } 664 if (next != null) { 665 Message msg = next.mMessage; 666 Log.d(TAG_C, "Next node what: " + msg.what + " when: " + msg.when + " seq: " 667 + next.mInsertSeq + " barrier: " + next.isBarrier() + " async: " 668 + next.isAsync() 669 + " now: " + SystemClock.uptimeMillis()); 670 } else { 671 Log.d(TAG_C, "No next node"); 672 } 673 } 674 675 /* 676 * If we have a found message, we will get called again so there's no need to set state. 677 * In that case we can leave our state as ACTIVE. 678 * 679 * Otherwise we should determine how to park the thread. 680 */ 681 StateNode nextOp = sStackStateActive; 682 if (found == null) { 683 if (next == null) { 684 /* No message to deliver, sleep indefinitely */ 685 mNextPollTimeoutMillis = -1; 686 nextOp = sStackStateParked; 687 if (DEBUG) { 688 Log.d(TAG_C, "nextMessage next state is StackStateParked"); 689 } 690 } else { 691 /* Message not ready, or we found one to deliver already, set a timeout */ 692 long nextMessageWhen = next.getWhen(); 693 if (nextMessageWhen > now) { 694 mNextPollTimeoutMillis = (int) Math.min(nextMessageWhen - now, 695 Integer.MAX_VALUE); 696 } else { 697 mNextPollTimeoutMillis = 0; 698 } 699 700 mStackStateTimedPark.mWhenToWake = now + mNextPollTimeoutMillis; 701 nextOp = mStackStateTimedPark; 702 if (DEBUG) { 703 Log.d(TAG_C, "nextMessage next state is StackStateTimedParked timeout ms " 704 + mNextPollTimeoutMillis + " mWhenToWake: " 705 + mStackStateTimedPark.mWhenToWake + " now " + now); 706 } 707 } 708 } 709 710 /* 711 * Try to swap our state from Active back to Park or TimedPark. If we raced with 712 * enqueue, loop back around to pick up any new items. 713 */ 714 if (sState.compareAndSet(this, sStackStateActive, nextOp)) { 715 mMessageCounts.clearCounts(); 716 if (found != null) { 717 if (!peek && !removeFromPriorityQueue(found)) { 718 /* 719 * RemoveMessages() might be able to pull messages out from under us 720 * However we can detect that here and just loop around if it happens. 721 */ 722 continue; 723 } 724 725 if (TRACE) { 726 Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); 727 } 728 return found.mMessage; 729 } 730 return null; 731 } 732 } 733 } 734 nextConcurrent()735 private Message nextConcurrent() { 736 final long ptr = mPtr; 737 if (ptr == 0) { 738 return null; 739 } 740 741 mNextPollTimeoutMillis = 0; 742 int pendingIdleHandlerCount = -1; // -1 only during first iteration 743 while (true) { 744 if (mNextPollTimeoutMillis != 0) { 745 Binder.flushPendingCommands(); 746 } 747 748 mMessageDirectlyQueued = false; 749 nativePollOnce(ptr, mNextPollTimeoutMillis); 750 751 Message msg = nextMessage(false); 752 if (msg != null) { 753 msg.markInUse(); 754 return msg; 755 } 756 757 if ((boolean) sQuitting.getVolatile(this)) { 758 return null; 759 } 760 761 synchronized (mIdleHandlersLock) { 762 // If first time idle, then get the number of idlers to run. 763 // Idle handles only run if the queue is empty or if the first message 764 // in the queue (possibly a barrier) is due to be handled in the future. 765 if (pendingIdleHandlerCount < 0 766 && isIdle()) { 767 pendingIdleHandlerCount = mIdleHandlers.size(); 768 } 769 if (pendingIdleHandlerCount <= 0) { 770 // No idle handlers to run. Loop and wait some more. 771 continue; 772 } 773 774 if (mPendingIdleHandlers == null) { 775 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 776 } 777 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 778 } 779 780 // Run the idle handlers. 781 // We only ever reach this code block during the first iteration. 782 for (int i = 0; i < pendingIdleHandlerCount; i++) { 783 final IdleHandler idler = mPendingIdleHandlers[i]; 784 mPendingIdleHandlers[i] = null; // release the reference to the handler 785 786 boolean keep = false; 787 try { 788 keep = idler.queueIdle(); 789 } catch (Throwable t) { 790 Log.wtf(TAG_C, "IdleHandler threw exception", t); 791 } 792 793 if (!keep) { 794 synchronized (mIdleHandlersLock) { 795 mIdleHandlers.remove(idler); 796 } 797 } 798 } 799 800 // Reset the idle handler count to 0 so we do not run them again. 801 pendingIdleHandlerCount = 0; 802 803 // While calling an idle handler, a new message could have been delivered 804 // so go back and look again for a pending message without waiting. 805 mNextPollTimeoutMillis = 0; 806 } 807 } 808 809 @UnsupportedAppUsage next()810 Message next() { 811 if (mUseConcurrent) { 812 return nextConcurrent(); 813 } 814 815 // Return here if the message loop has already quit and been disposed. 816 // This can happen if the application tries to restart a looper after quit 817 // which is not supported. 818 final long ptr = mPtr; 819 if (ptr == 0) { 820 return null; 821 } 822 823 int pendingIdleHandlerCount = -1; // -1 only during first iteration 824 int nextPollTimeoutMillis = 0; 825 for (;;) { 826 if (nextPollTimeoutMillis != 0) { 827 Binder.flushPendingCommands(); 828 } 829 830 nativePollOnce(ptr, nextPollTimeoutMillis); 831 832 synchronized (this) { 833 // Try to retrieve the next message. Return if found. 834 final long now = SystemClock.uptimeMillis(); 835 Message prevMsg = null; 836 Message msg = mMessages; 837 if (msg != null && msg.target == null) { 838 // Stalled by a barrier. Find the next asynchronous message in the queue. 839 do { 840 prevMsg = msg; 841 msg = msg.next; 842 } while (msg != null && !msg.isAsynchronous()); 843 } 844 if (msg != null) { 845 if (now < msg.when) { 846 // Next message is not ready. Set a timeout to wake up when it is ready. 847 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 848 } else { 849 // Got a message. 850 mBlocked = false; 851 if (prevMsg != null) { 852 prevMsg.next = msg.next; 853 if (prevMsg.next == null) { 854 mLast = prevMsg; 855 } 856 } else { 857 mMessages = msg.next; 858 if (msg.next == null) { 859 mLast = null; 860 } 861 } 862 msg.next = null; 863 if (DEBUG) Log.v(TAG_L, "Returning message: " + msg); 864 msg.markInUse(); 865 if (msg.isAsynchronous()) { 866 mAsyncMessageCount--; 867 } 868 if (TRACE) { 869 Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); 870 } 871 return msg; 872 } 873 } else { 874 // No more messages. 875 nextPollTimeoutMillis = -1; 876 } 877 878 // Process the quit message now that all pending messages have been handled. 879 if (mQuitting) { 880 dispose(); 881 return null; 882 } 883 884 // If first time idle, then get the number of idlers to run. 885 // Idle handles only run if the queue is empty or if the first message 886 // in the queue (possibly a barrier) is due to be handled in the future. 887 if (pendingIdleHandlerCount < 0 888 && (mMessages == null || now < mMessages.when)) { 889 pendingIdleHandlerCount = mIdleHandlers.size(); 890 } 891 if (pendingIdleHandlerCount <= 0) { 892 // No idle handlers to run. Loop and wait some more. 893 mBlocked = true; 894 continue; 895 } 896 897 if (mPendingIdleHandlers == null) { 898 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 899 } 900 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 901 } 902 903 // Run the idle handlers. 904 // We only ever reach this code block during the first iteration. 905 for (int i = 0; i < pendingIdleHandlerCount; i++) { 906 final IdleHandler idler = mPendingIdleHandlers[i]; 907 mPendingIdleHandlers[i] = null; // release the reference to the handler 908 909 boolean keep = false; 910 try { 911 keep = idler.queueIdle(); 912 } catch (Throwable t) { 913 Log.wtf(TAG_L, "IdleHandler threw exception", t); 914 } 915 916 if (!keep) { 917 synchronized (this) { 918 mIdleHandlers.remove(idler); 919 } 920 } 921 } 922 923 // Reset the idle handler count to 0 so we do not run them again. 924 pendingIdleHandlerCount = 0; 925 926 // While calling an idle handler, a new message could have been delivered 927 // so go back and look again for a pending message without waiting. 928 nextPollTimeoutMillis = 0; 929 } 930 } 931 quit(boolean safe)932 void quit(boolean safe) { 933 if (!mQuitAllowed) { 934 throw new IllegalStateException("Main thread not allowed to quit."); 935 } 936 937 if (mUseConcurrent) { 938 synchronized (mIdleHandlersLock) { 939 if (sQuitting.compareAndSet(this, false, true)) { 940 if (safe) { 941 removeAllFutureMessages(); 942 } else { 943 removeAllMessages(); 944 } 945 946 // We can assume mPtr != 0 because sQuitting was previously false. 947 nativeWake(mPtr); 948 } 949 } 950 } else { 951 synchronized (this) { 952 if (mQuitting) { 953 return; 954 } 955 mQuitting = true; 956 957 if (safe) { 958 removeAllFutureMessagesLocked(); 959 } else { 960 removeAllMessagesLocked(); 961 } 962 963 // We can assume mPtr != 0 because mQuitting was previously false. 964 nativeWake(mPtr); 965 } 966 } 967 } 968 969 /** 970 * Posts a synchronization barrier to the Looper's message queue. 971 * 972 * Message processing occurs as usual until the message queue encounters the 973 * synchronization barrier that has been posted. When the barrier is encountered, 974 * later synchronous messages in the queue are stalled (prevented from being executed) 975 * until the barrier is released by calling {@link #removeSyncBarrier} and specifying 976 * the token that identifies the synchronization barrier. 977 * 978 * This method is used to immediately postpone execution of all subsequently posted 979 * synchronous messages until a condition is met that releases the barrier. 980 * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier 981 * and continue to be processed as usual. 982 * 983 * This call must be always matched by a call to {@link #removeSyncBarrier} with 984 * the same token to ensure that the message queue resumes normal operation. 985 * Otherwise the application will probably hang! 986 * 987 * @return A token that uniquely identifies the barrier. This token must be 988 * passed to {@link #removeSyncBarrier} to release the barrier. 989 * 990 * @hide 991 */ 992 @UnsupportedAppUsage 993 @TestApi postSyncBarrier()994 public int postSyncBarrier() { 995 return postSyncBarrier(SystemClock.uptimeMillis()); 996 } 997 postSyncBarrier(long when)998 private int postSyncBarrier(long when) { 999 // Enqueue a new sync barrier token. 1000 // We don't need to wake the queue because the purpose of a barrier is to stall it. 1001 if (mUseConcurrent) { 1002 final int token = mNextBarrierTokenAtomic.getAndIncrement(); 1003 1004 // b/376573804: apps and tests may expect to be able to use reflection 1005 // to read this value. Make some effort to support this legacy use case. 1006 mNextBarrierToken = token + 1; 1007 1008 final Message msg = Message.obtain(); 1009 1010 msg.markInUse(); 1011 msg.arg1 = token; 1012 1013 if (!enqueueMessageUnchecked(msg, when)) { 1014 Log.wtf(TAG_C, "Unexpected error while adding sync barrier!"); 1015 return -1; 1016 } 1017 1018 return token; 1019 } 1020 1021 synchronized (this) { 1022 final int token = mNextBarrierToken++; 1023 final Message msg = Message.obtain(); 1024 msg.markInUse(); 1025 msg.when = when; 1026 msg.arg1 = token; 1027 1028 if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) { 1029 /* Message goes to tail of list */ 1030 mLast.next = msg; 1031 mLast = msg; 1032 msg.next = null; 1033 return token; 1034 } 1035 1036 Message prev = null; 1037 Message p = mMessages; 1038 if (when != 0) { 1039 while (p != null && p.when <= when) { 1040 prev = p; 1041 p = p.next; 1042 } 1043 } 1044 1045 if (p == null) { 1046 /* We reached the tail of the list, or list is empty. */ 1047 mLast = msg; 1048 } 1049 1050 if (prev != null) { // invariant: p == prev.next 1051 msg.next = p; 1052 prev.next = msg; 1053 } else { 1054 msg.next = p; 1055 mMessages = msg; 1056 } 1057 return token; 1058 } 1059 } 1060 1061 private static final class MatchBarrierToken extends MessageCompare { 1062 int mBarrierToken; 1063 MatchBarrierToken(int token)1064 MatchBarrierToken(int token) { 1065 super(); 1066 mBarrierToken = token; 1067 } 1068 1069 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1070 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1071 long when) { 1072 final Message m = n.mMessage; 1073 if (m.target == null && m.arg1 == mBarrierToken) { 1074 return true; 1075 } 1076 return false; 1077 } 1078 } 1079 1080 /** 1081 * Removes a synchronization barrier. 1082 * 1083 * @param token The synchronization barrier token that was returned by 1084 * {@link #postSyncBarrier}. 1085 * 1086 * @throws IllegalStateException if the barrier was not found. 1087 * 1088 * @hide 1089 */ 1090 @UnsupportedAppUsage 1091 @TestApi removeSyncBarrier(int token)1092 public void removeSyncBarrier(int token) { 1093 // Remove a sync barrier token from the queue. 1094 // If the queue is no longer stalled by a barrier then wake it. 1095 if (mUseConcurrent) { 1096 boolean removed; 1097 MessageNode first; 1098 final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token); 1099 1100 try { 1101 /* Retain the first element to see if we are currently stuck on a barrier. */ 1102 first = mPriorityQueue.first(); 1103 } catch (NoSuchElementException e) { 1104 /* The queue is empty */ 1105 first = null; 1106 } 1107 1108 removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true); 1109 if (removed && first != null) { 1110 Message m = first.mMessage; 1111 if (m.target == null && m.arg1 == token) { 1112 /* Wake up next() in case it was sleeping on this barrier. */ 1113 nativeWake(mPtr); 1114 } 1115 } else if (!removed) { 1116 throw new IllegalStateException("The specified message queue synchronization " 1117 + " barrier token has not been posted or has already been removed."); 1118 } 1119 return; 1120 } 1121 1122 synchronized (this) { 1123 Message prev = null; 1124 Message p = mMessages; 1125 while (p != null && (p.target != null || p.arg1 != token)) { 1126 prev = p; 1127 p = p.next; 1128 } 1129 if (p == null) { 1130 throw new IllegalStateException("The specified message queue synchronization " 1131 + " barrier token has not been posted or has already been removed."); 1132 } 1133 final boolean needWake; 1134 if (prev != null) { 1135 prev.next = p.next; 1136 if (prev.next == null) { 1137 mLast = prev; 1138 } 1139 needWake = false; 1140 } else { 1141 mMessages = p.next; 1142 if (mMessages == null) { 1143 mLast = null; 1144 } 1145 needWake = mMessages == null || mMessages.target != null; 1146 } 1147 p.recycleUnchecked(); 1148 1149 // If the loop is quitting then it is already awake. 1150 // We can assume mPtr != 0 when mQuitting is false. 1151 if (needWake && !mQuitting) { 1152 nativeWake(mPtr); 1153 } 1154 } 1155 } 1156 enqueueMessage(Message msg, long when)1157 boolean enqueueMessage(Message msg, long when) { 1158 if (msg.target == null) { 1159 throw new IllegalArgumentException("Message must have a target."); 1160 } 1161 1162 if (mUseConcurrent) { 1163 if (msg.isInUse()) { 1164 throw new IllegalStateException(msg + " This message is already in use."); 1165 } 1166 1167 return enqueueMessageUnchecked(msg, when); 1168 } 1169 1170 synchronized (this) { 1171 if (msg.isInUse()) { 1172 throw new IllegalStateException(msg + " This message is already in use."); 1173 } 1174 1175 if (mQuitting) { 1176 IllegalStateException e = new IllegalStateException( 1177 msg.target + " sending message to a Handler on a dead thread"); 1178 Log.w(TAG_L, e.getMessage(), e); 1179 msg.recycle(); 1180 return false; 1181 } 1182 1183 msg.markInUse(); 1184 msg.when = when; 1185 Message p = mMessages; 1186 boolean needWake; 1187 if (p == null || when == 0 || when < p.when) { 1188 // New head, wake up the event queue if blocked. 1189 msg.next = p; 1190 mMessages = msg; 1191 needWake = mBlocked; 1192 if (p == null) { 1193 mLast = mMessages; 1194 } 1195 } else { 1196 // Message is to be inserted at tail or middle of queue. Usually we don't have to 1197 // wake up the event queue unless there is a barrier at the head of the queue and 1198 // the message is the earliest asynchronous message in the queue. 1199 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 1200 1201 // For readability, we split this portion of the function into two blocks based on 1202 // whether tail tracking is enabled. This has a minor implication for the case 1203 // where tail tracking is disabled. See the comment below. 1204 if (Flags.messageQueueTailTracking()) { 1205 if (when >= mLast.when) { 1206 needWake = needWake && mAsyncMessageCount == 0; 1207 msg.next = null; 1208 mLast.next = msg; 1209 mLast = msg; 1210 } else { 1211 // Inserted within the middle of the queue. 1212 Message prev; 1213 for (;;) { 1214 prev = p; 1215 p = p.next; 1216 if (p == null || when < p.when) { 1217 break; 1218 } 1219 if (needWake && p.isAsynchronous()) { 1220 needWake = false; 1221 } 1222 } 1223 if (p == null) { 1224 /* Inserting at tail of queue */ 1225 mLast = msg; 1226 } 1227 msg.next = p; // invariant: p == prev.next 1228 prev.next = msg; 1229 } 1230 } else { 1231 Message prev; 1232 for (;;) { 1233 prev = p; 1234 p = p.next; 1235 if (p == null || when < p.when) { 1236 break; 1237 } 1238 if (needWake && p.isAsynchronous()) { 1239 needWake = false; 1240 } 1241 } 1242 msg.next = p; // invariant: p == prev.next 1243 prev.next = msg; 1244 1245 /* 1246 * If this block is executing then we have a build without tail tracking - 1247 * specifically: Flags.messageQueueTailTracking() == false. This is determined 1248 * at build time so the flag won't change on us during runtime. 1249 * 1250 * Since we don't want to pepper the code with extra checks, we only check 1251 * for tail tracking when we might use mLast. Otherwise, we continue to update 1252 * mLast as the tail of the list. 1253 * 1254 * In this case however we are not maintaining mLast correctly. Since we never 1255 * use it, this is fine. However, we run the risk of leaking a reference. 1256 * So set mLast to null in this case to avoid any Message leaks. The other 1257 * sites will never use the value so we are safe against null pointer derefs. 1258 */ 1259 mLast = null; 1260 } 1261 } 1262 1263 if (msg.isAsynchronous()) { 1264 mAsyncMessageCount++; 1265 } 1266 1267 // We can assume mPtr != 0 because mQuitting is false. 1268 if (needWake) { 1269 nativeWake(mPtr); 1270 } 1271 } 1272 return true; 1273 } 1274 legacyPeekOrPoll(boolean peek)1275 private Message legacyPeekOrPoll(boolean peek) { 1276 synchronized (this) { 1277 // Try to retrieve the next message. Return if found. 1278 final long now = SystemClock.uptimeMillis(); 1279 Message prevMsg = null; 1280 Message msg = mMessages; 1281 if (msg != null && msg.target == null) { 1282 // Stalled by a barrier. Find the next asynchronous message in the queue. 1283 do { 1284 prevMsg = msg; 1285 msg = msg.next; 1286 } while (msg != null && !msg.isAsynchronous()); 1287 } 1288 if (msg != null) { 1289 if (now >= msg.when) { 1290 // Got a message. 1291 mBlocked = false; 1292 if (peek) { 1293 return msg; 1294 } 1295 if (prevMsg != null) { 1296 prevMsg.next = msg.next; 1297 if (prevMsg.next == null) { 1298 mLast = prevMsg; 1299 } 1300 } else { 1301 mMessages = msg.next; 1302 if (msg.next == null) { 1303 mLast = null; 1304 } 1305 } 1306 msg.next = null; 1307 msg.markInUse(); 1308 if (msg.isAsynchronous()) { 1309 mAsyncMessageCount--; 1310 } 1311 if (TRACE) { 1312 Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet()); 1313 } 1314 return msg; 1315 } 1316 } 1317 } 1318 return null; 1319 } 1320 1321 /** 1322 * Get the timestamp of the next executable message in our priority queue. 1323 * Returns null if there are no messages ready for delivery. 1324 * 1325 * Caller must ensure that this doesn't race 'next' from the Looper thread. 1326 */ 1327 @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this peekWhenForTest()1328 Long peekWhenForTest() { 1329 throwIfNotTest(); 1330 Message ret; 1331 if (mUseConcurrent) { 1332 ret = nextMessage(true); 1333 } else { 1334 ret = legacyPeekOrPoll(true); 1335 } 1336 return ret != null ? ret.when : null; 1337 } 1338 1339 /** 1340 * Return the next executable message in our priority queue. 1341 * Returns null if there are no messages ready for delivery 1342 * 1343 * Caller must ensure that this doesn't race 'next' from the Looper thread. 1344 */ 1345 @SuppressLint("VisiblySynchronized") // Legacy MessageQueue synchronizes on this 1346 @Nullable pollForTest()1347 Message pollForTest() { 1348 throwIfNotTest(); 1349 if (mUseConcurrent) { 1350 return nextMessage(false); 1351 } else { 1352 return legacyPeekOrPoll(false); 1353 } 1354 } 1355 1356 /** 1357 * @return true if we are blocked on a sync barrier 1358 */ isBlockedOnSyncBarrier()1359 boolean isBlockedOnSyncBarrier() { 1360 throwIfNotTest(); 1361 if (mUseConcurrent) { 1362 Iterator<MessageNode> queueIter = mPriorityQueue.iterator(); 1363 MessageNode queueNode = iterateNext(queueIter); 1364 1365 if (queueNode.isBarrier()) { 1366 long now = SystemClock.uptimeMillis(); 1367 1368 /* Look for a deliverable async node. If one exists we are not blocked. */ 1369 Iterator<MessageNode> asyncQueueIter = mAsyncPriorityQueue.iterator(); 1370 MessageNode asyncNode = iterateNext(asyncQueueIter); 1371 if (asyncNode != null && now >= asyncNode.getWhen()) { 1372 return false; 1373 } 1374 /* 1375 * Look for a deliverable sync node. In this case, if one exists we are blocked 1376 * since the barrier prevents delivery of the Message. 1377 */ 1378 while (queueNode.isBarrier()) { 1379 queueNode = iterateNext(queueIter); 1380 } 1381 if (queueNode != null && now >= queueNode.getWhen()) { 1382 return true; 1383 } 1384 1385 return false; 1386 } 1387 } else { 1388 Message msg = mMessages; 1389 if (msg != null && msg.target == null) { 1390 Message iter = msg; 1391 /* Look for a deliverable async node */ 1392 do { 1393 iter = iter.next; 1394 } while (iter != null && !iter.isAsynchronous()); 1395 1396 long now = SystemClock.uptimeMillis(); 1397 if (iter != null && now >= iter.when) { 1398 return false; 1399 } 1400 /* 1401 * Look for a deliverable sync node. In this case, if one exists we are blocked 1402 * since the barrier prevents delivery of the Message. 1403 */ 1404 iter = msg; 1405 do { 1406 iter = iter.next; 1407 } while (iter != null && (iter.target == null || iter.isAsynchronous())); 1408 1409 if (iter != null && now >= iter.when) { 1410 return true; 1411 } 1412 return false; 1413 } 1414 } 1415 /* No barrier was found. */ 1416 return false; 1417 } 1418 1419 private static final class MatchHandlerWhatAndObject extends MessageCompare { 1420 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1421 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1422 long when) { 1423 final Message m = n.mMessage; 1424 if (m.target == h && m.what == what && (object == null || m.obj == object)) { 1425 return true; 1426 } 1427 return false; 1428 } 1429 } 1430 private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject = 1431 new MatchHandlerWhatAndObject(); hasMessages(Handler h, int what, Object object)1432 boolean hasMessages(Handler h, int what, Object object) { 1433 if (h == null) { 1434 return false; 1435 } 1436 if (mUseConcurrent) { 1437 return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, 1438 false); 1439 } 1440 synchronized (this) { 1441 Message p = mMessages; 1442 while (p != null) { 1443 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 1444 return true; 1445 } 1446 p = p.next; 1447 } 1448 return false; 1449 } 1450 } 1451 1452 private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare { 1453 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1454 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1455 long when) { 1456 final Message m = n.mMessage; 1457 if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) { 1458 return true; 1459 } 1460 return false; 1461 } 1462 } 1463 private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals = 1464 new MatchHandlerWhatAndObjectEquals(); hasEqualMessages(Handler h, int what, Object object)1465 boolean hasEqualMessages(Handler h, int what, Object object) { 1466 if (h == null) { 1467 return false; 1468 } 1469 if (mUseConcurrent) { 1470 return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, 1471 false); 1472 1473 } 1474 synchronized (this) { 1475 Message p = mMessages; 1476 while (p != null) { 1477 if (p.target == h && p.what == what && (object == null || object.equals(p.obj))) { 1478 return true; 1479 } 1480 p = p.next; 1481 } 1482 return false; 1483 } 1484 } 1485 1486 private static final class MatchHandlerRunnableAndObject extends MessageCompare { 1487 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1488 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1489 long when) { 1490 final Message m = n.mMessage; 1491 if (m.target == h && m.callback == r && (object == null || m.obj == object)) { 1492 return true; 1493 } 1494 return false; 1495 } 1496 } 1497 private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject = 1498 new MatchHandlerRunnableAndObject(); 1499 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) hasMessages(Handler h, Runnable r, Object object)1500 boolean hasMessages(Handler h, Runnable r, Object object) { 1501 if (h == null) { 1502 return false; 1503 } 1504 if (mUseConcurrent) { 1505 return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, 1506 false); 1507 } 1508 1509 synchronized (this) { 1510 Message p = mMessages; 1511 while (p != null) { 1512 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 1513 return true; 1514 } 1515 p = p.next; 1516 } 1517 return false; 1518 } 1519 } 1520 1521 private static final class MatchHandler extends MessageCompare { 1522 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1523 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1524 long when) { 1525 return n.mMessage.target == h; 1526 } 1527 } 1528 private final MatchHandler mMatchHandler = new MatchHandler(); hasMessages(Handler h)1529 boolean hasMessages(Handler h) { 1530 if (h == null) { 1531 return false; 1532 } 1533 if (mUseConcurrent) { 1534 return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false); 1535 } 1536 synchronized (this) { 1537 Message p = mMessages; 1538 while (p != null) { 1539 if (p.target == h) { 1540 return true; 1541 } 1542 p = p.next; 1543 } 1544 return false; 1545 } 1546 } 1547 removeMessages(Handler h, int what, Object object)1548 void removeMessages(Handler h, int what, Object object) { 1549 if (h == null) { 1550 return; 1551 } 1552 if (mUseConcurrent) { 1553 findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true); 1554 return; 1555 } 1556 synchronized (this) { 1557 Message p = mMessages; 1558 1559 // Remove all messages at front. 1560 while (p != null && p.target == h && p.what == what 1561 && (object == null || p.obj == object)) { 1562 Message n = p.next; 1563 mMessages = n; 1564 if (p.isAsynchronous()) { 1565 mAsyncMessageCount--; 1566 } 1567 p.recycleUnchecked(); 1568 p = n; 1569 } 1570 1571 if (p == null) { 1572 mLast = mMessages; 1573 } 1574 1575 // Remove all messages after front. 1576 while (p != null) { 1577 Message n = p.next; 1578 if (n != null) { 1579 if (n.target == h && n.what == what 1580 && (object == null || n.obj == object)) { 1581 Message nn = n.next; 1582 if (n.isAsynchronous()) { 1583 mAsyncMessageCount--; 1584 } 1585 n.recycleUnchecked(); 1586 p.next = nn; 1587 if (p.next == null) { 1588 mLast = p; 1589 } 1590 continue; 1591 } 1592 } 1593 p = n; 1594 } 1595 } 1596 } 1597 removeEqualMessages(Handler h, int what, Object object)1598 void removeEqualMessages(Handler h, int what, Object object) { 1599 if (h == null) { 1600 return; 1601 } 1602 1603 if (mUseConcurrent) { 1604 findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true); 1605 return; 1606 } 1607 1608 synchronized (this) { 1609 Message p = mMessages; 1610 1611 // Remove all messages at front. 1612 while (p != null && p.target == h && p.what == what 1613 && (object == null || object.equals(p.obj))) { 1614 Message n = p.next; 1615 mMessages = n; 1616 if (p.isAsynchronous()) { 1617 mAsyncMessageCount--; 1618 } 1619 p.recycleUnchecked(); 1620 p = n; 1621 } 1622 1623 if (p == null) { 1624 mLast = mMessages; 1625 } 1626 1627 // Remove all messages after front. 1628 while (p != null) { 1629 Message n = p.next; 1630 if (n != null) { 1631 if (n.target == h && n.what == what 1632 && (object == null || object.equals(n.obj))) { 1633 Message nn = n.next; 1634 if (n.isAsynchronous()) { 1635 mAsyncMessageCount--; 1636 } 1637 n.recycleUnchecked(); 1638 p.next = nn; 1639 if (p.next == null) { 1640 mLast = p; 1641 } 1642 continue; 1643 } 1644 } 1645 p = n; 1646 } 1647 } 1648 } 1649 removeMessages(Handler h, Runnable r, Object object)1650 void removeMessages(Handler h, Runnable r, Object object) { 1651 if (h == null || r == null) { 1652 return; 1653 } 1654 1655 if (mUseConcurrent) { 1656 findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true); 1657 return; 1658 } 1659 synchronized (this) { 1660 Message p = mMessages; 1661 1662 // Remove all messages at front. 1663 while (p != null && p.target == h && p.callback == r 1664 && (object == null || p.obj == object)) { 1665 Message n = p.next; 1666 mMessages = n; 1667 if (p.isAsynchronous()) { 1668 mAsyncMessageCount--; 1669 } 1670 p.recycleUnchecked(); 1671 p = n; 1672 } 1673 1674 if (p == null) { 1675 mLast = mMessages; 1676 } 1677 1678 // Remove all messages after front. 1679 while (p != null) { 1680 Message n = p.next; 1681 if (n != null) { 1682 if (n.target == h && n.callback == r 1683 && (object == null || n.obj == object)) { 1684 Message nn = n.next; 1685 if (n.isAsynchronous()) { 1686 mAsyncMessageCount--; 1687 } 1688 n.recycleUnchecked(); 1689 p.next = nn; 1690 if (p.next == null) { 1691 mLast = p; 1692 } 1693 continue; 1694 } 1695 } 1696 p = n; 1697 } 1698 } 1699 } 1700 1701 private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare { 1702 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1703 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1704 long when) { 1705 final Message m = n.mMessage; 1706 if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) { 1707 return true; 1708 } 1709 return false; 1710 } 1711 } 1712 private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals = 1713 new MatchHandlerRunnableAndObjectEquals(); removeEqualMessages(Handler h, Runnable r, Object object)1714 void removeEqualMessages(Handler h, Runnable r, Object object) { 1715 if (h == null || r == null) { 1716 return; 1717 } 1718 1719 if (mUseConcurrent) { 1720 findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true); 1721 return; 1722 } 1723 synchronized (this) { 1724 Message p = mMessages; 1725 1726 // Remove all messages at front. 1727 while (p != null && p.target == h && p.callback == r 1728 && (object == null || object.equals(p.obj))) { 1729 Message n = p.next; 1730 mMessages = n; 1731 if (p.isAsynchronous()) { 1732 mAsyncMessageCount--; 1733 } 1734 p.recycleUnchecked(); 1735 p = n; 1736 } 1737 1738 if (p == null) { 1739 mLast = mMessages; 1740 } 1741 1742 // Remove all messages after front. 1743 while (p != null) { 1744 Message n = p.next; 1745 if (n != null) { 1746 if (n.target == h && n.callback == r 1747 && (object == null || object.equals(n.obj))) { 1748 Message nn = n.next; 1749 if (n.isAsynchronous()) { 1750 mAsyncMessageCount--; 1751 } 1752 n.recycleUnchecked(); 1753 p.next = nn; 1754 if (p.next == null) { 1755 mLast = p; 1756 } 1757 continue; 1758 } 1759 } 1760 p = n; 1761 } 1762 } 1763 } 1764 1765 private static final class MatchHandlerAndObject extends MessageCompare { 1766 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1767 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1768 long when) { 1769 final Message m = n.mMessage; 1770 if (m.target == h && (object == null || m.obj == object)) { 1771 return true; 1772 } 1773 return false; 1774 } 1775 } 1776 private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject(); removeCallbacksAndMessages(Handler h, Object object)1777 void removeCallbacksAndMessages(Handler h, Object object) { 1778 if (h == null) { 1779 return; 1780 } 1781 1782 if (mUseConcurrent) { 1783 findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true); 1784 return; 1785 } 1786 synchronized (this) { 1787 Message p = mMessages; 1788 1789 // Remove all messages at front. 1790 while (p != null && p.target == h 1791 && (object == null || p.obj == object)) { 1792 Message n = p.next; 1793 mMessages = n; 1794 if (p.isAsynchronous()) { 1795 mAsyncMessageCount--; 1796 } 1797 p.recycleUnchecked(); 1798 p = n; 1799 } 1800 1801 if (p == null) { 1802 mLast = mMessages; 1803 } 1804 1805 // Remove all messages after front. 1806 while (p != null) { 1807 Message n = p.next; 1808 if (n != null) { 1809 if (n.target == h && (object == null || n.obj == object)) { 1810 Message nn = n.next; 1811 if (n.isAsynchronous()) { 1812 mAsyncMessageCount--; 1813 } 1814 n.recycleUnchecked(); 1815 p.next = nn; 1816 if (p.next == null) { 1817 mLast = p; 1818 } 1819 continue; 1820 } 1821 } 1822 p = n; 1823 } 1824 } 1825 } 1826 1827 private static final class MatchHandlerAndObjectEquals extends MessageCompare { 1828 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1829 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1830 long when) { 1831 final Message m = n.mMessage; 1832 if (m.target == h && (object == null || object.equals(m.obj))) { 1833 return true; 1834 } 1835 return false; 1836 } 1837 } 1838 private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals = 1839 new MatchHandlerAndObjectEquals(); removeCallbacksAndEqualMessages(Handler h, Object object)1840 void removeCallbacksAndEqualMessages(Handler h, Object object) { 1841 if (h == null) { 1842 return; 1843 } 1844 1845 if (mUseConcurrent) { 1846 findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true); 1847 return; 1848 } 1849 synchronized (this) { 1850 Message p = mMessages; 1851 1852 // Remove all messages at front. 1853 while (p != null && p.target == h 1854 && (object == null || object.equals(p.obj))) { 1855 Message n = p.next; 1856 mMessages = n; 1857 if (p.isAsynchronous()) { 1858 mAsyncMessageCount--; 1859 } 1860 p.recycleUnchecked(); 1861 p = n; 1862 } 1863 1864 if (p == null) { 1865 mLast = mMessages; 1866 } 1867 1868 // Remove all messages after front. 1869 while (p != null) { 1870 Message n = p.next; 1871 if (n != null) { 1872 if (n.target == h && (object == null || object.equals(n.obj))) { 1873 Message nn = n.next; 1874 if (n.isAsynchronous()) { 1875 mAsyncMessageCount--; 1876 } 1877 n.recycleUnchecked(); 1878 p.next = nn; 1879 if (p.next == null) { 1880 mLast = p; 1881 } 1882 continue; 1883 } 1884 } 1885 p = n; 1886 } 1887 } 1888 } 1889 removeAllMessagesLocked()1890 private void removeAllMessagesLocked() { 1891 Message p = mMessages; 1892 while (p != null) { 1893 Message n = p.next; 1894 p.recycleUnchecked(); 1895 p = n; 1896 } 1897 mMessages = null; 1898 mLast = null; 1899 mAsyncMessageCount = 0; 1900 } 1901 removeAllFutureMessagesLocked()1902 private void removeAllFutureMessagesLocked() { 1903 final long now = SystemClock.uptimeMillis(); 1904 Message p = mMessages; 1905 if (p != null) { 1906 if (p.when > now) { 1907 removeAllMessagesLocked(); 1908 } else { 1909 Message n; 1910 for (;;) { 1911 n = p.next; 1912 if (n == null) { 1913 return; 1914 } 1915 if (n.when > now) { 1916 break; 1917 } 1918 p = n; 1919 } 1920 p.next = null; 1921 mLast = p; 1922 1923 do { 1924 p = n; 1925 n = p.next; 1926 if (p.isAsynchronous()) { 1927 mAsyncMessageCount--; 1928 } 1929 p.recycleUnchecked(); 1930 } while (n != null); 1931 } 1932 } 1933 } 1934 1935 private static final class MatchAllMessages extends MessageCompare { 1936 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1937 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1938 long when) { 1939 return true; 1940 } 1941 } 1942 private final MatchAllMessages mMatchAllMessages = new MatchAllMessages(); removeAllMessages()1943 private void removeAllMessages() { 1944 findOrRemoveMessages(null, -1, null, null, 0, mMatchAllMessages, true); 1945 } 1946 1947 private static final class MatchAllFutureMessages extends MessageCompare { 1948 @Override compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)1949 public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, 1950 long when) { 1951 final Message m = n.mMessage; 1952 if (m.when > when) { 1953 return true; 1954 } 1955 return false; 1956 } 1957 } 1958 private final MatchAllFutureMessages mMatchAllFutureMessages = new MatchAllFutureMessages(); removeAllFutureMessages()1959 private void removeAllFutureMessages() { 1960 findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(), 1961 mMatchAllFutureMessages, true); 1962 } 1963 1964 @NeverCompile printPriorityQueueNodes()1965 private void printPriorityQueueNodes() { 1966 Iterator<MessageNode> iterator = mPriorityQueue.iterator(); 1967 1968 Log.d(TAG_C, "* Dump priority queue"); 1969 while (iterator.hasNext()) { 1970 MessageNode msgNode = iterator.next(); 1971 Log.d(TAG_C, "** MessageNode what: " + msgNode.mMessage.what + " when " 1972 + msgNode.mMessage.when + " seq: " + msgNode.mInsertSeq); 1973 } 1974 } 1975 1976 @NeverCompile dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, Printer pw, String prefix, Handler h, int n)1977 private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, Printer pw, 1978 String prefix, Handler h, int n) { 1979 int count = 0; 1980 long now = SystemClock.uptimeMillis(); 1981 1982 for (MessageNode msgNode : queue) { 1983 Message msg = msgNode.mMessage; 1984 if (h == null || h == msg.target) { 1985 pw.println(prefix + "Message " + (n + count) + ": " + msg.toString(now)); 1986 } 1987 count++; 1988 } 1989 return count; 1990 } 1991 1992 @NeverCompile dump(Printer pw, String prefix, Handler h)1993 void dump(Printer pw, String prefix, Handler h) { 1994 if (mUseConcurrent) { 1995 long now = SystemClock.uptimeMillis(); 1996 int n = 0; 1997 1998 pw.println(prefix + "(MessageQueue is using Concurrent implementation)"); 1999 2000 StackNode node = (StackNode) sState.getVolatile(this); 2001 while (node != null) { 2002 if (node.isMessageNode()) { 2003 Message msg = ((MessageNode) node).mMessage; 2004 if (h == null || h == msg.target) { 2005 pw.println(prefix + "Message " + n + ": " + msg.toString(now)); 2006 } 2007 node = ((MessageNode) node).mNext; 2008 } else { 2009 pw.println(prefix + "State: " + node); 2010 node = null; 2011 } 2012 n++; 2013 } 2014 2015 pw.println(prefix + "PriorityQueue Messages: "); 2016 n += dumpPriorityQueue(mPriorityQueue, pw, prefix, h, n); 2017 pw.println(prefix + "AsyncPriorityQueue Messages: "); 2018 n += dumpPriorityQueue(mAsyncPriorityQueue, pw, prefix, h, n); 2019 2020 pw.println(prefix + "(Total messages: " + n + ", polling=" + isPolling() 2021 + ", quitting=" + (boolean) sQuitting.getVolatile(this) + ")"); 2022 return; 2023 } 2024 2025 synchronized (this) { 2026 pw.println(prefix + "(MessageQueue is using Legacy implementation)"); 2027 long now = SystemClock.uptimeMillis(); 2028 int n = 0; 2029 for (Message msg = mMessages; msg != null; msg = msg.next) { 2030 if (h == null || h == msg.target) { 2031 pw.println(prefix + "Message " + n + ": " + msg.toString(now)); 2032 } 2033 n++; 2034 } 2035 pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() 2036 + ", quitting=" + mQuitting + ")"); 2037 } 2038 } 2039 2040 @NeverCompile dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, ProtoOutputStream proto)2041 private int dumpPriorityQueue(ConcurrentSkipListSet<MessageNode> queue, 2042 ProtoOutputStream proto) { 2043 int count = 0; 2044 2045 for (MessageNode msgNode : queue) { 2046 Message msg = msgNode.mMessage; 2047 msg.dumpDebug(proto, MessageQueueProto.MESSAGES); 2048 count++; 2049 } 2050 return count; 2051 } 2052 2053 @NeverCompile dumpDebug(ProtoOutputStream proto, long fieldId)2054 void dumpDebug(ProtoOutputStream proto, long fieldId) { 2055 if (mUseConcurrent) { 2056 final long messageQueueToken = proto.start(fieldId); 2057 2058 StackNode node = (StackNode) sState.getVolatile(this); 2059 while (node.isMessageNode()) { 2060 Message msg = ((MessageNode) node).mMessage; 2061 msg.dumpDebug(proto, MessageQueueProto.MESSAGES); 2062 node = ((MessageNode) node).mNext; 2063 } 2064 2065 dumpPriorityQueue(mPriorityQueue, proto); 2066 dumpPriorityQueue(mAsyncPriorityQueue, proto); 2067 2068 proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPolling()); 2069 proto.write(MessageQueueProto.IS_QUITTING, (boolean) sQuitting.getVolatile(this)); 2070 proto.end(messageQueueToken); 2071 return; 2072 } 2073 2074 final long messageQueueToken = proto.start(fieldId); 2075 synchronized (this) { 2076 for (Message msg = mMessages; msg != null; msg = msg.next) { 2077 msg.dumpDebug(proto, MessageQueueProto.MESSAGES); 2078 } 2079 proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked()); 2080 proto.write(MessageQueueProto.IS_QUITTING, mQuitting); 2081 } 2082 proto.end(messageQueueToken); 2083 } 2084 2085 /** 2086 * Callback interface for discovering when a thread is going to block 2087 * waiting for more messages. 2088 */ 2089 public static interface IdleHandler { 2090 /** 2091 * Called when the message queue has run out of messages and will now 2092 * wait for more. Return true to keep your idle handler active, false 2093 * to have it removed. This may be called if there are still messages 2094 * pending in the queue, but they are all scheduled to be dispatched 2095 * after the current time. 2096 */ queueIdle()2097 boolean queueIdle(); 2098 } 2099 2100 /** 2101 * A listener which is invoked when file descriptor related events occur. 2102 */ 2103 public interface OnFileDescriptorEventListener { 2104 /** 2105 * File descriptor event: Indicates that the file descriptor is ready for input 2106 * operations, such as reading. 2107 * <p> 2108 * The listener should read all available data from the file descriptor 2109 * then return <code>true</code> to keep the listener active or <code>false</code> 2110 * to remove the listener. 2111 * </p><p> 2112 * In the case of a socket, this event may be generated to indicate 2113 * that there is at least one incoming connection that the listener 2114 * should accept. 2115 * </p><p> 2116 * This event will only be generated if the {@link #EVENT_INPUT} event mask was 2117 * specified when the listener was added. 2118 * </p> 2119 */ 2120 public static final int EVENT_INPUT = 1 << 0; 2121 2122 /** 2123 * File descriptor event: Indicates that the file descriptor is ready for output 2124 * operations, such as writing. 2125 * <p> 2126 * The listener should write as much data as it needs. If it could not 2127 * write everything at once, then it should return <code>true</code> to 2128 * keep the listener active. Otherwise, it should return <code>false</code> 2129 * to remove the listener then re-register it later when it needs to write 2130 * something else. 2131 * </p><p> 2132 * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was 2133 * specified when the listener was added. 2134 * </p> 2135 */ 2136 public static final int EVENT_OUTPUT = 1 << 1; 2137 2138 /** 2139 * File descriptor event: Indicates that the file descriptor encountered a 2140 * fatal error. 2141 * <p> 2142 * File descriptor errors can occur for various reasons. One common error 2143 * is when the remote peer of a socket or pipe closes its end of the connection. 2144 * </p><p> 2145 * This event may be generated at any time regardless of whether the 2146 * {@link #EVENT_ERROR} event mask was specified when the listener was added. 2147 * </p> 2148 */ 2149 public static final int EVENT_ERROR = 1 << 2; 2150 2151 /** @hide */ 2152 @Retention(RetentionPolicy.SOURCE) 2153 @IntDef(flag = true, prefix = { "EVENT_" }, value = { 2154 EVENT_INPUT, 2155 EVENT_OUTPUT, 2156 EVENT_ERROR 2157 }) 2158 public @interface Events {} 2159 2160 /** 2161 * Called when a file descriptor receives events. 2162 * 2163 * @param fd The file descriptor. 2164 * @param events The set of events that occurred: a combination of the 2165 * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. 2166 * @return The new set of events to watch, or 0 to unregister the listener. 2167 * 2168 * @see #EVENT_INPUT 2169 * @see #EVENT_OUTPUT 2170 * @see #EVENT_ERROR 2171 */ onFileDescriptorEvents(@onNull FileDescriptor fd, @Events int events)2172 @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); 2173 } 2174 2175 private static final class FileDescriptorRecord { 2176 public final FileDescriptor mDescriptor; 2177 public int mEvents; 2178 public OnFileDescriptorEventListener mListener; 2179 public int mSeq; 2180 FileDescriptorRecord(FileDescriptor descriptor, int events, OnFileDescriptorEventListener listener)2181 public FileDescriptorRecord(FileDescriptor descriptor, 2182 int events, OnFileDescriptorEventListener listener) { 2183 mDescriptor = descriptor; 2184 mEvents = events; 2185 mListener = listener; 2186 } 2187 } 2188 2189 /** 2190 * ConcurrentMessageQueue specific classes methods and variables 2191 */ 2192 /* Helper to choose the correct queue to insert into. */ insertIntoPriorityQueue(MessageNode msgNode)2193 private void insertIntoPriorityQueue(MessageNode msgNode) { 2194 if (msgNode.isAsync()) { 2195 mAsyncPriorityQueue.add(msgNode); 2196 } else { 2197 mPriorityQueue.add(msgNode); 2198 } 2199 } 2200 removeFromPriorityQueue(MessageNode msgNode)2201 private boolean removeFromPriorityQueue(MessageNode msgNode) { 2202 if (msgNode.isAsync()) { 2203 return mAsyncPriorityQueue.remove(msgNode); 2204 } else { 2205 return mPriorityQueue.remove(msgNode); 2206 } 2207 } 2208 pickEarliestNode(MessageNode nodeA, MessageNode nodeB)2209 private MessageNode pickEarliestNode(MessageNode nodeA, MessageNode nodeB) { 2210 if (nodeA != null && nodeB != null) { 2211 if (nodeA.compareTo(nodeB) < 0) { 2212 return nodeA; 2213 } 2214 return nodeB; 2215 } 2216 2217 return nodeA != null ? nodeA : nodeB; 2218 } 2219 iterateNext(Iterator<MessageNode> iter)2220 private MessageNode iterateNext(Iterator<MessageNode> iter) { 2221 if (iter.hasNext()) { 2222 try { 2223 return iter.next(); 2224 } catch (NoSuchElementException e) { 2225 /* The queue is empty - this can happen if we race with remove */ 2226 } 2227 } 2228 return null; 2229 } 2230 2231 /* Move any non-cancelled messages into the priority queue */ drainStack(StackNode oldTop)2232 private void drainStack(StackNode oldTop) { 2233 while (oldTop.isMessageNode()) { 2234 MessageNode oldTopMessageNode = (MessageNode) oldTop; 2235 if (oldTopMessageNode.removeFromStack()) { 2236 insertIntoPriorityQueue(oldTopMessageNode); 2237 } 2238 MessageNode inserted = oldTopMessageNode; 2239 oldTop = oldTopMessageNode.mNext; 2240 /* 2241 * removeMessages can walk this list while we are consuming it. 2242 * Set our next pointer to null *after* we add the message to our 2243 * priority queue. This way removeMessages() will always find the 2244 * message, either in our list or in the priority queue. 2245 */ 2246 inserted.mNext = null; 2247 } 2248 } 2249 2250 /* Set the stack state to Active, return a list of nodes to walk. */ swapAndSetStackStateActive()2251 private StackNode swapAndSetStackStateActive() { 2252 while (true) { 2253 /* Set stack state to Active, get node list to walk later */ 2254 StackNode current = (StackNode) sState.getVolatile(this); 2255 if (current == sStackStateActive 2256 || sState.compareAndSet(this, current, sStackStateActive)) { 2257 return current; 2258 } 2259 } 2260 } getStateNode(StackNode node)2261 private StateNode getStateNode(StackNode node) { 2262 if (node.isMessageNode()) { 2263 return ((MessageNode) node).mBottomOfStack; 2264 } 2265 return (StateNode) node; 2266 } 2267 waitForDrainCompleted()2268 private void waitForDrainCompleted() { 2269 mDrainingLock.lock(); 2270 while (mNextIsDrainingStack) { 2271 mDrainCompleted.awaitUninterruptibly(); 2272 } 2273 mDrainingLock.unlock(); 2274 } 2275 2276 @IntDef(value = { 2277 STACK_NODE_MESSAGE, 2278 STACK_NODE_ACTIVE, 2279 STACK_NODE_PARKED, 2280 STACK_NODE_TIMEDPARK}) 2281 @Retention(RetentionPolicy.SOURCE) 2282 private @interface StackNodeType {} 2283 2284 /* 2285 * Stack node types. STACK_NODE_MESSAGE indicates a node containing a message. 2286 * The other types indicate what state our Looper thread is in. The bottom of 2287 * the stack is always a single state node. Message nodes are added on top. 2288 */ 2289 private static final int STACK_NODE_MESSAGE = 0; 2290 /* 2291 * Active state indicates that next() is processing messages 2292 */ 2293 private static final int STACK_NODE_ACTIVE = 1; 2294 /* 2295 * Parked state indicates that the Looper thread is sleeping indefinitely (nothing to deliver) 2296 */ 2297 private static final int STACK_NODE_PARKED = 2; 2298 /* 2299 * Timed Park state indicates that the Looper thread is sleeping, waiting for a message 2300 * deadline 2301 */ 2302 private static final int STACK_NODE_TIMEDPARK = 3; 2303 2304 /* Describes a node in the Treiber stack */ 2305 static class StackNode { 2306 @StackNodeType 2307 private final int mType; 2308 StackNode(@tackNodeType int type)2309 StackNode(@StackNodeType int type) { 2310 mType = type; 2311 } 2312 2313 @StackNodeType getNodeType()2314 final int getNodeType() { 2315 return mType; 2316 } 2317 isMessageNode()2318 final boolean isMessageNode() { 2319 return mType == STACK_NODE_MESSAGE; 2320 } 2321 } 2322 2323 static final class MessageNode extends StackNode implements Comparable<MessageNode> { 2324 private final Message mMessage; 2325 volatile StackNode mNext; 2326 StateNode mBottomOfStack; 2327 boolean mWokeUp; 2328 final long mInsertSeq; 2329 private static final VarHandle sRemovedFromStack; 2330 private volatile boolean mRemovedFromStackValue; 2331 static { 2332 try { 2333 MethodHandles.Lookup l = MethodHandles.lookup(); 2334 sRemovedFromStack = l.findVarHandle(MessageQueue.MessageNode.class, 2335 "mRemovedFromStackValue", boolean.class); 2336 } catch (Exception e) { 2337 Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e); 2338 throw new ExceptionInInitializerError(e); 2339 } 2340 } 2341 MessageNode(@onNull Message message, long insertSeq)2342 MessageNode(@NonNull Message message, long insertSeq) { 2343 super(STACK_NODE_MESSAGE); 2344 mMessage = message; 2345 mInsertSeq = insertSeq; 2346 } 2347 getWhen()2348 long getWhen() { 2349 return mMessage.when; 2350 } 2351 isRemovedFromStack()2352 boolean isRemovedFromStack() { 2353 return mRemovedFromStackValue; 2354 } 2355 removeFromStack()2356 boolean removeFromStack() { 2357 return sRemovedFromStack.compareAndSet(this, false, true); 2358 } 2359 isAsync()2360 boolean isAsync() { 2361 return mMessage.isAsynchronous(); 2362 } 2363 isBarrier()2364 boolean isBarrier() { 2365 return mMessage.target == null; 2366 } 2367 2368 @Override compareTo(@onNull MessageNode messageNode)2369 public int compareTo(@NonNull MessageNode messageNode) { 2370 Message other = messageNode.mMessage; 2371 2372 int compared = Long.compare(mMessage.when, other.when); 2373 if (compared == 0) { 2374 compared = Long.compare(mInsertSeq, messageNode.mInsertSeq); 2375 } 2376 return compared; 2377 } 2378 } 2379 2380 static class StateNode extends StackNode { StateNode(int type)2381 StateNode(int type) { 2382 super(type); 2383 } 2384 } 2385 2386 static final class TimedParkStateNode extends StateNode { 2387 long mWhenToWake; 2388 TimedParkStateNode()2389 TimedParkStateNode() { 2390 super(STACK_NODE_TIMEDPARK); 2391 } 2392 } 2393 2394 private static final StateNode sStackStateActive = new StateNode(STACK_NODE_ACTIVE); 2395 private static final StateNode sStackStateParked = new StateNode(STACK_NODE_PARKED); 2396 private final TimedParkStateNode mStackStateTimedPark = new TimedParkStateNode(); 2397 2398 /* This is the top of our treiber stack. */ 2399 private static final VarHandle sState; 2400 static { 2401 try { 2402 MethodHandles.Lookup l = MethodHandles.lookup(); 2403 sState = l.findVarHandle(MessageQueue.class, "mStateValue", 2404 MessageQueue.StackNode.class); 2405 } catch (Exception e) { 2406 Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e); 2407 throw new ExceptionInInitializerError(e); 2408 } 2409 } 2410 2411 private volatile StackNode mStateValue = sStackStateParked; 2412 private final ConcurrentSkipListSet<MessageNode> mPriorityQueue = 2413 new ConcurrentSkipListSet<MessageNode>(); 2414 private final ConcurrentSkipListSet<MessageNode> mAsyncPriorityQueue = 2415 new ConcurrentSkipListSet<MessageNode>(); 2416 2417 /* 2418 * This helps us ensure that messages with the same timestamp are inserted in FIFO order. 2419 * Increments on each insert, starting at 0. MessageNode.compareTo() will compare sequences 2420 * when delivery timestamps are identical. 2421 */ 2422 private static final VarHandle sNextInsertSeq; 2423 private volatile long mNextInsertSeqValue = 0; 2424 /* 2425 * The exception to the FIFO order rule is sendMessageAtFrontOfQueue(). 2426 * Those messages must be in LIFO order. 2427 * Decrements on each front of queue insert. 2428 */ 2429 private static final VarHandle sNextFrontInsertSeq; 2430 private volatile long mNextFrontInsertSeqValue = -1; 2431 static { 2432 try { 2433 MethodHandles.Lookup l = MethodHandles.lookup(); 2434 sNextInsertSeq = l.findVarHandle(MessageQueue.class, "mNextInsertSeqValue", 2435 long.class); 2436 sNextFrontInsertSeq = l.findVarHandle(MessageQueue.class, "mNextFrontInsertSeqValue", 2437 long.class); 2438 } catch (Exception e) { 2439 Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e); 2440 throw new ExceptionInInitializerError(e); 2441 } 2442 2443 } 2444 2445 /* 2446 * Tracks the number of queued and cancelled messages in our stack. 2447 * 2448 * On item cancellation, determine whether to wake next() to flush tombstoned messages. 2449 * We track queued and cancelled counts as two ints packed into a single long. 2450 */ 2451 private static final class MessageCounts { 2452 private static VarHandle sCounts; 2453 private volatile long mCountsValue = 0; 2454 static { 2455 try { 2456 MethodHandles.Lookup l = MethodHandles.lookup(); 2457 sCounts = l.findVarHandle(MessageQueue.MessageCounts.class, "mCountsValue", 2458 long.class); 2459 } catch (Exception e) { 2460 Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e); 2461 throw new ExceptionInInitializerError(e); 2462 } 2463 } 2464 2465 /* We use a special value to indicate when next() has been woken for flush. */ 2466 private static final long AWAKE = Long.MAX_VALUE; 2467 /* 2468 * Minimum number of messages in the stack which we need before we consider flushing 2469 * tombstoned items. 2470 */ 2471 private static final int MESSAGE_FLUSH_THRESHOLD = 10; 2472 numQueued(long val)2473 private static int numQueued(long val) { 2474 return (int) (val >>> Integer.SIZE); 2475 } 2476 numCancelled(long val)2477 private static int numCancelled(long val) { 2478 return (int) val; 2479 } 2480 combineCounts(int queued, int cancelled)2481 private static long combineCounts(int queued, int cancelled) { 2482 return ((long) queued << Integer.SIZE) | (long) cancelled; 2483 } 2484 incrementQueued()2485 public void incrementQueued() { 2486 while (true) { 2487 long oldVal = mCountsValue; 2488 int queued = numQueued(oldVal); 2489 int cancelled = numCancelled(oldVal); 2490 /* Use Math.max() to avoid overflow of queued count */ 2491 long newVal = combineCounts(Math.max(queued + 1, queued), cancelled); 2492 2493 /* Don't overwrite 'AWAKE' state */ 2494 if (oldVal == AWAKE || sCounts.compareAndSet(this, oldVal, newVal)) { 2495 break; 2496 } 2497 } 2498 } 2499 incrementCancelled()2500 public boolean incrementCancelled() { 2501 while (true) { 2502 long oldVal = mCountsValue; 2503 if (oldVal == AWAKE) { 2504 return false; 2505 } 2506 int queued = numQueued(oldVal); 2507 int cancelled = numCancelled(oldVal); 2508 boolean needsPurge = queued > MESSAGE_FLUSH_THRESHOLD 2509 && (queued >> 1) < cancelled; 2510 long newVal; 2511 if (needsPurge) { 2512 newVal = AWAKE; 2513 } else { 2514 newVal = combineCounts(queued, 2515 Math.max(cancelled + 1, cancelled)); 2516 } 2517 2518 if (sCounts.compareAndSet(this, oldVal, newVal)) { 2519 return needsPurge; 2520 } 2521 } 2522 } 2523 2524 public void clearCounts() { 2525 mCountsValue = 0; 2526 } 2527 } 2528 2529 private final MessageCounts mMessageCounts = new MessageCounts(); 2530 2531 private final Object mIdleHandlersLock = new Object(); 2532 private final Object mFileDescriptorRecordsLock = new Object(); 2533 2534 private static final VarHandle sQuitting; 2535 private boolean mQuittingValue = false; 2536 static { 2537 try { 2538 MethodHandles.Lookup l = MethodHandles.lookup(); 2539 sQuitting = l.findVarHandle(MessageQueue.class, "mQuittingValue", boolean.class); 2540 } catch (Exception e) { 2541 Log.wtf(TAG_C, "VarHandle lookup failed with exception: " + e); 2542 throw new ExceptionInInitializerError(e); 2543 } 2544 } 2545 2546 // The next barrier token. 2547 // Barriers are indicated by messages with a null target whose arg1 field carries the token. 2548 private final AtomicInteger mNextBarrierTokenAtomic = new AtomicInteger(1); 2549 2550 // Must retain this for compatibility reasons. 2551 @UnsupportedAppUsage 2552 private int mNextBarrierToken; 2553 2554 /* Protects mNextIsDrainingStack */ 2555 private final ReentrantLock mDrainingLock = new ReentrantLock(); 2556 private boolean mNextIsDrainingStack = false; 2557 private final Condition mDrainCompleted = mDrainingLock.newCondition(); 2558 2559 private boolean enqueueMessageUnchecked(@NonNull Message msg, long when) { 2560 if ((boolean) sQuitting.getVolatile(this)) { 2561 IllegalStateException e = new IllegalStateException( 2562 msg.target + " sending message to a Handler on a dead thread"); 2563 Log.w(TAG_C, e.getMessage(), e); 2564 msg.recycleUnchecked(); 2565 return false; 2566 } 2567 2568 long seq = when != 0 ? ((long) sNextInsertSeq.getAndAdd(this, 1L) + 1L) 2569 : ((long) sNextFrontInsertSeq.getAndAdd(this, -1L) - 1L); 2570 /* TODO: Add a MessageNode member to Message so we can avoid this allocation */ 2571 MessageNode node = new MessageNode(msg, seq); 2572 msg.when = when; 2573 msg.markInUse(); 2574 2575 if (DEBUG) { 2576 Log.d(TAG_C, "Insert message what: " + msg.what + " when: " + msg.when + " seq: " 2577 + node.mInsertSeq + " barrier: " + node.isBarrier() + " async: " 2578 + node.isAsync() + " now: " + SystemClock.uptimeMillis()); 2579 } 2580 2581 final Looper myLooper = Looper.myLooper(); 2582 /* If we are running on the looper thread we can add directly to the priority queue */ 2583 if (myLooper != null && myLooper.getQueue() == this) { 2584 node.removeFromStack(); 2585 insertIntoPriorityQueue(node); 2586 /* 2587 * We still need to do this even though we are the current thread, 2588 * otherwise next() may sleep indefinitely. 2589 */ 2590 if (!mMessageDirectlyQueued) { 2591 mMessageDirectlyQueued = true; 2592 nativeWake(mPtr); 2593 } 2594 return true; 2595 } 2596 2597 while (true) { 2598 StackNode old = (StackNode) sState.getVolatile(this); 2599 boolean wakeNeeded; 2600 boolean inactive; 2601 2602 node.mNext = old; 2603 switch (old.getNodeType()) { 2604 case STACK_NODE_ACTIVE: 2605 /* 2606 * The worker thread is currently active and will process any elements added to 2607 * the stack before parking again. 2608 */ 2609 node.mBottomOfStack = (StateNode) old; 2610 inactive = false; 2611 node.mWokeUp = true; 2612 wakeNeeded = false; 2613 break; 2614 2615 case STACK_NODE_PARKED: 2616 node.mBottomOfStack = (StateNode) old; 2617 inactive = true; 2618 node.mWokeUp = true; 2619 wakeNeeded = true; 2620 break; 2621 2622 case STACK_NODE_TIMEDPARK: 2623 node.mBottomOfStack = (StateNode) old; 2624 inactive = true; 2625 wakeNeeded = mStackStateTimedPark.mWhenToWake >= node.getWhen(); 2626 node.mWokeUp = wakeNeeded; 2627 break; 2628 2629 default: 2630 MessageNode oldMessage = (MessageNode) old; 2631 2632 node.mBottomOfStack = oldMessage.mBottomOfStack; 2633 int bottomType = node.mBottomOfStack.getNodeType(); 2634 inactive = bottomType >= STACK_NODE_PARKED; 2635 wakeNeeded = (bottomType == STACK_NODE_TIMEDPARK 2636 && mStackStateTimedPark.mWhenToWake >= node.getWhen() 2637 && !oldMessage.mWokeUp); 2638 node.mWokeUp = oldMessage.mWokeUp || wakeNeeded; 2639 break; 2640 } 2641 if (sState.compareAndSet(this, old, node)) { 2642 if (inactive) { 2643 if (wakeNeeded) { 2644 nativeWake(mPtr); 2645 } else { 2646 mMessageCounts.incrementQueued(); 2647 } 2648 } 2649 return true; 2650 } 2651 } 2652 } 2653 2654 /* 2655 * This class is used to find matches for hasMessages() and removeMessages() 2656 */ 2657 private abstract static class MessageCompare { compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r, long when)2658 public abstract boolean compareMessage(MessageNode n, Handler h, int what, Object object, 2659 Runnable r, long when); 2660 } 2661 stackHasMessages(Handler h, int what, Object object, Runnable r, long when, MessageCompare compare, boolean removeMatches)2662 private boolean stackHasMessages(Handler h, int what, Object object, Runnable r, long when, 2663 MessageCompare compare, boolean removeMatches) { 2664 boolean found = false; 2665 StackNode top = (StackNode) sState.getVolatile(this); 2666 StateNode bottom = getStateNode(top); 2667 2668 /* 2669 * If the top node is a state node, there are no reachable messages. 2670 * If it's anything other than Active, we can quit as we know that next() is not 2671 * consuming items. 2672 * If the top node is Active then we know that next() is currently consuming items. 2673 * In that case we should wait next() has drained the stack. 2674 */ 2675 if (top == bottom) { 2676 if (bottom != sStackStateActive) { 2677 return false; 2678 } 2679 waitForDrainCompleted(); 2680 return false; 2681 } 2682 2683 /* 2684 * We have messages that we may tombstone. Walk the stack until we hit the bottom or we 2685 * hit a null pointer. 2686 * If we hit the bottom, we are done. 2687 * If we hit a null pointer, then the stack is being consumed by next() and we must cycle 2688 * until the stack has been drained. 2689 */ 2690 MessageNode p = (MessageNode) top; 2691 2692 while (true) { 2693 if (compare.compareMessage(p, h, what, object, r, when)) { 2694 found = true; 2695 if (DEBUG) { 2696 Log.d(TAG_C, "stackHasMessages node matches"); 2697 } 2698 if (removeMatches) { 2699 if (p.removeFromStack()) { 2700 p.mMessage.recycleUnchecked(); 2701 if (mMessageCounts.incrementCancelled()) { 2702 nativeWake(mPtr); 2703 } 2704 } 2705 } else { 2706 return true; 2707 } 2708 } 2709 2710 StackNode n = p.mNext; 2711 if (n == null) { 2712 /* Next() is walking the stack, we must re-sample */ 2713 if (DEBUG) { 2714 Log.d(TAG_C, "stackHasMessages next() is walking the stack, we must re-sample"); 2715 } 2716 waitForDrainCompleted(); 2717 break; 2718 } 2719 if (!n.isMessageNode()) { 2720 /* We reached the end of the stack */ 2721 return found; 2722 } 2723 p = (MessageNode) n; 2724 } 2725 2726 return found; 2727 } 2728 priorityQueueHasMessage(ConcurrentSkipListSet<MessageNode> queue, Handler h, int what, Object object, Runnable r, long when, MessageCompare compare, boolean removeMatches)2729 private boolean priorityQueueHasMessage(ConcurrentSkipListSet<MessageNode> queue, Handler h, 2730 int what, Object object, Runnable r, long when, MessageCompare compare, 2731 boolean removeMatches) { 2732 Iterator<MessageNode> iterator = queue.iterator(); 2733 boolean found = false; 2734 2735 while (iterator.hasNext()) { 2736 MessageNode msg = iterator.next(); 2737 2738 if (compare.compareMessage(msg, h, what, object, r, when)) { 2739 if (removeMatches) { 2740 found = true; 2741 if (queue.remove(msg)) { 2742 msg.mMessage.recycleUnchecked(); 2743 } 2744 } else { 2745 return true; 2746 } 2747 } 2748 } 2749 return found; 2750 } 2751 findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, MessageCompare compare, boolean removeMatches)2752 private boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, 2753 MessageCompare compare, boolean removeMatches) { 2754 boolean foundInStack, foundInQueue; 2755 2756 foundInStack = stackHasMessages(h, what, object, r, when, compare, removeMatches); 2757 foundInQueue = priorityQueueHasMessage(mPriorityQueue, h, what, object, r, when, compare, 2758 removeMatches); 2759 foundInQueue |= priorityQueueHasMessage(mAsyncPriorityQueue, h, what, object, r, when, 2760 compare, removeMatches); 2761 2762 return foundInStack || foundInQueue; 2763 } 2764 2765 } 2766