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.util.Log; 22 import android.util.Printer; 23 import android.util.SparseArray; 24 25 import java.io.FileDescriptor; 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.ArrayList; 29 30 /** 31 * Low-level class holding the list of messages to be dispatched by a 32 * {@link Looper}. Messages are not added directly to a MessageQueue, 33 * but rather through {@link Handler} objects associated with the Looper. 34 * 35 * <p>You can retrieve the MessageQueue for the current thread with 36 * {@link Looper#myQueue() Looper.myQueue()}. 37 */ 38 public final class MessageQueue { 39 private static final String TAG = "MessageQueue"; 40 private static final boolean DEBUG = false; 41 42 // True if the message queue can be quit. 43 private final boolean mQuitAllowed; 44 45 @SuppressWarnings("unused") 46 private long mPtr; // used by native code 47 48 Message mMessages; 49 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); 50 private SparseArray<FileDescriptorRecord> mFileDescriptorRecords; 51 private IdleHandler[] mPendingIdleHandlers; 52 private boolean mQuitting; 53 54 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. 55 private boolean mBlocked; 56 57 // The next barrier token. 58 // Barriers are indicated by messages with a null target whose arg1 field carries the token. 59 private int mNextBarrierToken; 60 nativeInit()61 private native static long nativeInit(); nativeDestroy(long ptr)62 private native static void nativeDestroy(long ptr); nativePollOnce(long ptr, int timeoutMillis)63 private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/ nativeWake(long ptr)64 private native static void nativeWake(long ptr); nativeIsPolling(long ptr)65 private native static boolean nativeIsPolling(long ptr); nativeSetFileDescriptorEvents(long ptr, int fd, int events)66 private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events); 67 MessageQueue(boolean quitAllowed)68 MessageQueue(boolean quitAllowed) { 69 mQuitAllowed = quitAllowed; 70 mPtr = nativeInit(); 71 } 72 73 @Override finalize()74 protected void finalize() throws Throwable { 75 try { 76 dispose(); 77 } finally { 78 super.finalize(); 79 } 80 } 81 82 // Disposes of the underlying message queue. 83 // Must only be called on the looper thread or the finalizer. dispose()84 private void dispose() { 85 if (mPtr != 0) { 86 nativeDestroy(mPtr); 87 mPtr = 0; 88 } 89 } 90 91 /** 92 * Returns true if the looper has no pending messages which are due to be processed. 93 * 94 * <p>This method is safe to call from any thread. 95 * 96 * @return True if the looper is idle. 97 */ isIdle()98 public boolean isIdle() { 99 synchronized (this) { 100 final long now = SystemClock.uptimeMillis(); 101 return mMessages == null || now < mMessages.when; 102 } 103 } 104 105 /** 106 * Add a new {@link IdleHandler} to this message queue. This may be 107 * removed automatically for you by returning false from 108 * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is 109 * invoked, or explicitly removing it with {@link #removeIdleHandler}. 110 * 111 * <p>This method is safe to call from any thread. 112 * 113 * @param handler The IdleHandler to be added. 114 */ addIdleHandler(@onNull IdleHandler handler)115 public void addIdleHandler(@NonNull IdleHandler handler) { 116 if (handler == null) { 117 throw new NullPointerException("Can't add a null IdleHandler"); 118 } 119 synchronized (this) { 120 mIdleHandlers.add(handler); 121 } 122 } 123 124 /** 125 * Remove an {@link IdleHandler} from the queue that was previously added 126 * with {@link #addIdleHandler}. If the given object is not currently 127 * in the idle list, nothing is done. 128 * 129 * <p>This method is safe to call from any thread. 130 * 131 * @param handler The IdleHandler to be removed. 132 */ removeIdleHandler(@onNull IdleHandler handler)133 public void removeIdleHandler(@NonNull IdleHandler handler) { 134 synchronized (this) { 135 mIdleHandlers.remove(handler); 136 } 137 } 138 139 /** 140 * Returns whether this looper's thread is currently polling for more work to do. 141 * This is a good signal that the loop is still alive rather than being stuck 142 * handling a callback. Note that this method is intrinsically racy, since the 143 * state of the loop can change before you get the result back. 144 * 145 * <p>This method is safe to call from any thread. 146 * 147 * @return True if the looper is currently polling for events. 148 * @hide 149 */ isPolling()150 public boolean isPolling() { 151 synchronized (this) { 152 return isPollingLocked(); 153 } 154 } 155 isPollingLocked()156 private boolean isPollingLocked() { 157 // If the loop is quitting then it must not be idling. 158 // We can assume mPtr != 0 when mQuitting is false. 159 return !mQuitting && nativeIsPolling(mPtr); 160 } 161 162 /** 163 * Adds a file descriptor listener to receive notification when file descriptor 164 * related events occur. 165 * <p> 166 * If the file descriptor has already been registered, the specified events 167 * and listener will replace any that were previously associated with it. 168 * It is not possible to set more than one listener per file descriptor. 169 * </p><p> 170 * It is important to always unregister the listener when the file descriptor 171 * is no longer of use. 172 * </p> 173 * 174 * @param fd The file descriptor for which a listener will be registered. 175 * @param events The set of events to receive: a combination of the 176 * {@link OnFileDescriptorEventListener#EVENT_INPUT}, 177 * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and 178 * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks. If the requested 179 * set of events is zero, then the listener is unregistered. 180 * @param listener The listener to invoke when file descriptor events occur. 181 * 182 * @see OnFileDescriptorEventListener 183 * @see #removeOnFileDescriptorEventListener 184 */ addOnFileDescriptorEventListener(@onNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener)185 public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd, 186 @OnFileDescriptorEventListener.Events int events, 187 @NonNull OnFileDescriptorEventListener listener) { 188 if (fd == null) { 189 throw new IllegalArgumentException("fd must not be null"); 190 } 191 if (listener == null) { 192 throw new IllegalArgumentException("listener must not be null"); 193 } 194 195 synchronized (this) { 196 updateOnFileDescriptorEventListenerLocked(fd, events, listener); 197 } 198 } 199 200 /** 201 * Removes a file descriptor listener. 202 * <p> 203 * This method does nothing if no listener has been registered for the 204 * specified file descriptor. 205 * </p> 206 * 207 * @param fd The file descriptor whose listener will be unregistered. 208 * 209 * @see OnFileDescriptorEventListener 210 * @see #addOnFileDescriptorEventListener 211 */ removeOnFileDescriptorEventListener(@onNull FileDescriptor fd)212 public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) { 213 if (fd == null) { 214 throw new IllegalArgumentException("fd must not be null"); 215 } 216 217 synchronized (this) { 218 updateOnFileDescriptorEventListenerLocked(fd, 0, null); 219 } 220 } 221 updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, OnFileDescriptorEventListener listener)222 private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, 223 OnFileDescriptorEventListener listener) { 224 final int fdNum = fd.getInt$(); 225 226 int index = -1; 227 FileDescriptorRecord record = null; 228 if (mFileDescriptorRecords != null) { 229 index = mFileDescriptorRecords.indexOfKey(fdNum); 230 if (index >= 0) { 231 record = mFileDescriptorRecords.valueAt(index); 232 if (record != null && record.mEvents == events) { 233 return; 234 } 235 } 236 } 237 238 if (events != 0) { 239 events |= OnFileDescriptorEventListener.EVENT_ERROR; 240 if (record == null) { 241 if (mFileDescriptorRecords == null) { 242 mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>(); 243 } 244 record = new FileDescriptorRecord(fd, events, listener); 245 mFileDescriptorRecords.put(fdNum, record); 246 } else { 247 record.mListener = listener; 248 record.mEvents = events; 249 record.mSeq += 1; 250 } 251 nativeSetFileDescriptorEvents(mPtr, fdNum, events); 252 } else if (record != null) { 253 record.mEvents = 0; 254 mFileDescriptorRecords.removeAt(index); 255 } 256 } 257 258 // Called from native code. dispatchEvents(int fd, int events)259 private int dispatchEvents(int fd, int events) { 260 // Get the file descriptor record and any state that might change. 261 final FileDescriptorRecord record; 262 final int oldWatchedEvents; 263 final OnFileDescriptorEventListener listener; 264 final int seq; 265 synchronized (this) { 266 record = mFileDescriptorRecords.get(fd); 267 if (record == null) { 268 return 0; // spurious, no listener registered 269 } 270 271 oldWatchedEvents = record.mEvents; 272 events &= oldWatchedEvents; // filter events based on current watched set 273 if (events == 0) { 274 return oldWatchedEvents; // spurious, watched events changed 275 } 276 277 listener = record.mListener; 278 seq = record.mSeq; 279 } 280 281 // Invoke the listener outside of the lock. 282 int newWatchedEvents = listener.onFileDescriptorEvents( 283 record.mDescriptor, events); 284 if (newWatchedEvents != 0) { 285 newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR; 286 } 287 288 // Update the file descriptor record if the listener changed the set of 289 // events to watch and the listener itself hasn't been updated since. 290 if (newWatchedEvents != oldWatchedEvents) { 291 synchronized (this) { 292 int index = mFileDescriptorRecords.indexOfKey(fd); 293 if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record 294 && record.mSeq == seq) { 295 record.mEvents = newWatchedEvents; 296 if (newWatchedEvents == 0) { 297 mFileDescriptorRecords.removeAt(index); 298 } 299 } 300 } 301 } 302 303 // Return the new set of events to watch for native code to take care of. 304 return newWatchedEvents; 305 } 306 next()307 Message next() { 308 // Return here if the message loop has already quit and been disposed. 309 // This can happen if the application tries to restart a looper after quit 310 // which is not supported. 311 final long ptr = mPtr; 312 if (ptr == 0) { 313 return null; 314 } 315 316 int pendingIdleHandlerCount = -1; // -1 only during first iteration 317 int nextPollTimeoutMillis = 0; 318 for (;;) { 319 if (nextPollTimeoutMillis != 0) { 320 Binder.flushPendingCommands(); 321 } 322 323 nativePollOnce(ptr, nextPollTimeoutMillis); 324 325 synchronized (this) { 326 // Try to retrieve the next message. Return if found. 327 final long now = SystemClock.uptimeMillis(); 328 Message prevMsg = null; 329 Message msg = mMessages; 330 if (msg != null && msg.target == null) { 331 // Stalled by a barrier. Find the next asynchronous message in the queue. 332 do { 333 prevMsg = msg; 334 msg = msg.next; 335 } while (msg != null && !msg.isAsynchronous()); 336 } 337 if (msg != null) { 338 if (now < msg.when) { 339 // Next message is not ready. Set a timeout to wake up when it is ready. 340 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); 341 } else { 342 // Got a message. 343 mBlocked = false; 344 if (prevMsg != null) { 345 prevMsg.next = msg.next; 346 } else { 347 mMessages = msg.next; 348 } 349 msg.next = null; 350 if (DEBUG) Log.v(TAG, "Returning message: " + msg); 351 msg.markInUse(); 352 return msg; 353 } 354 } else { 355 // No more messages. 356 nextPollTimeoutMillis = -1; 357 } 358 359 // Process the quit message now that all pending messages have been handled. 360 if (mQuitting) { 361 dispose(); 362 return null; 363 } 364 365 // If first time idle, then get the number of idlers to run. 366 // Idle handles only run if the queue is empty or if the first message 367 // in the queue (possibly a barrier) is due to be handled in the future. 368 if (pendingIdleHandlerCount < 0 369 && (mMessages == null || now < mMessages.when)) { 370 pendingIdleHandlerCount = mIdleHandlers.size(); 371 } 372 if (pendingIdleHandlerCount <= 0) { 373 // No idle handlers to run. Loop and wait some more. 374 mBlocked = true; 375 continue; 376 } 377 378 if (mPendingIdleHandlers == null) { 379 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)]; 380 } 381 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers); 382 } 383 384 // Run the idle handlers. 385 // We only ever reach this code block during the first iteration. 386 for (int i = 0; i < pendingIdleHandlerCount; i++) { 387 final IdleHandler idler = mPendingIdleHandlers[i]; 388 mPendingIdleHandlers[i] = null; // release the reference to the handler 389 390 boolean keep = false; 391 try { 392 keep = idler.queueIdle(); 393 } catch (Throwable t) { 394 Log.wtf(TAG, "IdleHandler threw exception", t); 395 } 396 397 if (!keep) { 398 synchronized (this) { 399 mIdleHandlers.remove(idler); 400 } 401 } 402 } 403 404 // Reset the idle handler count to 0 so we do not run them again. 405 pendingIdleHandlerCount = 0; 406 407 // While calling an idle handler, a new message could have been delivered 408 // so go back and look again for a pending message without waiting. 409 nextPollTimeoutMillis = 0; 410 } 411 } 412 quit(boolean safe)413 void quit(boolean safe) { 414 if (!mQuitAllowed) { 415 throw new IllegalStateException("Main thread not allowed to quit."); 416 } 417 418 synchronized (this) { 419 if (mQuitting) { 420 return; 421 } 422 mQuitting = true; 423 424 if (safe) { 425 removeAllFutureMessagesLocked(); 426 } else { 427 removeAllMessagesLocked(); 428 } 429 430 // We can assume mPtr != 0 because mQuitting was previously false. 431 nativeWake(mPtr); 432 } 433 } 434 435 /** 436 * Posts a synchronization barrier to the Looper's message queue. 437 * 438 * Message processing occurs as usual until the message queue encounters the 439 * synchronization barrier that has been posted. When the barrier is encountered, 440 * later synchronous messages in the queue are stalled (prevented from being executed) 441 * until the barrier is released by calling {@link #removeSyncBarrier} and specifying 442 * the token that identifies the synchronization barrier. 443 * 444 * This method is used to immediately postpone execution of all subsequently posted 445 * synchronous messages until a condition is met that releases the barrier. 446 * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier 447 * and continue to be processed as usual. 448 * 449 * This call must be always matched by a call to {@link #removeSyncBarrier} with 450 * the same token to ensure that the message queue resumes normal operation. 451 * Otherwise the application will probably hang! 452 * 453 * @return A token that uniquely identifies the barrier. This token must be 454 * passed to {@link #removeSyncBarrier} to release the barrier. 455 * 456 * @hide 457 */ postSyncBarrier()458 public int postSyncBarrier() { 459 return postSyncBarrier(SystemClock.uptimeMillis()); 460 } 461 postSyncBarrier(long when)462 private int postSyncBarrier(long when) { 463 // Enqueue a new sync barrier token. 464 // We don't need to wake the queue because the purpose of a barrier is to stall it. 465 synchronized (this) { 466 final int token = mNextBarrierToken++; 467 final Message msg = Message.obtain(); 468 msg.markInUse(); 469 msg.when = when; 470 msg.arg1 = token; 471 472 Message prev = null; 473 Message p = mMessages; 474 if (when != 0) { 475 while (p != null && p.when <= when) { 476 prev = p; 477 p = p.next; 478 } 479 } 480 if (prev != null) { // invariant: p == prev.next 481 msg.next = p; 482 prev.next = msg; 483 } else { 484 msg.next = p; 485 mMessages = msg; 486 } 487 return token; 488 } 489 } 490 491 /** 492 * Removes a synchronization barrier. 493 * 494 * @param token The synchronization barrier token that was returned by 495 * {@link #postSyncBarrier}. 496 * 497 * @throws IllegalStateException if the barrier was not found. 498 * 499 * @hide 500 */ removeSyncBarrier(int token)501 public void removeSyncBarrier(int token) { 502 // Remove a sync barrier token from the queue. 503 // If the queue is no longer stalled by a barrier then wake it. 504 synchronized (this) { 505 Message prev = null; 506 Message p = mMessages; 507 while (p != null && (p.target != null || p.arg1 != token)) { 508 prev = p; 509 p = p.next; 510 } 511 if (p == null) { 512 throw new IllegalStateException("The specified message queue synchronization " 513 + " barrier token has not been posted or has already been removed."); 514 } 515 final boolean needWake; 516 if (prev != null) { 517 prev.next = p.next; 518 needWake = false; 519 } else { 520 mMessages = p.next; 521 needWake = mMessages == null || mMessages.target != null; 522 } 523 p.recycleUnchecked(); 524 525 // If the loop is quitting then it is already awake. 526 // We can assume mPtr != 0 when mQuitting is false. 527 if (needWake && !mQuitting) { 528 nativeWake(mPtr); 529 } 530 } 531 } 532 enqueueMessage(Message msg, long when)533 boolean enqueueMessage(Message msg, long when) { 534 if (msg.target == null) { 535 throw new IllegalArgumentException("Message must have a target."); 536 } 537 if (msg.isInUse()) { 538 throw new IllegalStateException(msg + " This message is already in use."); 539 } 540 541 synchronized (this) { 542 if (mQuitting) { 543 IllegalStateException e = new IllegalStateException( 544 msg.target + " sending message to a Handler on a dead thread"); 545 Log.w(TAG, e.getMessage(), e); 546 msg.recycle(); 547 return false; 548 } 549 550 msg.markInUse(); 551 msg.when = when; 552 Message p = mMessages; 553 boolean needWake; 554 if (p == null || when == 0 || when < p.when) { 555 // New head, wake up the event queue if blocked. 556 msg.next = p; 557 mMessages = msg; 558 needWake = mBlocked; 559 } else { 560 // Inserted within the middle of the queue. Usually we don't have to wake 561 // up the event queue unless there is a barrier at the head of the queue 562 // and the message is the earliest asynchronous message in the queue. 563 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 564 Message prev; 565 for (;;) { 566 prev = p; 567 p = p.next; 568 if (p == null || when < p.when) { 569 break; 570 } 571 if (needWake && p.isAsynchronous()) { 572 needWake = false; 573 } 574 } 575 msg.next = p; // invariant: p == prev.next 576 prev.next = msg; 577 } 578 579 // We can assume mPtr != 0 because mQuitting is false. 580 if (needWake) { 581 nativeWake(mPtr); 582 } 583 } 584 return true; 585 } 586 hasMessages(Handler h, int what, Object object)587 boolean hasMessages(Handler h, int what, Object object) { 588 if (h == null) { 589 return false; 590 } 591 592 synchronized (this) { 593 Message p = mMessages; 594 while (p != null) { 595 if (p.target == h && p.what == what && (object == null || p.obj == object)) { 596 return true; 597 } 598 p = p.next; 599 } 600 return false; 601 } 602 } 603 hasMessages(Handler h, Runnable r, Object object)604 boolean hasMessages(Handler h, Runnable r, Object object) { 605 if (h == null) { 606 return false; 607 } 608 609 synchronized (this) { 610 Message p = mMessages; 611 while (p != null) { 612 if (p.target == h && p.callback == r && (object == null || p.obj == object)) { 613 return true; 614 } 615 p = p.next; 616 } 617 return false; 618 } 619 } 620 removeMessages(Handler h, int what, Object object)621 void removeMessages(Handler h, int what, Object object) { 622 if (h == null) { 623 return; 624 } 625 626 synchronized (this) { 627 Message p = mMessages; 628 629 // Remove all messages at front. 630 while (p != null && p.target == h && p.what == what 631 && (object == null || p.obj == object)) { 632 Message n = p.next; 633 mMessages = n; 634 p.recycleUnchecked(); 635 p = n; 636 } 637 638 // Remove all messages after front. 639 while (p != null) { 640 Message n = p.next; 641 if (n != null) { 642 if (n.target == h && n.what == what 643 && (object == null || n.obj == object)) { 644 Message nn = n.next; 645 n.recycleUnchecked(); 646 p.next = nn; 647 continue; 648 } 649 } 650 p = n; 651 } 652 } 653 } 654 removeMessages(Handler h, Runnable r, Object object)655 void removeMessages(Handler h, Runnable r, Object object) { 656 if (h == null || r == null) { 657 return; 658 } 659 660 synchronized (this) { 661 Message p = mMessages; 662 663 // Remove all messages at front. 664 while (p != null && p.target == h && p.callback == r 665 && (object == null || p.obj == object)) { 666 Message n = p.next; 667 mMessages = n; 668 p.recycleUnchecked(); 669 p = n; 670 } 671 672 // Remove all messages after front. 673 while (p != null) { 674 Message n = p.next; 675 if (n != null) { 676 if (n.target == h && n.callback == r 677 && (object == null || n.obj == object)) { 678 Message nn = n.next; 679 n.recycleUnchecked(); 680 p.next = nn; 681 continue; 682 } 683 } 684 p = n; 685 } 686 } 687 } 688 removeCallbacksAndMessages(Handler h, Object object)689 void removeCallbacksAndMessages(Handler h, Object object) { 690 if (h == null) { 691 return; 692 } 693 694 synchronized (this) { 695 Message p = mMessages; 696 697 // Remove all messages at front. 698 while (p != null && p.target == h 699 && (object == null || p.obj == object)) { 700 Message n = p.next; 701 mMessages = n; 702 p.recycleUnchecked(); 703 p = n; 704 } 705 706 // Remove all messages after front. 707 while (p != null) { 708 Message n = p.next; 709 if (n != null) { 710 if (n.target == h && (object == null || n.obj == object)) { 711 Message nn = n.next; 712 n.recycleUnchecked(); 713 p.next = nn; 714 continue; 715 } 716 } 717 p = n; 718 } 719 } 720 } 721 removeAllMessagesLocked()722 private void removeAllMessagesLocked() { 723 Message p = mMessages; 724 while (p != null) { 725 Message n = p.next; 726 p.recycleUnchecked(); 727 p = n; 728 } 729 mMessages = null; 730 } 731 removeAllFutureMessagesLocked()732 private void removeAllFutureMessagesLocked() { 733 final long now = SystemClock.uptimeMillis(); 734 Message p = mMessages; 735 if (p != null) { 736 if (p.when > now) { 737 removeAllMessagesLocked(); 738 } else { 739 Message n; 740 for (;;) { 741 n = p.next; 742 if (n == null) { 743 return; 744 } 745 if (n.when > now) { 746 break; 747 } 748 p = n; 749 } 750 p.next = null; 751 do { 752 p = n; 753 n = p.next; 754 p.recycleUnchecked(); 755 } while (n != null); 756 } 757 } 758 } 759 dump(Printer pw, String prefix)760 void dump(Printer pw, String prefix) { 761 synchronized (this) { 762 long now = SystemClock.uptimeMillis(); 763 int n = 0; 764 for (Message msg = mMessages; msg != null; msg = msg.next) { 765 pw.println(prefix + "Message " + n + ": " + msg.toString(now)); 766 n++; 767 } 768 pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() 769 + ", quitting=" + mQuitting + ")"); 770 } 771 } 772 773 /** 774 * Callback interface for discovering when a thread is going to block 775 * waiting for more messages. 776 */ 777 public static interface IdleHandler { 778 /** 779 * Called when the message queue has run out of messages and will now 780 * wait for more. Return true to keep your idle handler active, false 781 * to have it removed. This may be called if there are still messages 782 * pending in the queue, but they are all scheduled to be dispatched 783 * after the current time. 784 */ queueIdle()785 boolean queueIdle(); 786 } 787 788 /** 789 * A listener which is invoked when file descriptor related events occur. 790 */ 791 public interface OnFileDescriptorEventListener { 792 /** 793 * File descriptor event: Indicates that the file descriptor is ready for input 794 * operations, such as reading. 795 * <p> 796 * The listener should read all available data from the file descriptor 797 * then return <code>true</code> to keep the listener active or <code>false</code> 798 * to remove the listener. 799 * </p><p> 800 * In the case of a socket, this event may be generated to indicate 801 * that there is at least one incoming connection that the listener 802 * should accept. 803 * </p><p> 804 * This event will only be generated if the {@link #EVENT_INPUT} event mask was 805 * specified when the listener was added. 806 * </p> 807 */ 808 public static final int EVENT_INPUT = 1 << 0; 809 810 /** 811 * File descriptor event: Indicates that the file descriptor is ready for output 812 * operations, such as writing. 813 * <p> 814 * The listener should write as much data as it needs. If it could not 815 * write everything at once, then it should return <code>true</code> to 816 * keep the listener active. Otherwise, it should return <code>false</code> 817 * to remove the listener then re-register it later when it needs to write 818 * something else. 819 * </p><p> 820 * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was 821 * specified when the listener was added. 822 * </p> 823 */ 824 public static final int EVENT_OUTPUT = 1 << 1; 825 826 /** 827 * File descriptor event: Indicates that the file descriptor encountered a 828 * fatal error. 829 * <p> 830 * File descriptor errors can occur for various reasons. One common error 831 * is when the remote peer of a socket or pipe closes its end of the connection. 832 * </p><p> 833 * This event may be generated at any time regardless of whether the 834 * {@link #EVENT_ERROR} event mask was specified when the listener was added. 835 * </p> 836 */ 837 public static final int EVENT_ERROR = 1 << 2; 838 839 /** @hide */ 840 @Retention(RetentionPolicy.SOURCE) 841 @IntDef(flag=true, value={EVENT_INPUT, EVENT_OUTPUT, EVENT_ERROR}) 842 public @interface Events {} 843 844 /** 845 * Called when a file descriptor receives events. 846 * 847 * @param fd The file descriptor. 848 * @param events The set of events that occurred: a combination of the 849 * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks. 850 * @return The new set of events to watch, or 0 to unregister the listener. 851 * 852 * @see #EVENT_INPUT 853 * @see #EVENT_OUTPUT 854 * @see #EVENT_ERROR 855 */ onFileDescriptorEvents(@onNull FileDescriptor fd, @Events int events)856 @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events); 857 } 858 859 private static final class FileDescriptorRecord { 860 public final FileDescriptor mDescriptor; 861 public int mEvents; 862 public OnFileDescriptorEventListener mListener; 863 public int mSeq; 864 FileDescriptorRecord(FileDescriptor descriptor, int events, OnFileDescriptorEventListener listener)865 public FileDescriptorRecord(FileDescriptor descriptor, 866 int events, OnFileDescriptorEventListener listener) { 867 mDescriptor = descriptor; 868 mEvents = events; 869 mListener = listener; 870 } 871 } 872 } 873