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
17package android.os;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.os.MessageQueueProto;
22import android.util.Log;
23import android.util.Printer;
24import android.util.SparseArray;
25import android.util.proto.ProtoOutputStream;
26
27import java.io.FileDescriptor;
28import java.lang.annotation.Retention;
29import java.lang.annotation.RetentionPolicy;
30import 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 */
40public 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            nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
258        }
259    }
260
261    // Called from native code.
262    private int dispatchEvents(int fd, int events) {
263        // Get the file descriptor record and any state that might change.
264        final FileDescriptorRecord record;
265        final int oldWatchedEvents;
266        final OnFileDescriptorEventListener listener;
267        final int seq;
268        synchronized (this) {
269            record = mFileDescriptorRecords.get(fd);
270            if (record == null) {
271                return 0; // spurious, no listener registered
272            }
273
274            oldWatchedEvents = record.mEvents;
275            events &= oldWatchedEvents; // filter events based on current watched set
276            if (events == 0) {
277                return oldWatchedEvents; // spurious, watched events changed
278            }
279
280            listener = record.mListener;
281            seq = record.mSeq;
282        }
283
284        // Invoke the listener outside of the lock.
285        int newWatchedEvents = listener.onFileDescriptorEvents(
286                record.mDescriptor, events);
287        if (newWatchedEvents != 0) {
288            newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
289        }
290
291        // Update the file descriptor record if the listener changed the set of
292        // events to watch and the listener itself hasn't been updated since.
293        if (newWatchedEvents != oldWatchedEvents) {
294            synchronized (this) {
295                int index = mFileDescriptorRecords.indexOfKey(fd);
296                if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
297                        && record.mSeq == seq) {
298                    record.mEvents = newWatchedEvents;
299                    if (newWatchedEvents == 0) {
300                        mFileDescriptorRecords.removeAt(index);
301                    }
302                }
303            }
304        }
305
306        // Return the new set of events to watch for native code to take care of.
307        return newWatchedEvents;
308    }
309
310    Message next() {
311        // Return here if the message loop has already quit and been disposed.
312        // This can happen if the application tries to restart a looper after quit
313        // which is not supported.
314        final long ptr = mPtr;
315        if (ptr == 0) {
316            return null;
317        }
318
319        int pendingIdleHandlerCount = -1; // -1 only during first iteration
320        int nextPollTimeoutMillis = 0;
321        for (;;) {
322            if (nextPollTimeoutMillis != 0) {
323                Binder.flushPendingCommands();
324            }
325
326            nativePollOnce(ptr, nextPollTimeoutMillis);
327
328            synchronized (this) {
329                // Try to retrieve the next message.  Return if found.
330                final long now = SystemClock.uptimeMillis();
331                Message prevMsg = null;
332                Message msg = mMessages;
333                if (msg != null && msg.target == null) {
334                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
335                    do {
336                        prevMsg = msg;
337                        msg = msg.next;
338                    } while (msg != null && !msg.isAsynchronous());
339                }
340                if (msg != null) {
341                    if (now < msg.when) {
342                        // Next message is not ready.  Set a timeout to wake up when it is ready.
343                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
344                    } else {
345                        // Got a message.
346                        mBlocked = false;
347                        if (prevMsg != null) {
348                            prevMsg.next = msg.next;
349                        } else {
350                            mMessages = msg.next;
351                        }
352                        msg.next = null;
353                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
354                        msg.markInUse();
355                        return msg;
356                    }
357                } else {
358                    // No more messages.
359                    nextPollTimeoutMillis = -1;
360                }
361
362                // Process the quit message now that all pending messages have been handled.
363                if (mQuitting) {
364                    dispose();
365                    return null;
366                }
367
368                // If first time idle, then get the number of idlers to run.
369                // Idle handles only run if the queue is empty or if the first message
370                // in the queue (possibly a barrier) is due to be handled in the future.
371                if (pendingIdleHandlerCount < 0
372                        && (mMessages == null || now < mMessages.when)) {
373                    pendingIdleHandlerCount = mIdleHandlers.size();
374                }
375                if (pendingIdleHandlerCount <= 0) {
376                    // No idle handlers to run.  Loop and wait some more.
377                    mBlocked = true;
378                    continue;
379                }
380
381                if (mPendingIdleHandlers == null) {
382                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
383                }
384                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
385            }
386
387            // Run the idle handlers.
388            // We only ever reach this code block during the first iteration.
389            for (int i = 0; i < pendingIdleHandlerCount; i++) {
390                final IdleHandler idler = mPendingIdleHandlers[i];
391                mPendingIdleHandlers[i] = null; // release the reference to the handler
392
393                boolean keep = false;
394                try {
395                    keep = idler.queueIdle();
396                } catch (Throwable t) {
397                    Log.wtf(TAG, "IdleHandler threw exception", t);
398                }
399
400                if (!keep) {
401                    synchronized (this) {
402                        mIdleHandlers.remove(idler);
403                    }
404                }
405            }
406
407            // Reset the idle handler count to 0 so we do not run them again.
408            pendingIdleHandlerCount = 0;
409
410            // While calling an idle handler, a new message could have been delivered
411            // so go back and look again for a pending message without waiting.
412            nextPollTimeoutMillis = 0;
413        }
414    }
415
416    void quit(boolean safe) {
417        if (!mQuitAllowed) {
418            throw new IllegalStateException("Main thread not allowed to quit.");
419        }
420
421        synchronized (this) {
422            if (mQuitting) {
423                return;
424            }
425            mQuitting = true;
426
427            if (safe) {
428                removeAllFutureMessagesLocked();
429            } else {
430                removeAllMessagesLocked();
431            }
432
433            // We can assume mPtr != 0 because mQuitting was previously false.
434            nativeWake(mPtr);
435        }
436    }
437
438    /**
439     * Posts a synchronization barrier to the Looper's message queue.
440     *
441     * Message processing occurs as usual until the message queue encounters the
442     * synchronization barrier that has been posted.  When the barrier is encountered,
443     * later synchronous messages in the queue are stalled (prevented from being executed)
444     * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
445     * the token that identifies the synchronization barrier.
446     *
447     * This method is used to immediately postpone execution of all subsequently posted
448     * synchronous messages until a condition is met that releases the barrier.
449     * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
450     * and continue to be processed as usual.
451     *
452     * This call must be always matched by a call to {@link #removeSyncBarrier} with
453     * the same token to ensure that the message queue resumes normal operation.
454     * Otherwise the application will probably hang!
455     *
456     * @return A token that uniquely identifies the barrier.  This token must be
457     * passed to {@link #removeSyncBarrier} to release the barrier.
458     *
459     * @hide
460     */
461    public int postSyncBarrier() {
462        return postSyncBarrier(SystemClock.uptimeMillis());
463    }
464
465    private int postSyncBarrier(long when) {
466        // Enqueue a new sync barrier token.
467        // We don't need to wake the queue because the purpose of a barrier is to stall it.
468        synchronized (this) {
469            final int token = mNextBarrierToken++;
470            final Message msg = Message.obtain();
471            msg.markInUse();
472            msg.when = when;
473            msg.arg1 = token;
474
475            Message prev = null;
476            Message p = mMessages;
477            if (when != 0) {
478                while (p != null && p.when <= when) {
479                    prev = p;
480                    p = p.next;
481                }
482            }
483            if (prev != null) { // invariant: p == prev.next
484                msg.next = p;
485                prev.next = msg;
486            } else {
487                msg.next = p;
488                mMessages = msg;
489            }
490            return token;
491        }
492    }
493
494    /**
495     * Removes a synchronization barrier.
496     *
497     * @param token The synchronization barrier token that was returned by
498     * {@link #postSyncBarrier}.
499     *
500     * @throws IllegalStateException if the barrier was not found.
501     *
502     * @hide
503     */
504    public void removeSyncBarrier(int token) {
505        // Remove a sync barrier token from the queue.
506        // If the queue is no longer stalled by a barrier then wake it.
507        synchronized (this) {
508            Message prev = null;
509            Message p = mMessages;
510            while (p != null && (p.target != null || p.arg1 != token)) {
511                prev = p;
512                p = p.next;
513            }
514            if (p == null) {
515                throw new IllegalStateException("The specified message queue synchronization "
516                        + " barrier token has not been posted or has already been removed.");
517            }
518            final boolean needWake;
519            if (prev != null) {
520                prev.next = p.next;
521                needWake = false;
522            } else {
523                mMessages = p.next;
524                needWake = mMessages == null || mMessages.target != null;
525            }
526            p.recycleUnchecked();
527
528            // If the loop is quitting then it is already awake.
529            // We can assume mPtr != 0 when mQuitting is false.
530            if (needWake && !mQuitting) {
531                nativeWake(mPtr);
532            }
533        }
534    }
535
536    boolean enqueueMessage(Message msg, long when) {
537        if (msg.target == null) {
538            throw new IllegalArgumentException("Message must have a target.");
539        }
540        if (msg.isInUse()) {
541            throw new IllegalStateException(msg + " This message is already in use.");
542        }
543
544        synchronized (this) {
545            if (mQuitting) {
546                IllegalStateException e = new IllegalStateException(
547                        msg.target + " sending message to a Handler on a dead thread");
548                Log.w(TAG, e.getMessage(), e);
549                msg.recycle();
550                return false;
551            }
552
553            msg.markInUse();
554            msg.when = when;
555            Message p = mMessages;
556            boolean needWake;
557            if (p == null || when == 0 || when < p.when) {
558                // New head, wake up the event queue if blocked.
559                msg.next = p;
560                mMessages = msg;
561                needWake = mBlocked;
562            } else {
563                // Inserted within the middle of the queue.  Usually we don't have to wake
564                // up the event queue unless there is a barrier at the head of the queue
565                // and the message is the earliest asynchronous message in the queue.
566                needWake = mBlocked && p.target == null && msg.isAsynchronous();
567                Message prev;
568                for (;;) {
569                    prev = p;
570                    p = p.next;
571                    if (p == null || when < p.when) {
572                        break;
573                    }
574                    if (needWake && p.isAsynchronous()) {
575                        needWake = false;
576                    }
577                }
578                msg.next = p; // invariant: p == prev.next
579                prev.next = msg;
580            }
581
582            // We can assume mPtr != 0 because mQuitting is false.
583            if (needWake) {
584                nativeWake(mPtr);
585            }
586        }
587        return true;
588    }
589
590    boolean hasMessages(Handler h, int what, Object object) {
591        if (h == null) {
592            return false;
593        }
594
595        synchronized (this) {
596            Message p = mMessages;
597            while (p != null) {
598                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
599                    return true;
600                }
601                p = p.next;
602            }
603            return false;
604        }
605    }
606
607    boolean hasMessages(Handler h, Runnable r, Object object) {
608        if (h == null) {
609            return false;
610        }
611
612        synchronized (this) {
613            Message p = mMessages;
614            while (p != null) {
615                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
616                    return true;
617                }
618                p = p.next;
619            }
620            return false;
621        }
622    }
623
624    boolean hasMessages(Handler h) {
625        if (h == null) {
626            return false;
627        }
628
629        synchronized (this) {
630            Message p = mMessages;
631            while (p != null) {
632                if (p.target == h) {
633                    return true;
634                }
635                p = p.next;
636            }
637            return false;
638        }
639    }
640
641    void removeMessages(Handler h, int what, Object object) {
642        if (h == null) {
643            return;
644        }
645
646        synchronized (this) {
647            Message p = mMessages;
648
649            // Remove all messages at front.
650            while (p != null && p.target == h && p.what == what
651                   && (object == null || p.obj == object)) {
652                Message n = p.next;
653                mMessages = n;
654                p.recycleUnchecked();
655                p = n;
656            }
657
658            // Remove all messages after front.
659            while (p != null) {
660                Message n = p.next;
661                if (n != null) {
662                    if (n.target == h && n.what == what
663                        && (object == null || n.obj == object)) {
664                        Message nn = n.next;
665                        n.recycleUnchecked();
666                        p.next = nn;
667                        continue;
668                    }
669                }
670                p = n;
671            }
672        }
673    }
674
675    void removeMessages(Handler h, Runnable r, Object object) {
676        if (h == null || r == null) {
677            return;
678        }
679
680        synchronized (this) {
681            Message p = mMessages;
682
683            // Remove all messages at front.
684            while (p != null && p.target == h && p.callback == r
685                   && (object == null || p.obj == object)) {
686                Message n = p.next;
687                mMessages = n;
688                p.recycleUnchecked();
689                p = n;
690            }
691
692            // Remove all messages after front.
693            while (p != null) {
694                Message n = p.next;
695                if (n != null) {
696                    if (n.target == h && n.callback == r
697                        && (object == null || n.obj == object)) {
698                        Message nn = n.next;
699                        n.recycleUnchecked();
700                        p.next = nn;
701                        continue;
702                    }
703                }
704                p = n;
705            }
706        }
707    }
708
709    void removeCallbacksAndMessages(Handler h, Object object) {
710        if (h == null) {
711            return;
712        }
713
714        synchronized (this) {
715            Message p = mMessages;
716
717            // Remove all messages at front.
718            while (p != null && p.target == h
719                    && (object == null || p.obj == object)) {
720                Message n = p.next;
721                mMessages = n;
722                p.recycleUnchecked();
723                p = n;
724            }
725
726            // Remove all messages after front.
727            while (p != null) {
728                Message n = p.next;
729                if (n != null) {
730                    if (n.target == h && (object == null || n.obj == object)) {
731                        Message nn = n.next;
732                        n.recycleUnchecked();
733                        p.next = nn;
734                        continue;
735                    }
736                }
737                p = n;
738            }
739        }
740    }
741
742    private void removeAllMessagesLocked() {
743        Message p = mMessages;
744        while (p != null) {
745            Message n = p.next;
746            p.recycleUnchecked();
747            p = n;
748        }
749        mMessages = null;
750    }
751
752    private void removeAllFutureMessagesLocked() {
753        final long now = SystemClock.uptimeMillis();
754        Message p = mMessages;
755        if (p != null) {
756            if (p.when > now) {
757                removeAllMessagesLocked();
758            } else {
759                Message n;
760                for (;;) {
761                    n = p.next;
762                    if (n == null) {
763                        return;
764                    }
765                    if (n.when > now) {
766                        break;
767                    }
768                    p = n;
769                }
770                p.next = null;
771                do {
772                    p = n;
773                    n = p.next;
774                    p.recycleUnchecked();
775                } while (n != null);
776            }
777        }
778    }
779
780    void dump(Printer pw, String prefix, Handler h) {
781        synchronized (this) {
782            long now = SystemClock.uptimeMillis();
783            int n = 0;
784            for (Message msg = mMessages; msg != null; msg = msg.next) {
785                if (h == null || h == msg.target) {
786                    pw.println(prefix + "Message " + n + ": " + msg.toString(now));
787                }
788                n++;
789            }
790            pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
791                    + ", quitting=" + mQuitting + ")");
792        }
793    }
794
795    void writeToProto(ProtoOutputStream proto, long fieldId) {
796        final long messageQueueToken = proto.start(fieldId);
797        synchronized (this) {
798            for (Message msg = mMessages; msg != null; msg = msg.next) {
799                msg.writeToProto(proto, MessageQueueProto.MESSAGES);
800            }
801            proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
802            proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
803        }
804        proto.end(messageQueueToken);
805    }
806
807    /**
808     * Callback interface for discovering when a thread is going to block
809     * waiting for more messages.
810     */
811    public static interface IdleHandler {
812        /**
813         * Called when the message queue has run out of messages and will now
814         * wait for more.  Return true to keep your idle handler active, false
815         * to have it removed.  This may be called if there are still messages
816         * pending in the queue, but they are all scheduled to be dispatched
817         * after the current time.
818         */
819        boolean queueIdle();
820    }
821
822    /**
823     * A listener which is invoked when file descriptor related events occur.
824     */
825    public interface OnFileDescriptorEventListener {
826        /**
827         * File descriptor event: Indicates that the file descriptor is ready for input
828         * operations, such as reading.
829         * <p>
830         * The listener should read all available data from the file descriptor
831         * then return <code>true</code> to keep the listener active or <code>false</code>
832         * to remove the listener.
833         * </p><p>
834         * In the case of a socket, this event may be generated to indicate
835         * that there is at least one incoming connection that the listener
836         * should accept.
837         * </p><p>
838         * This event will only be generated if the {@link #EVENT_INPUT} event mask was
839         * specified when the listener was added.
840         * </p>
841         */
842        public static final int EVENT_INPUT = 1 << 0;
843
844        /**
845         * File descriptor event: Indicates that the file descriptor is ready for output
846         * operations, such as writing.
847         * <p>
848         * The listener should write as much data as it needs.  If it could not
849         * write everything at once, then it should return <code>true</code> to
850         * keep the listener active.  Otherwise, it should return <code>false</code>
851         * to remove the listener then re-register it later when it needs to write
852         * something else.
853         * </p><p>
854         * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
855         * specified when the listener was added.
856         * </p>
857         */
858        public static final int EVENT_OUTPUT = 1 << 1;
859
860        /**
861         * File descriptor event: Indicates that the file descriptor encountered a
862         * fatal error.
863         * <p>
864         * File descriptor errors can occur for various reasons.  One common error
865         * is when the remote peer of a socket or pipe closes its end of the connection.
866         * </p><p>
867         * This event may be generated at any time regardless of whether the
868         * {@link #EVENT_ERROR} event mask was specified when the listener was added.
869         * </p>
870         */
871        public static final int EVENT_ERROR = 1 << 2;
872
873        /** @hide */
874        @Retention(RetentionPolicy.SOURCE)
875        @IntDef(flag = true, prefix = { "EVENT_" }, value = {
876                EVENT_INPUT,
877                EVENT_OUTPUT,
878                EVENT_ERROR
879        })
880        public @interface Events {}
881
882        /**
883         * Called when a file descriptor receives events.
884         *
885         * @param fd The file descriptor.
886         * @param events The set of events that occurred: a combination of the
887         * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
888         * @return The new set of events to watch, or 0 to unregister the listener.
889         *
890         * @see #EVENT_INPUT
891         * @see #EVENT_OUTPUT
892         * @see #EVENT_ERROR
893         */
894        @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
895    }
896
897    private static final class FileDescriptorRecord {
898        public final FileDescriptor mDescriptor;
899        public int mEvents;
900        public OnFileDescriptorEventListener mListener;
901        public int mSeq;
902
903        public FileDescriptorRecord(FileDescriptor descriptor,
904                int events, OnFileDescriptorEventListener listener) {
905            mDescriptor = descriptor;
906            mEvents = events;
907            mListener = listener;
908        }
909    }
910}
911