xref: /aosp_15_r20/frameworks/base/core/java/android/os/LockedMessageQueue/MessageQueue.java (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.os;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.TestApi;
22 import android.compat.annotation.UnsupportedAppUsage;
23 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
24 import android.ravenwood.annotation.RavenwoodRedirect;
25 import android.ravenwood.annotation.RavenwoodRedirectionClass;
26 import android.util.Log;
27 import android.util.Printer;
28 import android.util.SparseArray;
29 import android.util.proto.ProtoOutputStream;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 import dalvik.annotation.optimization.NeverCompile;
34 
35 import java.io.FileDescriptor;
36 import java.lang.annotation.Retention;
37 import java.lang.annotation.RetentionPolicy;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.concurrent.atomic.AtomicLong;
41 
42 /**
43  * Low-level class holding the list of messages to be dispatched by a
44  * {@link Looper}.  Messages are not added directly to a MessageQueue,
45  * but rather through {@link Handler} objects associated with the Looper.
46  *
47  * <p>You can retrieve the MessageQueue for the current thread with
48  * {@link Looper#myQueue() Looper.myQueue()}.
49  */
50 @RavenwoodKeepWholeClass
51 @RavenwoodRedirectionClass("MessageQueue_ravenwood")
52 public final class MessageQueue {
53     private static final String TAG = "LockedMessageQueue";
54     private static final boolean DEBUG = false;
55     private static final boolean TRACE = false;
56 
57     static final class MessageHeap {
58         static final int MESSAGE_HEAP_INITIAL_SIZE = 16;
59 
60         Message[] mHeap = new Message[MESSAGE_HEAP_INITIAL_SIZE];
61         int mNumElements = 0;
62 
parentNodeIdx(int i)63         static int parentNodeIdx(int i) {
64             return (i - 1) >>> 1;
65         }
66 
getParentNode(int i)67         Message getParentNode(int i) {
68             return mHeap[(i - 1) >>> 1];
69         }
70 
rightNodeIdx(int i)71         static int rightNodeIdx(int i) {
72             return 2 * i + 2;
73         }
74 
getRightNode(int i)75         Message getRightNode(int i) {
76             return mHeap[2 * i + 2];
77         }
78 
leftNodeIdx(int i)79         static int leftNodeIdx(int i) {
80             return 2 * i + 1;
81         }
82 
getLeftNode(int i)83         Message getLeftNode(int i) {
84             return mHeap[2 * i + 1];
85         }
86 
size()87         int size() {
88             return mHeap.length;
89         }
90 
numElements()91         int numElements() {
92             return mNumElements;
93         }
94 
isEmpty()95         boolean isEmpty() {
96             return mNumElements == 0;
97         }
98 
getMessageAt(int index)99         Message getMessageAt(int index) {
100             return mHeap[index];
101         }
102 
103         /*
104         * Returns:
105         *    0 if x==y.
106         *    A value less than 0 if x<y.
107         *    A value greater than 0 if x>y.
108         */
compareMessage(Message x, Message y)109         int compareMessage(Message x, Message y) {
110             int compared = Long.compare(x.when, y.when);
111             if (compared == 0) {
112                 compared = Long.compare(x.mInsertSeq, y.mInsertSeq);
113             }
114             return compared;
115         }
116 
compareMessageByIdx(int x, int y)117         int compareMessageByIdx(int x, int y) {
118             return compareMessage(mHeap[x], mHeap[y]);
119         }
120 
swap(int x, int y)121         void swap(int x, int y) {
122             Message tmp = mHeap[x];
123             mHeap[x] = mHeap[y];
124             mHeap[y] = tmp;
125         }
126 
siftDown(int i)127         void siftDown(int i) {
128             int smallest = i;
129             int r, l;
130 
131             while (true) {
132                 r = rightNodeIdx(i);
133                 l = leftNodeIdx(i);
134 
135                 if (r < mNumElements && compareMessageByIdx(r, smallest) < 0) {
136                     smallest = r;
137                 }
138 
139                 if (l < mNumElements && compareMessageByIdx(l, smallest) < 0) {
140                     smallest = l;
141                 }
142 
143                 if (smallest != i) {
144                     swap(i, smallest);
145                     i = smallest;
146                     continue;
147                 }
148                 break;
149             }
150         }
151 
siftUp(int i)152         boolean siftUp(int i) {
153             boolean swapped = false;
154             while (i != 0 && compareMessage(mHeap[i], getParentNode(i)) < 0) {
155                 int p = parentNodeIdx(i);
156 
157                 swap(i, p);
158                 swapped = true;
159                 i = p;
160             }
161 
162             return swapped;
163         }
164 
maybeGrowHeap()165         void maybeGrowHeap() {
166             if (mNumElements == mHeap.length) {
167                 /* Grow by 1.5x */
168                 int newSize = mHeap.length + (mHeap.length >>> 1);
169                 Message[] newHeap;
170                 if (DEBUG) {
171                     Log.v(TAG, "maybeGrowHeap mNumElements " + mNumElements + " mHeap.length "
172                             + mHeap.length + " newSize " + newSize);
173                 }
174 
175                 newHeap = Arrays.copyOf(mHeap, newSize);
176                 mHeap = newHeap;
177             }
178         }
179 
add(Message m)180         void add(Message m) {
181             int i;
182 
183             maybeGrowHeap();
184 
185             i = mNumElements;
186             mNumElements++;
187             mHeap[i] = m;
188 
189             siftUp(i);
190         }
191 
maybeShrinkHeap()192         void maybeShrinkHeap() {
193             /* Shrink by 2x */
194             int newSize = mHeap.length >>> 1;
195 
196             if (newSize >= MESSAGE_HEAP_INITIAL_SIZE
197                     && mNumElements <= newSize) {
198                 Message[] newHeap;
199 
200                 if (DEBUG) {
201                     Log.v(TAG, "maybeShrinkHeap mNumElements " + mNumElements + " mHeap.length "
202                             + mHeap.length + " newSize " + newSize);
203                 }
204 
205                 newHeap = Arrays.copyOf(mHeap, newSize);
206                 mHeap = newHeap;
207             }
208         }
209 
poll()210         Message poll() {
211             if (mNumElements > 0) {
212                 Message ret = mHeap[0];
213                 mNumElements--;
214                 mHeap[0] = mHeap[mNumElements];
215                 mHeap[mNumElements] = null;
216 
217                 siftDown(0);
218 
219                 maybeShrinkHeap();
220                 return ret;
221             }
222             return null;
223         }
224 
peek()225         Message peek() {
226             if (mNumElements > 0) {
227                 return mHeap[0];
228             }
229             return null;
230         }
231 
remove(int i)232         private void remove(int i) throws IllegalArgumentException {
233             if (i > mNumElements || mNumElements == 0) {
234                 throw new IllegalArgumentException("Index " + i + " out of bounds: "
235                         + mNumElements);
236             } else if (i == (mNumElements - 1)) {
237                 mHeap[i] = null;
238                 mNumElements--;
239             } else {
240                 mNumElements--;
241                 mHeap[i] = mHeap[mNumElements];
242                 mHeap[mNumElements] = null;
243                 if (!siftUp(i)) {
244                     siftDown(i);
245                 }
246             }
247             /* Don't shink here, let the caller do this once it has removed all matching items. */
248         }
249 
removeAll()250         void removeAll() {
251             Message m;
252             for (int i = 0; i < mNumElements; i++) {
253                 m = mHeap[i];
254                 mHeap[i] = null;
255                 m.recycleUnchecked();
256             }
257             mNumElements = 0;
258             maybeShrinkHeap();
259         }
260 
261         abstract static class MessageHeapCompare {
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)262             public abstract boolean compareMessage(Message m, Handler h, int what, Object object,
263                     Runnable r, long when);
264         }
265 
findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, MessageHeapCompare compare, boolean removeMatches)266         boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when,
267                 MessageHeapCompare compare, boolean removeMatches) {
268             boolean found = false;
269             /*
270              * Walk the heap backwards so we don't have to re-visit an array element due to
271              * sifting
272              */
273             for (int i = mNumElements - 1; i >= 0; i--) {
274                 if (compare.compareMessage(mHeap[i], h, what, object, r, when)) {
275                     found = true;
276                     if (removeMatches) {
277                         Message m = mHeap[i];
278                         try {
279                             remove(i);
280                         } catch (IllegalArgumentException e) {
281                             Log.wtf(TAG, "Index out of bounds during remove " + e);
282                         }
283                         m.recycleUnchecked();
284                         continue;
285                     }
286                     break;
287                 }
288             }
289             if (found && removeMatches) {
290                 maybeShrinkHeap();
291             }
292             return found;
293         }
294 
295         /*
296         * Keep this for manual debugging. It's easier to pepper the code with this function
297         * than MessageQueue.dump()
298         */
299         @NeverCompile
print()300         void print() {
301             Log.v(TAG, "heap num elem: " + mNumElements + " mHeap.length " + mHeap.length);
302             for (int i = 0; i < mNumElements; i++) {
303                 Log.v(TAG, "[" + i + "]\t" + mHeap[i] + " seq: " + mHeap[i].mInsertSeq + " async: "
304                         + mHeap[i].isAsynchronous());
305             }
306         }
307 
verify(int root)308         boolean verify(int root) {
309             int r = rightNodeIdx(root);
310             int l = leftNodeIdx(root);
311 
312             if (l >= mNumElements && r >= mNumElements) {
313                 return true;
314             }
315 
316             if (l < mNumElements && compareMessageByIdx(l, root) < 0) {
317                 Log.wtf(TAG, "Verify failure: root idx/when: " + root + "/" + mHeap[root].when
318                         + " left node idx/when: " + l + "/" + mHeap[l].when);
319                 return false;
320             }
321 
322             if (r < mNumElements && compareMessageByIdx(r, root) < 0) {
323                 Log.wtf(TAG, "Verify failure: root idx/when: " + root + "/" + mHeap[root].when
324                         + " right node idx/when: " + r + "/" + mHeap[r].when);
325                 return false;
326             }
327 
328             if (!verify(r) || !verify(l)) {
329                 return false;
330             }
331             return true;
332         }
333 
checkDanglingReferences(String where)334         boolean checkDanglingReferences(String where) {
335             /* First, let's make sure we didn't leave any dangling references */
336             for (int i = mNumElements; i < mHeap.length; i++) {
337                 if (mHeap[i] != null) {
338                     Log.wtf(TAG, "[" + where
339                             + "] Verify failure: dangling reference found at index "
340                             + i + ": " + mHeap[i] + " Async " + mHeap[i].isAsynchronous()
341                             + " mNumElements " + mNumElements + " mHeap.length " + mHeap.length);
342                     return false;
343                 }
344             }
345             return true;
346         }
347 
verify()348         boolean verify() {
349             if (!checkDanglingReferences(TAG)) {
350                 return false;
351             }
352             return verify(0);
353         }
354     }
355 
356     // True if the message queue can be quit.
357     @UnsupportedAppUsage
358     private final boolean mQuitAllowed;
359 
360     @UnsupportedAppUsage
361     @SuppressWarnings("unused")
362     private long mPtr; // used by native code
363 
364     private final MessageHeap mPriorityQueue = new MessageHeap();
365     private final MessageHeap mAsyncPriorityQueue = new MessageHeap();
366 
367     /*
368      * This helps us ensure that messages with the same timestamp are inserted in FIFO order.
369      * Increments on each insert, starting at 0. MessaeHeap.compareMessage() will compare sequences
370      * when delivery timestamps are identical.
371      */
372     private long mNextInsertSeq;
373 
374     /*
375      * The exception to the FIFO order rule is sendMessageAtFrontOfQueue().
376      * Those messages must be in LIFO order.
377      * Decrements on each front of queue insert.
378      */
379     private long mNextFrontInsertSeq = -1;
380 
381     @UnsupportedAppUsage
382     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
383     private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
384     private IdleHandler[] mPendingIdleHandlers;
385     private boolean mQuitting;
386 
387     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
388     private boolean mBlocked;
389 
390     // The next barrier token.
391     // Barriers are indicated by messages with a null target whose arg1 field carries the token.
392     @UnsupportedAppUsage
393     private int mNextBarrierToken;
394 
395     @RavenwoodRedirect
nativeInit()396     private native static long nativeInit();
397     @RavenwoodRedirect
nativeDestroy(long ptr)398     private native static void nativeDestroy(long ptr);
399     @UnsupportedAppUsage
400     @RavenwoodRedirect
nativePollOnce(long ptr, int timeoutMillis)401     private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/
402     @RavenwoodRedirect
nativeWake(long ptr)403     private native static void nativeWake(long ptr);
404     @RavenwoodRedirect
nativeIsPolling(long ptr)405     private native static boolean nativeIsPolling(long ptr);
406     @RavenwoodRedirect
nativeSetFileDescriptorEvents(long ptr, int fd, int events)407     private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);
408 
MessageQueue(boolean quitAllowed)409     MessageQueue(boolean quitAllowed) {
410         mQuitAllowed = quitAllowed;
411         mPtr = nativeInit();
412     }
413 
414     @GuardedBy("this")
removeRootFromPriorityQueue(Message msg)415     private void removeRootFromPriorityQueue(Message msg) {
416         Message tmp;
417         if (msg.isAsynchronous()) {
418             tmp = mAsyncPriorityQueue.poll();
419         } else {
420             tmp = mPriorityQueue.poll();
421         }
422         if (DEBUG && tmp != msg) {
423             Log.wtf(TAG, "Unexpected message at head of heap. Wanted: " + msg + " msg.isAsync "
424                     + msg.isAsynchronous() + " Found: " + tmp);
425 
426             mPriorityQueue.print();
427             mAsyncPriorityQueue.print();
428         }
429     }
430 
431     @GuardedBy("this")
pickEarliestMessage(Message x, Message y)432     private Message pickEarliestMessage(Message x, Message y) {
433         if (x != null && y != null) {
434             if (mPriorityQueue.compareMessage(x, y) < 0) {
435                 return x;
436             }
437             return y;
438         }
439 
440         return x != null ? x : y;
441     }
442 
443     @GuardedBy("this")
peekEarliestMessage()444     private Message peekEarliestMessage() {
445         Message x = mPriorityQueue.peek();
446         Message y = mAsyncPriorityQueue.peek();
447 
448         return pickEarliestMessage(x, y);
449     }
450 
451     @GuardedBy("this")
priorityQueuesAreEmpty()452     private boolean priorityQueuesAreEmpty() {
453         return mPriorityQueue.isEmpty() && mAsyncPriorityQueue.isEmpty();
454     }
455 
456     @GuardedBy("this")
priorityQueueHasBarrier()457     private boolean priorityQueueHasBarrier() {
458         Message m = mPriorityQueue.peek();
459 
460         if (m != null && m.target == null) {
461             return true;
462         }
463         return false;
464     }
465 
466     @Override
finalize()467     protected void finalize() throws Throwable {
468         try {
469             dispose();
470         } finally {
471             super.finalize();
472         }
473     }
474 
475     // Disposes of the underlying message queue.
476     // Must only be called on the looper thread or the finalizer.
dispose()477     private void dispose() {
478         if (mPtr != 0) {
479             nativeDestroy(mPtr);
480             mPtr = 0;
481         }
482     }
483 
484     /**
485      * Returns true if the looper has no pending messages which are due to be processed.
486      *
487      * <p>This method is safe to call from any thread.
488      *
489      * @return True if the looper is idle.
490      */
isIdle()491     public boolean isIdle() {
492         synchronized (this) {
493             Message m = peekEarliestMessage();
494             final long now = SystemClock.uptimeMillis();
495 
496             return (priorityQueuesAreEmpty() || now < m.when);
497         }
498     }
499 
500     /**
501      * Add a new {@link IdleHandler} to this message queue.  This may be
502      * removed automatically for you by returning false from
503      * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
504      * invoked, or explicitly removing it with {@link #removeIdleHandler}.
505      *
506      * <p>This method is safe to call from any thread.
507      *
508      * @param handler The IdleHandler to be added.
509      */
addIdleHandler(@onNull IdleHandler handler)510     public void addIdleHandler(@NonNull IdleHandler handler) {
511         if (handler == null) {
512             throw new NullPointerException("Can't add a null IdleHandler");
513         }
514         synchronized (this) {
515             mIdleHandlers.add(handler);
516         }
517     }
518 
519     /**
520      * Remove an {@link IdleHandler} from the queue that was previously added
521      * with {@link #addIdleHandler}.  If the given object is not currently
522      * in the idle list, nothing is done.
523      *
524      * <p>This method is safe to call from any thread.
525      *
526      * @param handler The IdleHandler to be removed.
527      */
removeIdleHandler(@onNull IdleHandler handler)528     public void removeIdleHandler(@NonNull IdleHandler handler) {
529         synchronized (this) {
530             mIdleHandlers.remove(handler);
531         }
532     }
533 
534     /**
535      * Returns whether this looper's thread is currently polling for more work to do.
536      * This is a good signal that the loop is still alive rather than being stuck
537      * handling a callback.  Note that this method is intrinsically racy, since the
538      * state of the loop can change before you get the result back.
539      *
540      * <p>This method is safe to call from any thread.
541      *
542      * @return True if the looper is currently polling for events.
543      * @hide
544      */
isPolling()545     public boolean isPolling() {
546         synchronized (this) {
547             return isPollingLocked();
548         }
549     }
550 
isPollingLocked()551     private boolean isPollingLocked() {
552         // If the loop is quitting then it must not be idling.
553         // We can assume mPtr != 0 when mQuitting is false.
554         return !mQuitting && nativeIsPolling(mPtr);
555     }
556 
557     /**
558      * Adds a file descriptor listener to receive notification when file descriptor
559      * related events occur.
560      * <p>
561      * If the file descriptor has already been registered, the specified events
562      * and listener will replace any that were previously associated with it.
563      * It is not possible to set more than one listener per file descriptor.
564      * </p><p>
565      * It is important to always unregister the listener when the file descriptor
566      * is no longer of use.
567      * </p>
568      *
569      * @param fd The file descriptor for which a listener will be registered.
570      * @param events The set of events to receive: a combination of the
571      * {@link OnFileDescriptorEventListener#EVENT_INPUT},
572      * {@link OnFileDescriptorEventListener#EVENT_OUTPUT}, and
573      * {@link OnFileDescriptorEventListener#EVENT_ERROR} event masks.  If the requested
574      * set of events is zero, then the listener is unregistered.
575      * @param listener The listener to invoke when file descriptor events occur.
576      *
577      * @see OnFileDescriptorEventListener
578      * @see #removeOnFileDescriptorEventListener
579      */
580     @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
addOnFileDescriptorEventListener(@onNull FileDescriptor fd, @OnFileDescriptorEventListener.Events int events, @NonNull OnFileDescriptorEventListener listener)581     public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
582             @OnFileDescriptorEventListener.Events int events,
583             @NonNull OnFileDescriptorEventListener listener) {
584         if (fd == null) {
585             throw new IllegalArgumentException("fd must not be null");
586         }
587         if (listener == null) {
588             throw new IllegalArgumentException("listener must not be null");
589         }
590 
591         synchronized (this) {
592             updateOnFileDescriptorEventListenerLocked(fd, events, listener);
593         }
594     }
595 
596     /**
597      * Removes a file descriptor listener.
598      * <p>
599      * This method does nothing if no listener has been registered for the
600      * specified file descriptor.
601      * </p>
602      *
603      * @param fd The file descriptor whose listener will be unregistered.
604      *
605      * @see OnFileDescriptorEventListener
606      * @see #addOnFileDescriptorEventListener
607      */
608     @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
removeOnFileDescriptorEventListener(@onNull FileDescriptor fd)609     public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
610         if (fd == null) {
611             throw new IllegalArgumentException("fd must not be null");
612         }
613 
614         synchronized (this) {
615             updateOnFileDescriptorEventListenerLocked(fd, 0, null);
616         }
617     }
618 
619     @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events, OnFileDescriptorEventListener listener)620     private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
621             OnFileDescriptorEventListener listener) {
622         final int fdNum = fd.getInt$();
623 
624         int index = -1;
625         FileDescriptorRecord record = null;
626         if (mFileDescriptorRecords != null) {
627             index = mFileDescriptorRecords.indexOfKey(fdNum);
628             if (index >= 0) {
629                 record = mFileDescriptorRecords.valueAt(index);
630                 if (record != null && record.mEvents == events) {
631                     return;
632                 }
633             }
634         }
635 
636         if (events != 0) {
637             events |= OnFileDescriptorEventListener.EVENT_ERROR;
638             if (record == null) {
639                 if (mFileDescriptorRecords == null) {
640                     mFileDescriptorRecords = new SparseArray<FileDescriptorRecord>();
641                 }
642                 record = new FileDescriptorRecord(fd, events, listener);
643                 mFileDescriptorRecords.put(fdNum, record);
644             } else {
645                 record.mListener = listener;
646                 record.mEvents = events;
647                 record.mSeq += 1;
648             }
649             nativeSetFileDescriptorEvents(mPtr, fdNum, events);
650         } else if (record != null) {
651             record.mEvents = 0;
652             mFileDescriptorRecords.removeAt(index);
653             nativeSetFileDescriptorEvents(mPtr, fdNum, 0);
654         }
655     }
656 
657     // Called from native code.
658     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dispatchEvents(int fd, int events)659     private int dispatchEvents(int fd, int events) {
660         // Get the file descriptor record and any state that might change.
661         final FileDescriptorRecord record;
662         final int oldWatchedEvents;
663         final OnFileDescriptorEventListener listener;
664         final int seq;
665         synchronized (this) {
666             record = mFileDescriptorRecords.get(fd);
667             if (record == null) {
668                 return 0; // spurious, no listener registered
669             }
670 
671             oldWatchedEvents = record.mEvents;
672             events &= oldWatchedEvents; // filter events based on current watched set
673             if (events == 0) {
674                 return oldWatchedEvents; // spurious, watched events changed
675             }
676 
677             listener = record.mListener;
678             seq = record.mSeq;
679         }
680 
681         // Invoke the listener outside of the lock.
682         int newWatchedEvents = listener.onFileDescriptorEvents(
683                 record.mDescriptor, events);
684         if (newWatchedEvents != 0) {
685             newWatchedEvents |= OnFileDescriptorEventListener.EVENT_ERROR;
686         }
687 
688         // Update the file descriptor record if the listener changed the set of
689         // events to watch and the listener itself hasn't been updated since.
690         if (newWatchedEvents != oldWatchedEvents) {
691             synchronized (this) {
692                 int index = mFileDescriptorRecords.indexOfKey(fd);
693                 if (index >= 0 && mFileDescriptorRecords.valueAt(index) == record
694                         && record.mSeq == seq) {
695                     record.mEvents = newWatchedEvents;
696                     if (newWatchedEvents == 0) {
697                         mFileDescriptorRecords.removeAt(index);
698                     }
699                 }
700             }
701         }
702 
703         // Return the new set of events to watch for native code to take care of.
704         return newWatchedEvents;
705     }
706 
707     private static final AtomicLong mMessagesDelivered = new AtomicLong();
708 
709     @UnsupportedAppUsage
next()710     Message next() {
711         // Return here if the message loop has already quit and been disposed.
712         // This can happen if the application tries to restart a looper after quit
713         // which is not supported.
714         final long ptr = mPtr;
715         if (ptr == 0) {
716             return null;
717         }
718 
719         int pendingIdleHandlerCount = -1; // -1 only during first iteration
720         int nextPollTimeoutMillis = 0;
721         for (;;) {
722             if (nextPollTimeoutMillis != 0) {
723                 Binder.flushPendingCommands();
724             }
725 
726             nativePollOnce(ptr, nextPollTimeoutMillis);
727 
728             synchronized (this) {
729                 // Try to retrieve the next message.  Return if found.
730                 final long now = SystemClock.uptimeMillis();
731                 Message prevMsg = null;
732                 Message msg = peekEarliestMessage();
733 
734                 if (DEBUG && msg != null) {
735                     Log.v(TAG, "Next found message " + msg + " isAsynchronous: "
736                             + msg.isAsynchronous() + " target " + msg.target);
737                 }
738 
739                 if (msg != null && !msg.isAsynchronous() && msg.target == null) {
740                     // Stalled by a barrier.  Find the next asynchronous message in the queue.
741                     msg = mAsyncPriorityQueue.peek();
742                     if (DEBUG) {
743                         Log.v(TAG, "Next message was barrier async msg: " + msg);
744                     }
745                 }
746 
747                 if (msg != null) {
748                     if (now < msg.when) {
749                         // Next message is not ready.  Set a timeout to wake up when it is ready.
750                         nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
751                     } else {
752                         mBlocked = false;
753                         removeRootFromPriorityQueue(msg);
754                         if (DEBUG) Log.v(TAG, "Returning message: " + msg);
755                         msg.markInUse();
756                         if (TRACE) {
757                             Trace.setCounter("MQ.Delivered", mMessagesDelivered.incrementAndGet());
758                         }
759                         return msg;
760                     }
761                 } else {
762                     // No more messages.
763                     nextPollTimeoutMillis = -1;
764                 }
765 
766                 // Process the quit message now that all pending messages have been handled.
767                 if (mQuitting) {
768                     dispose();
769                     return null;
770                 }
771 
772                 // If first time idle, then get the number of idlers to run.
773                 // Idle handles only run if the queue is empty or if the first message
774                 // in the queue (possibly a barrier) is due to be handled in the future.
775                 Message next = peekEarliestMessage();
776                 if (pendingIdleHandlerCount < 0
777                         && (next == null || now < next.when)) {
778                     pendingIdleHandlerCount = mIdleHandlers.size();
779                 }
780                 if (pendingIdleHandlerCount <= 0) {
781                     // No idle handlers to run.  Loop and wait some more.
782                     mBlocked = true;
783                     continue;
784                 }
785 
786                 if (mPendingIdleHandlers == null) {
787                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
788                 }
789                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
790             }
791 
792             // Run the idle handlers.
793             // We only ever reach this code block during the first iteration.
794             for (int i = 0; i < pendingIdleHandlerCount; i++) {
795                 final IdleHandler idler = mPendingIdleHandlers[i];
796                 mPendingIdleHandlers[i] = null; // release the reference to the handler
797 
798                 boolean keep = false;
799                 try {
800                     keep = idler.queueIdle();
801                 } catch (Throwable t) {
802                     Log.wtf(TAG, "IdleHandler threw exception", t);
803                 }
804 
805                 if (!keep) {
806                     synchronized (this) {
807                         mIdleHandlers.remove(idler);
808                     }
809                 }
810             }
811 
812             // Reset the idle handler count to 0 so we do not run them again.
813             pendingIdleHandlerCount = 0;
814 
815             // While calling an idle handler, a new message could have been delivered
816             // so go back and look again for a pending message without waiting.
817             nextPollTimeoutMillis = 0;
818         }
819     }
820 
quit(boolean safe)821     void quit(boolean safe) {
822         if (!mQuitAllowed) {
823             throw new IllegalStateException("Main thread not allowed to quit.");
824         }
825 
826         synchronized (this) {
827             if (mQuitting) {
828                 return;
829             }
830             mQuitting = true;
831 
832             if (safe) {
833                 removeAllFutureMessagesLocked();
834             } else {
835                 removeAllMessagesLocked();
836             }
837 
838             // We can assume mPtr != 0 because mQuitting was previously false.
839             nativeWake(mPtr);
840         }
841     }
842 
843     /**
844      * Posts a synchronization barrier to the Looper's message queue.
845      *
846      * Message processing occurs as usual until the message queue encounters the
847      * synchronization barrier that has been posted.  When the barrier is encountered,
848      * later synchronous messages in the queue are stalled (prevented from being executed)
849      * until the barrier is released by calling {@link #removeSyncBarrier} and specifying
850      * the token that identifies the synchronization barrier.
851      *
852      * This method is used to immediately postpone execution of all subsequently posted
853      * synchronous messages until a condition is met that releases the barrier.
854      * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier
855      * and continue to be processed as usual.
856      *
857      * This call must be always matched by a call to {@link #removeSyncBarrier} with
858      * the same token to ensure that the message queue resumes normal operation.
859      * Otherwise the application will probably hang!
860      *
861      * @return A token that uniquely identifies the barrier.  This token must be
862      * passed to {@link #removeSyncBarrier} to release the barrier.
863      *
864      * @hide
865      */
866     @UnsupportedAppUsage
867     @TestApi
postSyncBarrier()868     public int postSyncBarrier() {
869         return postSyncBarrier(SystemClock.uptimeMillis());
870     }
871 
postSyncBarrier(long when)872     private int postSyncBarrier(long when) {
873         // Enqueue a new sync barrier token.
874         // We don't need to wake the queue because the purpose of a barrier is to stall it.
875         synchronized (this) {
876             final int token = mNextBarrierToken++;
877             final Message msg = Message.obtain();
878             msg.arg1 = token;
879 
880             enqueueMessageUnchecked(msg, when);
881             return token;
882         }
883     }
884 
885     private class MatchBarrierToken extends MessageHeap.MessageHeapCompare {
886         int mBarrierToken;
887 
MatchBarrierToken(int token)888         MatchBarrierToken(int token) {
889             super();
890             mBarrierToken = token;
891         }
892 
893         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)894         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
895                 long when) {
896             if (m.target == null && m.arg1 == mBarrierToken) {
897                 return true;
898             }
899             return false;
900         }
901     }
902 
903     /**
904      * Removes a synchronization barrier.
905      *
906      * @param token The synchronization barrier token that was returned by
907      * {@link #postSyncBarrier}.
908      *
909      * @throws IllegalStateException if the barrier was not found.
910      *
911      * @hide
912      */
913     @UnsupportedAppUsage
914     @TestApi
removeSyncBarrier(int token)915     public void removeSyncBarrier(int token) {
916         final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
917 
918         // Remove a sync barrier token from the queue.
919         // If the queue is no longer stalled by a barrier then wake it.
920         synchronized (this) {
921             boolean removed;
922             Message first = mPriorityQueue.peek();
923 
924             removed = mPriorityQueue.findOrRemoveMessages(null, 0, null, null, 0,
925                     matchBarrierToken, true);
926             if (removed && first != null) {
927                 // If the loop is quitting then it is already awake.
928                 // We can assume mPtr != 0 when mQuitting is false.
929                 if (first.target == null && first.arg1 == token && !mQuitting) {
930                     nativeWake(mPtr);
931                 }
932             } else if (!removed) {
933                 throw new IllegalStateException("The specified message queue synchronization "
934                         + " barrier token has not been posted or has already been removed.");
935             }
936         }
937     }
938 
enqueueMessage(Message msg, long when)939     boolean enqueueMessage(Message msg, long when) {
940         if (msg.target == null) {
941             throw new IllegalArgumentException("Message must have a target.");
942         }
943 
944         return enqueueMessageUnchecked(msg, when);
945     }
946 
enqueueMessageUnchecked(Message msg, long when)947     boolean enqueueMessageUnchecked(Message msg, long when) {
948         synchronized (this) {
949             if (mQuitting) {
950                 IllegalStateException e = new IllegalStateException(
951                         msg.target + " sending message to a Handler on a dead thread");
952                 Log.w(TAG, e.getMessage(), e);
953                 msg.recycle();
954                 return false;
955             }
956 
957             if (msg.isInUse()) {
958                 throw new IllegalStateException(msg + " This message is already in use.");
959             }
960 
961             msg.markInUse();
962             msg.when = when;
963             msg.mInsertSeq = when != 0 ? mNextInsertSeq++ : mNextFrontInsertSeq--;
964             if (DEBUG) Log.v(TAG, "Enqueue message: " + msg);
965             boolean needWake;
966             boolean isBarrier = msg.target == null;
967             Message first = peekEarliestMessage();
968 
969             if (priorityQueuesAreEmpty() || when == 0 || when < first.when) {
970                 needWake = mBlocked && !isBarrier;
971             } else {
972                 Message firstNonAsyncMessage =
973                         first.isAsynchronous() ? mPriorityQueue.peek() : first;
974 
975                 needWake = mBlocked && firstNonAsyncMessage != null
976                         && firstNonAsyncMessage.target == null && msg.isAsynchronous();
977             }
978 
979             if (msg.isAsynchronous()) {
980                 mAsyncPriorityQueue.add(msg);
981             } else {
982                 mPriorityQueue.add(msg);
983             }
984 
985             // We can assume mPtr != 0 because mQuitting is false.
986             if (needWake) {
987                 nativeWake(mPtr);
988             }
989         }
990         return true;
991     }
992 
993     @GuardedBy("this")
findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when, MessageHeap.MessageHeapCompare compare, boolean removeMatches)994     boolean findOrRemoveMessages(Handler h, int what, Object object, Runnable r, long when,
995                 MessageHeap.MessageHeapCompare compare, boolean removeMatches) {
996         boolean found = mPriorityQueue.findOrRemoveMessages(h, what, object, r, when, compare,
997                 removeMatches);
998         boolean foundAsync = mAsyncPriorityQueue.findOrRemoveMessages(h, what, object, r, when,
999                 compare, removeMatches);
1000         return found || foundAsync;
1001     }
1002 
1003     private static class MatchHandlerWhatAndObject extends MessageHeap.MessageHeapCompare {
1004         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1005         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1006                 long when) {
1007             if (m.target == h && m.what == what && (object == null || m.obj == object)) {
1008                 return true;
1009             }
1010             return false;
1011         }
1012     }
1013     private static final MatchHandlerWhatAndObject sMatchHandlerWhatAndObject =
1014             new MatchHandlerWhatAndObject();
1015 
hasMessages(Handler h, int what, Object object)1016     boolean hasMessages(Handler h, int what, Object object) {
1017         if (h == null) {
1018             return false;
1019         }
1020 
1021         synchronized (this) {
1022             return findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObject,
1023                     false);
1024         }
1025     }
1026 
1027     private static class MatchHandlerWhatAndObjectEquals extends MessageHeap.MessageHeapCompare {
1028         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1029         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1030                 long when) {
1031             if (m.target == h && m.what == what && (object == null || object.equals(m.obj))) {
1032                 return true;
1033             }
1034             return false;
1035         }
1036     }
1037     private static final MatchHandlerWhatAndObjectEquals sMatchHandlerWhatAndObjectEquals =
1038             new MatchHandlerWhatAndObjectEquals();
hasEqualMessages(Handler h, int what, Object object)1039     boolean hasEqualMessages(Handler h, int what, Object object) {
1040         if (h == null) {
1041             return false;
1042         }
1043 
1044         synchronized (this) {
1045             return findOrRemoveMessages(h, what, object, null, 0,
1046                     sMatchHandlerWhatAndObjectEquals, false);
1047         }
1048     }
1049 
1050     private static class MatchHandlerRunnableAndObject extends MessageHeap.MessageHeapCompare {
1051         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1052         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1053                 long when) {
1054             if (m.target == h && m.callback == r && (object == null || m.obj == object)) {
1055                 return true;
1056             }
1057             return false;
1058         }
1059     }
1060     private static final MatchHandlerRunnableAndObject sMatchHandlerRunnableAndObject =
1061             new MatchHandlerRunnableAndObject();
1062     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
hasMessages(Handler h, Runnable r, Object object)1063     boolean hasMessages(Handler h, Runnable r, Object object) {
1064         if (h == null) {
1065             return false;
1066         }
1067 
1068         synchronized (this) {
1069             return findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObject,
1070                     false);
1071         }
1072     }
1073 
1074     private static class MatchHandler extends MessageHeap.MessageHeapCompare {
1075         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1076         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1077                 long when) {
1078             if (m.target == h) {
1079                 return true;
1080             }
1081             return false;
1082         }
1083     }
1084     private static final MatchHandler sMatchHandler = new MatchHandler();
hasMessages(Handler h)1085     boolean hasMessages(Handler h) {
1086         if (h == null) {
1087             return false;
1088         }
1089 
1090         synchronized (this) {
1091             return findOrRemoveMessages(h, -1, null, null, 0, sMatchHandler, false);
1092         }
1093     }
1094 
removeMessages(Handler h, int what, Object object)1095     void removeMessages(Handler h, int what, Object object) {
1096         if (h == null) {
1097             return;
1098         }
1099 
1100         synchronized (this) {
1101             findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObject, true);
1102         }
1103     }
1104 
removeEqualMessages(Handler h, int what, Object object)1105     void removeEqualMessages(Handler h, int what, Object object) {
1106         if (h == null) {
1107             return;
1108         }
1109 
1110         synchronized (this) {
1111             findOrRemoveMessages(h, what, object, null, 0, sMatchHandlerWhatAndObjectEquals, true);
1112         }
1113     }
1114 
removeMessages(Handler h, Runnable r, Object object)1115     void removeMessages(Handler h, Runnable r, Object object) {
1116         if (h == null || r == null) {
1117             return;
1118         }
1119 
1120         synchronized (this) {
1121             findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObject, true);
1122         }
1123     }
1124 
1125     private static class MatchHandlerRunnableAndObjectEquals
1126             extends MessageHeap.MessageHeapCompare {
1127         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1128         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1129                 long when) {
1130             if (m.target == h && m.callback == r && (object == null || object.equals(m.obj))) {
1131                 return true;
1132             }
1133             return false;
1134         }
1135     }
1136     private static final MatchHandlerRunnableAndObjectEquals sMatchHandlerRunnableAndObjectEquals =
1137             new MatchHandlerRunnableAndObjectEquals();
removeEqualMessages(Handler h, Runnable r, Object object)1138     void removeEqualMessages(Handler h, Runnable r, Object object) {
1139         if (h == null || r == null) {
1140             return;
1141         }
1142 
1143         synchronized (this) {
1144             findOrRemoveMessages(h, -1, object, r, 0, sMatchHandlerRunnableAndObjectEquals, true);
1145         }
1146     }
1147 
1148     private static class MatchHandlerAndObject extends MessageHeap.MessageHeapCompare {
1149         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1150         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1151                 long when) {
1152             if (m.target == h && (object == null || m.obj == object)) {
1153                 return true;
1154             }
1155             return false;
1156         }
1157     }
1158     private static final MatchHandlerAndObject sMatchHandlerAndObject = new MatchHandlerAndObject();
removeCallbacksAndMessages(Handler h, Object object)1159     void removeCallbacksAndMessages(Handler h, Object object) {
1160         if (h == null) {
1161             return;
1162         }
1163 
1164         synchronized (this) {
1165             findOrRemoveMessages(h, -1, object, null, 0, sMatchHandlerAndObject, true);
1166         }
1167     }
1168 
1169     private static class MatchHandlerAndObjectEquals extends MessageHeap.MessageHeapCompare {
1170         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1171         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1172                 long when) {
1173             if (m.target == h && (object == null || object.equals(m.obj))) {
1174                 return true;
1175             }
1176             return false;
1177         }
1178     }
1179     private static final MatchHandlerAndObjectEquals sMatchHandlerAndObjectEquals =
1180             new MatchHandlerAndObjectEquals();
removeCallbacksAndEqualMessages(Handler h, Object object)1181     void removeCallbacksAndEqualMessages(Handler h, Object object) {
1182         if (h == null) {
1183             return;
1184         }
1185 
1186         synchronized (this) {
1187             findOrRemoveMessages(h, -1, object, null, 0, sMatchHandlerAndObjectEquals, true);
1188         }
1189     }
1190 
1191     @GuardedBy("this")
removeAllMessagesLocked()1192     private void removeAllMessagesLocked() {
1193         mPriorityQueue.removeAll();
1194         mAsyncPriorityQueue.removeAll();
1195     }
1196 
1197     private static class MatchAllFutureMessages extends MessageHeap.MessageHeapCompare {
1198         @Override
compareMessage(Message m, Handler h, int what, Object object, Runnable r, long when)1199         public boolean compareMessage(Message m, Handler h, int what, Object object, Runnable r,
1200                 long when) {
1201             if (m.when > when) {
1202                 return true;
1203             }
1204             return false;
1205         }
1206     }
1207     private static final MatchAllFutureMessages sMatchAllFutureMessages =
1208             new MatchAllFutureMessages();
1209     @GuardedBy("this")
removeAllFutureMessagesLocked()1210     private void removeAllFutureMessagesLocked() {
1211         findOrRemoveMessages(null, -1, null, null, SystemClock.uptimeMillis(),
1212                 sMatchAllFutureMessages, true);
1213     }
1214 
1215     @NeverCompile
dumpPriorityQueue(Printer pw, String prefix, Handler h, MessageHeap priorityQueue)1216     int dumpPriorityQueue(Printer pw, String prefix, Handler h, MessageHeap priorityQueue) {
1217         int n = 0;
1218         long now = SystemClock.uptimeMillis();
1219         for (int i = 0; i < priorityQueue.numElements(); i++) {
1220             Message m = priorityQueue.getMessageAt(i);
1221             if (h == null && h == m.target) {
1222                 pw.println(prefix + "Message " + n + ": " + m.toString(now));
1223                 n++;
1224             }
1225         }
1226         return n;
1227     }
1228 
1229     @NeverCompile
dumpPriorityQueue(ProtoOutputStream proto, MessageHeap priorityQueue)1230     void dumpPriorityQueue(ProtoOutputStream proto, MessageHeap priorityQueue) {
1231         for (int i = 0; i < priorityQueue.numElements(); i++) {
1232             Message m = priorityQueue.getMessageAt(i);
1233             m.dumpDebug(proto, MessageQueueProto.MESSAGES);
1234         }
1235     }
1236 
1237     @NeverCompile
dump(Printer pw, String prefix, Handler h)1238     void dump(Printer pw, String prefix, Handler h) {
1239         synchronized (this) {
1240             pw.println(prefix + "(MessageQueue is using Locked implementation)");
1241             long now = SystemClock.uptimeMillis();
1242             int n = dumpPriorityQueue(pw, prefix, h, mPriorityQueue);
1243             n += dumpPriorityQueue(pw, prefix, h, mAsyncPriorityQueue);
1244             pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked()
1245                     + ", quitting=" + mQuitting + ")");
1246         }
1247     }
1248 
1249     @NeverCompile
dumpDebug(ProtoOutputStream proto, long fieldId)1250     void dumpDebug(ProtoOutputStream proto, long fieldId) {
1251         final long messageQueueToken = proto.start(fieldId);
1252         synchronized (this) {
1253             dumpPriorityQueue(proto, mPriorityQueue);
1254             dumpPriorityQueue(proto, mAsyncPriorityQueue);
1255             proto.write(MessageQueueProto.IS_POLLING_LOCKED, isPollingLocked());
1256             proto.write(MessageQueueProto.IS_QUITTING, mQuitting);
1257         }
1258         proto.end(messageQueueToken);
1259     }
1260 
1261     /**
1262      * Callback interface for discovering when a thread is going to block
1263      * waiting for more messages.
1264      */
1265     public static interface IdleHandler {
1266         /**
1267          * Called when the message queue has run out of messages and will now
1268          * wait for more.  Return true to keep your idle handler active, false
1269          * to have it removed.  This may be called if there are still messages
1270          * pending in the queue, but they are all scheduled to be dispatched
1271          * after the current time.
1272          */
queueIdle()1273         boolean queueIdle();
1274     }
1275 
1276     /**
1277      * A listener which is invoked when file descriptor related events occur.
1278      */
1279     public interface OnFileDescriptorEventListener {
1280         /**
1281          * File descriptor event: Indicates that the file descriptor is ready for input
1282          * operations, such as reading.
1283          * <p>
1284          * The listener should read all available data from the file descriptor
1285          * then return <code>true</code> to keep the listener active or <code>false</code>
1286          * to remove the listener.
1287          * </p><p>
1288          * In the case of a socket, this event may be generated to indicate
1289          * that there is at least one incoming connection that the listener
1290          * should accept.
1291          * </p><p>
1292          * This event will only be generated if the {@link #EVENT_INPUT} event mask was
1293          * specified when the listener was added.
1294          * </p>
1295          */
1296         public static final int EVENT_INPUT = 1 << 0;
1297 
1298         /**
1299          * File descriptor event: Indicates that the file descriptor is ready for output
1300          * operations, such as writing.
1301          * <p>
1302          * The listener should write as much data as it needs.  If it could not
1303          * write everything at once, then it should return <code>true</code> to
1304          * keep the listener active.  Otherwise, it should return <code>false</code>
1305          * to remove the listener then re-register it later when it needs to write
1306          * something else.
1307          * </p><p>
1308          * This event will only be generated if the {@link #EVENT_OUTPUT} event mask was
1309          * specified when the listener was added.
1310          * </p>
1311          */
1312         public static final int EVENT_OUTPUT = 1 << 1;
1313 
1314         /**
1315          * File descriptor event: Indicates that the file descriptor encountered a
1316          * fatal error.
1317          * <p>
1318          * File descriptor errors can occur for various reasons.  One common error
1319          * is when the remote peer of a socket or pipe closes its end of the connection.
1320          * </p><p>
1321          * This event may be generated at any time regardless of whether the
1322          * {@link #EVENT_ERROR} event mask was specified when the listener was added.
1323          * </p>
1324          */
1325         public static final int EVENT_ERROR = 1 << 2;
1326 
1327         /** @hide */
1328         @Retention(RetentionPolicy.SOURCE)
1329         @IntDef(flag = true, prefix = { "EVENT_" }, value = {
1330                 EVENT_INPUT,
1331                 EVENT_OUTPUT,
1332                 EVENT_ERROR
1333         })
1334         public @interface Events {}
1335 
1336         /**
1337          * Called when a file descriptor receives events.
1338          *
1339          * @param fd The file descriptor.
1340          * @param events The set of events that occurred: a combination of the
1341          * {@link #EVENT_INPUT}, {@link #EVENT_OUTPUT}, and {@link #EVENT_ERROR} event masks.
1342          * @return The new set of events to watch, or 0 to unregister the listener.
1343          *
1344          * @see #EVENT_INPUT
1345          * @see #EVENT_OUTPUT
1346          * @see #EVENT_ERROR
1347          */
onFileDescriptorEvents(@onNull FileDescriptor fd, @Events int events)1348         @Events int onFileDescriptorEvents(@NonNull FileDescriptor fd, @Events int events);
1349     }
1350 
1351     private static final class FileDescriptorRecord {
1352         public final FileDescriptor mDescriptor;
1353         public int mEvents;
1354         public OnFileDescriptorEventListener mListener;
1355         public int mSeq;
1356 
FileDescriptorRecord(FileDescriptor descriptor, int events, OnFileDescriptorEventListener listener)1357         public FileDescriptorRecord(FileDescriptor descriptor,
1358                 int events, OnFileDescriptorEventListener listener) {
1359             mDescriptor = descriptor;
1360             mEvents = events;
1361             mListener = listener;
1362         }
1363     }
1364 }
1365