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