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