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