Home | History | Annotate | Download | only in os
      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 
     61     private native static long nativeInit();
     62     private native static void nativeDestroy(long ptr);
     63     private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
     64     private native static void nativeWake(long ptr);
     65     private native static boolean nativeIsPolling(long ptr);
     66     private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
     67 
     68     MessageQueue(boolean quitAllowed) {
     69         mQuitAllowed = quitAllowed;
     70         mPtr = nativeInit();
     71     }
     72 
     73     @Override
     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.
     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      */
     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      */
    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      */
    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      */
    150     public boolean isPolling() {
    151         synchronized (this) {
    152             return isPollingLocked();
    153         }
    154     }
    155 
    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      */
    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      */
    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 
    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.
    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 
    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 
    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      */
    458     public int postSyncBarrier() {
    459         return postSyncBarrier(SystemClock.uptimeMillis());
    460     }
    461 
    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      */
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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          */
    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          */
    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 
    865         public FileDescriptorRecord(FileDescriptor descriptor,
    866                 int events, OnFileDescriptorEventListener listener) {
    867             mDescriptor = descriptor;
    868             mEvents = events;
    869             mListener = listener;
    870         }
    871     }
    872 }
    873