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