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.util.Log;
20import android.util.Printer;
21
22import 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 */
32public 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
51    private native static long nativeInit();
52    private native static void nativeDestroy(long ptr);
53    private native static void nativePollOnce(long ptr, int timeoutMillis);
54    private native static void nativeWake(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         */
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     */
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     */
98    public void removeIdleHandler(IdleHandler handler) {
99        synchronized (this) {
100            mIdleHandlers.remove(handler);
101        }
102    }
103
104    MessageQueue(boolean quitAllowed) {
105        mQuitAllowed = quitAllowed;
106        mPtr = nativeInit();
107    }
108
109    @Override
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.
120    private void dispose() {
121        if (mPtr != 0) {
122            nativeDestroy(mPtr);
123            mPtr = 0;
124        }
125    }
126
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
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
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
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
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
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
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
403    boolean isIdling() {
404        synchronized (this) {
405            return isIdlingLocked();
406        }
407    }
408
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
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
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
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
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
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
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