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