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.util.Log;
20 import android.util.Printer;
21 
22 import java.util.ArrayList;
23 
24 /**
25  * Low-level class holding the list of messages to be dispatched by a
26  * {@link Looper}.  Messages are not added directly to a MessageQueue,
27  * but rather through {@link Handler} objects associated with the Looper.
28  *
29  * <p>You can retrieve the MessageQueue for the current thread with
30  * {@link Looper#myQueue() Looper.myQueue()}.
31  */
32 public final class MessageQueue {
33     // True if the message queue can be quit.
34     private final boolean mQuitAllowed;
35 
36     @SuppressWarnings("unused")
37     private long mPtr; // used by native code
38 
39     Message mMessages;
40     private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
41     private IdleHandler[] mPendingIdleHandlers;
42     private boolean mQuitting;
43 
44     // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
45     private boolean mBlocked;
46 
47     // The next barrier token.
48     // Barriers are indicated by messages with a null target whose arg1 field carries the token.
49     private int mNextBarrierToken;
50 
nativeInit()51     private native static long nativeInit();
nativeDestroy(long ptr)52     private native static void nativeDestroy(long ptr);
nativePollOnce(long ptr, int timeoutMillis)53     private native static void nativePollOnce(long ptr, int timeoutMillis);
nativeWake(long ptr)54     private native static void nativeWake(long ptr);
nativeIsIdling(long ptr)55     private native static boolean nativeIsIdling(long ptr);
56 
57     /**
58      * Callback interface for discovering when a thread is going to block
59      * waiting for more messages.
60      */
61     public static interface IdleHandler {
62         /**
63          * Called when the message queue has run out of messages and will now
64          * wait for more.  Return true to keep your idle handler active, false
65          * to have it removed.  This may be called if there are still messages
66          * pending in the queue, but they are all scheduled to be dispatched
67          * after the current time.
68          */
queueIdle()69         boolean queueIdle();
70     }
71 
72     /**
73      * Add a new {@link IdleHandler} to this message queue.  This may be
74      * removed automatically for you by returning false from
75      * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
76      * invoked, or explicitly removing it with {@link #removeIdleHandler}.
77      *
78      * <p>This method is safe to call from any thread.
79      *
80      * @param handler The IdleHandler to be added.
81      */
addIdleHandler(IdleHandler handler)82     public void addIdleHandler(IdleHandler handler) {
83         if (handler == null) {
84             throw new NullPointerException("Can't add a null IdleHandler");
85         }
86         synchronized (this) {
87             mIdleHandlers.add(handler);
88         }
89     }
90 
91     /**
92      * Remove an {@link IdleHandler} from the queue that was previously added
93      * with {@link #addIdleHandler}.  If the given object is not currently
94      * in the idle list, nothing is done.
95      *
96      * @param handler The IdleHandler to be removed.
97      */
removeIdleHandler(IdleHandler handler)98     public void removeIdleHandler(IdleHandler handler) {
99         synchronized (this) {
100             mIdleHandlers.remove(handler);
101         }
102     }
103 
MessageQueue(boolean quitAllowed)104     MessageQueue(boolean quitAllowed) {
105         mQuitAllowed = quitAllowed;
106         mPtr = nativeInit();
107     }
108 
109     @Override
finalize()110     protected void finalize() throws Throwable {
111         try {
112             dispose();
113         } finally {
114             super.finalize();
115         }
116     }
117 
118     // Disposes of the underlying message queue.
119     // Must only be called on the looper thread or the finalizer.
dispose()120     private void dispose() {
121         if (mPtr != 0) {
122             nativeDestroy(mPtr);
123             mPtr = 0;
124         }
125     }
126 
next()127     Message next() {
128         // Return here if the message loop has already quit and been disposed.
129         // This can happen if the application tries to restart a looper after quit
130         // which is not supported.
131         final long ptr = mPtr;
132         if (ptr == 0) {
133             return null;
134         }
135 
136         int pendingIdleHandlerCount = -1; // -1 only during first iteration
137         int nextPollTimeoutMillis = 0;
138         for (;;) {
139             if (nextPollTimeoutMillis != 0) {
140                 Binder.flushPendingCommands();
141             }
142 
143             nativePollOnce(ptr, nextPollTimeoutMillis);
144 
145             synchronized (this) {
146                 // Try to retrieve the next message.  Return if found.
147                 final long now = SystemClock.uptimeMillis();
148                 Message prevMsg = null;
149                 Message msg = mMessages;
150                 if (msg != null && msg.target == null) {
151                     // Stalled by a barrier.  Find the next asynchronous message in the queue.
152                     do {
153                         prevMsg = msg;
154                         msg = msg.next;
155                     } while (msg != null && !msg.isAsynchronous());
156                 }
157                 if (msg != null) {
158                     if (now < msg.when) {
159                         // Next message is not ready.  Set a timeout to wake up when it is ready.
160                         nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
161                     } else {
162                         // Got a message.
163                         mBlocked = false;
164                         if (prevMsg != null) {
165                             prevMsg.next = msg.next;
166                         } else {
167                             mMessages = msg.next;
168                         }
169                         msg.next = null;
170                         if (false) Log.v("MessageQueue", "Returning message: " + msg);
171                         return msg;
172                     }
173                 } else {
174                     // No more messages.
175                     nextPollTimeoutMillis = -1;
176                 }
177 
178                 // Process the quit message now that all pending messages have been handled.
179                 if (mQuitting) {
180                     dispose();
181                     return null;
182                 }
183 
184                 // If first time idle, then get the number of idlers to run.
185                 // Idle handles only run if the queue is empty or if the first message
186                 // in the queue (possibly a barrier) is due to be handled in the future.
187                 if (pendingIdleHandlerCount < 0
188                         && (mMessages == null || now < mMessages.when)) {
189                     pendingIdleHandlerCount = mIdleHandlers.size();
190                 }
191                 if (pendingIdleHandlerCount <= 0) {
192                     // No idle handlers to run.  Loop and wait some more.
193                     mBlocked = true;
194                     continue;
195                 }
196 
197                 if (mPendingIdleHandlers == null) {
198                     mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
199                 }
200                 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
201             }
202 
203             // Run the idle handlers.
204             // We only ever reach this code block during the first iteration.
205             for (int i = 0; i < pendingIdleHandlerCount; i++) {
206                 final IdleHandler idler = mPendingIdleHandlers[i];
207                 mPendingIdleHandlers[i] = null; // release the reference to the handler
208 
209                 boolean keep = false;
210                 try {
211                     keep = idler.queueIdle();
212                 } catch (Throwable t) {
213                     Log.wtf("MessageQueue", "IdleHandler threw exception", t);
214                 }
215 
216                 if (!keep) {
217                     synchronized (this) {
218                         mIdleHandlers.remove(idler);
219                     }
220                 }
221             }
222 
223             // Reset the idle handler count to 0 so we do not run them again.
224             pendingIdleHandlerCount = 0;
225 
226             // While calling an idle handler, a new message could have been delivered
227             // so go back and look again for a pending message without waiting.
228             nextPollTimeoutMillis = 0;
229         }
230     }
231 
quit(boolean safe)232     void quit(boolean safe) {
233         if (!mQuitAllowed) {
234             throw new IllegalStateException("Main thread not allowed to quit.");
235         }
236 
237         synchronized (this) {
238             if (mQuitting) {
239                 return;
240             }
241             mQuitting = true;
242 
243             if (safe) {
244                 removeAllFutureMessagesLocked();
245             } else {
246                 removeAllMessagesLocked();
247             }
248 
249             // We can assume mPtr != 0 because mQuitting was previously false.
250             nativeWake(mPtr);
251         }
252     }
253 
enqueueSyncBarrier(long when)254     int enqueueSyncBarrier(long when) {
255         // Enqueue a new sync barrier token.
256         // We don't need to wake the queue because the purpose of a barrier is to stall it.
257         synchronized (this) {
258             final int token = mNextBarrierToken++;
259             final Message msg = Message.obtain();
260             msg.markInUse();
261             msg.when = when;
262             msg.arg1 = token;
263 
264             Message prev = null;
265             Message p = mMessages;
266             if (when != 0) {
267                 while (p != null && p.when <= when) {
268                     prev = p;
269                     p = p.next;
270                 }
271             }
272             if (prev != null) { // invariant: p == prev.next
273                 msg.next = p;
274                 prev.next = msg;
275             } else {
276                 msg.next = p;
277                 mMessages = msg;
278             }
279             return token;
280         }
281     }
282 
removeSyncBarrier(int token)283     void removeSyncBarrier(int token) {
284         // Remove a sync barrier token from the queue.
285         // If the queue is no longer stalled by a barrier then wake it.
286         synchronized (this) {
287             Message prev = null;
288             Message p = mMessages;
289             while (p != null && (p.target != null || p.arg1 != token)) {
290                 prev = p;
291                 p = p.next;
292             }
293             if (p == null) {
294                 throw new IllegalStateException("The specified message queue synchronization "
295                         + " barrier token has not been posted or has already been removed.");
296             }
297             final boolean needWake;
298             if (prev != null) {
299                 prev.next = p.next;
300                 needWake = false;
301             } else {
302                 mMessages = p.next;
303                 needWake = mMessages == null || mMessages.target != null;
304             }
305             p.recycleUnchecked();
306 
307             // If the loop is quitting then it is already awake.
308             // We can assume mPtr != 0 when mQuitting is false.
309             if (needWake && !mQuitting) {
310                 nativeWake(mPtr);
311             }
312         }
313     }
314 
enqueueMessage(Message msg, long when)315     boolean enqueueMessage(Message msg, long when) {
316         if (msg.target == null) {
317             throw new IllegalArgumentException("Message must have a target.");
318         }
319         if (msg.isInUse()) {
320             throw new IllegalStateException(msg + " This message is already in use.");
321         }
322 
323         synchronized (this) {
324             if (mQuitting) {
325                 IllegalStateException e = new IllegalStateException(
326                         msg.target + " sending message to a Handler on a dead thread");
327                 Log.w("MessageQueue", e.getMessage(), e);
328                 msg.recycle();
329                 return false;
330             }
331 
332             msg.markInUse();
333             msg.when = when;
334             Message p = mMessages;
335             boolean needWake;
336             if (p == null || when == 0 || when < p.when) {
337                 // New head, wake up the event queue if blocked.
338                 msg.next = p;
339                 mMessages = msg;
340                 needWake = mBlocked;
341             } else {
342                 // Inserted within the middle of the queue.  Usually we don't have to wake
343                 // up the event queue unless there is a barrier at the head of the queue
344                 // and the message is the earliest asynchronous message in the queue.
345                 needWake = mBlocked && p.target == null && msg.isAsynchronous();
346                 Message prev;
347                 for (;;) {
348                     prev = p;
349                     p = p.next;
350                     if (p == null || when < p.when) {
351                         break;
352                     }
353                     if (needWake && p.isAsynchronous()) {
354                         needWake = false;
355                     }
356                 }
357                 msg.next = p; // invariant: p == prev.next
358                 prev.next = msg;
359             }
360 
361             // We can assume mPtr != 0 because mQuitting is false.
362             if (needWake) {
363                 nativeWake(mPtr);
364             }
365         }
366         return true;
367     }
368 
hasMessages(Handler h, int what, Object object)369     boolean hasMessages(Handler h, int what, Object object) {
370         if (h == null) {
371             return false;
372         }
373 
374         synchronized (this) {
375             Message p = mMessages;
376             while (p != null) {
377                 if (p.target == h && p.what == what && (object == null || p.obj == object)) {
378                     return true;
379                 }
380                 p = p.next;
381             }
382             return false;
383         }
384     }
385 
hasMessages(Handler h, Runnable r, Object object)386     boolean hasMessages(Handler h, Runnable r, Object object) {
387         if (h == null) {
388             return false;
389         }
390 
391         synchronized (this) {
392             Message p = mMessages;
393             while (p != null) {
394                 if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
395                     return true;
396                 }
397                 p = p.next;
398             }
399             return false;
400         }
401     }
402 
isIdling()403     boolean isIdling() {
404         synchronized (this) {
405             return isIdlingLocked();
406         }
407     }
408 
isIdlingLocked()409     private boolean isIdlingLocked() {
410         // If the loop is quitting then it must not be idling.
411         // We can assume mPtr != 0 when mQuitting is false.
412         return !mQuitting && nativeIsIdling(mPtr);
413      }
414 
removeMessages(Handler h, int what, Object object)415     void removeMessages(Handler h, int what, Object object) {
416         if (h == null) {
417             return;
418         }
419 
420         synchronized (this) {
421             Message p = mMessages;
422 
423             // Remove all messages at front.
424             while (p != null && p.target == h && p.what == what
425                    && (object == null || p.obj == object)) {
426                 Message n = p.next;
427                 mMessages = n;
428                 p.recycleUnchecked();
429                 p = n;
430             }
431 
432             // Remove all messages after front.
433             while (p != null) {
434                 Message n = p.next;
435                 if (n != null) {
436                     if (n.target == h && n.what == what
437                         && (object == null || n.obj == object)) {
438                         Message nn = n.next;
439                         n.recycleUnchecked();
440                         p.next = nn;
441                         continue;
442                     }
443                 }
444                 p = n;
445             }
446         }
447     }
448 
removeMessages(Handler h, Runnable r, Object object)449     void removeMessages(Handler h, Runnable r, Object object) {
450         if (h == null || r == null) {
451             return;
452         }
453 
454         synchronized (this) {
455             Message p = mMessages;
456 
457             // Remove all messages at front.
458             while (p != null && p.target == h && p.callback == r
459                    && (object == null || p.obj == object)) {
460                 Message n = p.next;
461                 mMessages = n;
462                 p.recycleUnchecked();
463                 p = n;
464             }
465 
466             // Remove all messages after front.
467             while (p != null) {
468                 Message n = p.next;
469                 if (n != null) {
470                     if (n.target == h && n.callback == r
471                         && (object == null || n.obj == object)) {
472                         Message nn = n.next;
473                         n.recycleUnchecked();
474                         p.next = nn;
475                         continue;
476                     }
477                 }
478                 p = n;
479             }
480         }
481     }
482 
removeCallbacksAndMessages(Handler h, Object object)483     void removeCallbacksAndMessages(Handler h, Object object) {
484         if (h == null) {
485             return;
486         }
487 
488         synchronized (this) {
489             Message p = mMessages;
490 
491             // Remove all messages at front.
492             while (p != null && p.target == h
493                     && (object == null || p.obj == object)) {
494                 Message n = p.next;
495                 mMessages = n;
496                 p.recycleUnchecked();
497                 p = n;
498             }
499 
500             // Remove all messages after front.
501             while (p != null) {
502                 Message n = p.next;
503                 if (n != null) {
504                     if (n.target == h && (object == null || n.obj == object)) {
505                         Message nn = n.next;
506                         n.recycleUnchecked();
507                         p.next = nn;
508                         continue;
509                     }
510                 }
511                 p = n;
512             }
513         }
514     }
515 
removeAllMessagesLocked()516     private void removeAllMessagesLocked() {
517         Message p = mMessages;
518         while (p != null) {
519             Message n = p.next;
520             p.recycleUnchecked();
521             p = n;
522         }
523         mMessages = null;
524     }
525 
removeAllFutureMessagesLocked()526     private void removeAllFutureMessagesLocked() {
527         final long now = SystemClock.uptimeMillis();
528         Message p = mMessages;
529         if (p != null) {
530             if (p.when > now) {
531                 removeAllMessagesLocked();
532             } else {
533                 Message n;
534                 for (;;) {
535                     n = p.next;
536                     if (n == null) {
537                         return;
538                     }
539                     if (n.when > now) {
540                         break;
541                     }
542                     p = n;
543                 }
544                 p.next = null;
545                 do {
546                     p = n;
547                     n = p.next;
548                     p.recycleUnchecked();
549                 } while (n != null);
550             }
551         }
552     }
553 
dump(Printer pw, String prefix)554     void dump(Printer pw, String prefix) {
555         synchronized (this) {
556             long now = SystemClock.uptimeMillis();
557             int n = 0;
558             for (Message msg = mMessages; msg != null; msg = msg.next) {
559                 pw.println(prefix + "Message " + n + ": " + msg.toString(now));
560                 n++;
561             }
562             pw.println(prefix + "(Total messages: " + n + ", idling=" + isIdlingLocked()
563                     + ", quitting=" + mQuitting + ")");
564         }
565     }
566 }
567