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.AndroidRuntimeException;
20import android.util.Log;
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 class MessageQueue {
33    // True if the message queue can be quit.
34    private final boolean mQuitAllowed;
35
36    @SuppressWarnings("unused")
37    private int 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 mQuiting;
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 void nativeInit();
52    private native void nativeDestroy();
53    private native void nativePollOnce(int ptr, int timeoutMillis);
54    private native void nativeWake(int ptr);
55
56    /**
57     * Callback interface for discovering when a thread is going to block
58     * waiting for more messages.
59     */
60    public static interface IdleHandler {
61        /**
62         * Called when the message queue has run out of messages and will now
63         * wait for more.  Return true to keep your idle handler active, false
64         * to have it removed.  This may be called if there are still messages
65         * pending in the queue, but they are all scheduled to be dispatched
66         * after the current time.
67         */
68        boolean queueIdle();
69    }
70
71    /**
72     * Add a new {@link IdleHandler} to this message queue.  This may be
73     * removed automatically for you by returning false from
74     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
75     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
76     *
77     * <p>This method is safe to call from any thread.
78     *
79     * @param handler The IdleHandler to be added.
80     */
81    public final void addIdleHandler(IdleHandler handler) {
82        if (handler == null) {
83            throw new NullPointerException("Can't add a null IdleHandler");
84        }
85        synchronized (this) {
86            mIdleHandlers.add(handler);
87        }
88    }
89
90    /**
91     * Remove an {@link IdleHandler} from the queue that was previously added
92     * with {@link #addIdleHandler}.  If the given object is not currently
93     * in the idle list, nothing is done.
94     *
95     * @param handler The IdleHandler to be removed.
96     */
97    public final void removeIdleHandler(IdleHandler handler) {
98        synchronized (this) {
99            mIdleHandlers.remove(handler);
100        }
101    }
102
103    MessageQueue(boolean quitAllowed) {
104        mQuitAllowed = quitAllowed;
105        nativeInit();
106    }
107
108    @Override
109    protected void finalize() throws Throwable {
110        try {
111            nativeDestroy();
112        } finally {
113            super.finalize();
114        }
115    }
116
117    final Message next() {
118        int pendingIdleHandlerCount = -1; // -1 only during first iteration
119        int nextPollTimeoutMillis = 0;
120
121        for (;;) {
122            if (nextPollTimeoutMillis != 0) {
123                Binder.flushPendingCommands();
124            }
125            nativePollOnce(mPtr, nextPollTimeoutMillis);
126
127            synchronized (this) {
128                if (mQuiting) {
129                    return null;
130                }
131
132                // Try to retrieve the next message.  Return if found.
133                final long now = SystemClock.uptimeMillis();
134                Message prevMsg = null;
135                Message msg = mMessages;
136                if (msg != null && msg.target == null) {
137                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
138                    do {
139                        prevMsg = msg;
140                        msg = msg.next;
141                    } while (msg != null && !msg.isAsynchronous());
142                }
143                if (msg != null) {
144                    if (now < msg.when) {
145                        // Next message is not ready.  Set a timeout to wake up when it is ready.
146                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
147                    } else {
148                        // Got a message.
149                        mBlocked = false;
150                        if (prevMsg != null) {
151                            prevMsg.next = msg.next;
152                        } else {
153                            mMessages = msg.next;
154                        }
155                        msg.next = null;
156                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
157                        msg.markInUse();
158                        return msg;
159                    }
160                } else {
161                    // No more messages.
162                    nextPollTimeoutMillis = -1;
163                }
164
165                // If first time idle, then get the number of idlers to run.
166                // Idle handles only run if the queue is empty or if the first message
167                // in the queue (possibly a barrier) is due to be handled in the future.
168                if (pendingIdleHandlerCount < 0
169                        && (mMessages == null || now < mMessages.when)) {
170                    pendingIdleHandlerCount = mIdleHandlers.size();
171                }
172                if (pendingIdleHandlerCount <= 0) {
173                    // No idle handlers to run.  Loop and wait some more.
174                    mBlocked = true;
175                    continue;
176                }
177
178                if (mPendingIdleHandlers == null) {
179                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
180                }
181                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
182            }
183
184            // Run the idle handlers.
185            // We only ever reach this code block during the first iteration.
186            for (int i = 0; i < pendingIdleHandlerCount; i++) {
187                final IdleHandler idler = mPendingIdleHandlers[i];
188                mPendingIdleHandlers[i] = null; // release the reference to the handler
189
190                boolean keep = false;
191                try {
192                    keep = idler.queueIdle();
193                } catch (Throwable t) {
194                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
195                }
196
197                if (!keep) {
198                    synchronized (this) {
199                        mIdleHandlers.remove(idler);
200                    }
201                }
202            }
203
204            // Reset the idle handler count to 0 so we do not run them again.
205            pendingIdleHandlerCount = 0;
206
207            // While calling an idle handler, a new message could have been delivered
208            // so go back and look again for a pending message without waiting.
209            nextPollTimeoutMillis = 0;
210        }
211    }
212
213    final void quit() {
214        if (!mQuitAllowed) {
215            throw new RuntimeException("Main thread not allowed to quit.");
216        }
217
218        synchronized (this) {
219            if (mQuiting) {
220                return;
221            }
222            mQuiting = true;
223        }
224        nativeWake(mPtr);
225    }
226
227    final int enqueueSyncBarrier(long when) {
228        // Enqueue a new sync barrier token.
229        // We don't need to wake the queue because the purpose of a barrier is to stall it.
230        synchronized (this) {
231            final int token = mNextBarrierToken++;
232            final Message msg = Message.obtain();
233            msg.arg1 = token;
234
235            Message prev = null;
236            Message p = mMessages;
237            if (when != 0) {
238                while (p != null && p.when <= when) {
239                    prev = p;
240                    p = p.next;
241                }
242            }
243            if (prev != null) { // invariant: p == prev.next
244                msg.next = p;
245                prev.next = msg;
246            } else {
247                msg.next = p;
248                mMessages = msg;
249            }
250            return token;
251        }
252    }
253
254    final void removeSyncBarrier(int token) {
255        // Remove a sync barrier token from the queue.
256        // If the queue is no longer stalled by a barrier then wake it.
257        final boolean needWake;
258        synchronized (this) {
259            Message prev = null;
260            Message p = mMessages;
261            while (p != null && (p.target != null || p.arg1 != token)) {
262                prev = p;
263                p = p.next;
264            }
265            if (p == null) {
266                throw new IllegalStateException("The specified message queue synchronization "
267                        + " barrier token has not been posted or has already been removed.");
268            }
269            if (prev != null) {
270                prev.next = p.next;
271                needWake = false;
272            } else {
273                mMessages = p.next;
274                needWake = mMessages == null || mMessages.target != null;
275            }
276            p.recycle();
277        }
278        if (needWake) {
279            nativeWake(mPtr);
280        }
281    }
282
283    final boolean enqueueMessage(Message msg, long when) {
284        if (msg.isInUse()) {
285            throw new AndroidRuntimeException(msg + " This message is already in use.");
286        }
287        if (msg.target == null) {
288            throw new AndroidRuntimeException("Message must have a target.");
289        }
290
291        boolean needWake;
292        synchronized (this) {
293            if (mQuiting) {
294                RuntimeException e = new RuntimeException(
295                        msg.target + " sending message to a Handler on a dead thread");
296                Log.w("MessageQueue", e.getMessage(), e);
297                return false;
298            }
299
300            msg.when = when;
301            Message p = mMessages;
302            if (p == null || when == 0 || when < p.when) {
303                // New head, wake up the event queue if blocked.
304                msg.next = p;
305                mMessages = msg;
306                needWake = mBlocked;
307            } else {
308                // Inserted within the middle of the queue.  Usually we don't have to wake
309                // up the event queue unless there is a barrier at the head of the queue
310                // and the message is the earliest asynchronous message in the queue.
311                needWake = mBlocked && p.target == null && msg.isAsynchronous();
312                Message prev;
313                for (;;) {
314                    prev = p;
315                    p = p.next;
316                    if (p == null || when < p.when) {
317                        break;
318                    }
319                    if (needWake && p.isAsynchronous()) {
320                        needWake = false;
321                    }
322                }
323                msg.next = p; // invariant: p == prev.next
324                prev.next = msg;
325            }
326        }
327        if (needWake) {
328            nativeWake(mPtr);
329        }
330        return true;
331    }
332
333    final boolean hasMessages(Handler h, int what, Object object) {
334        if (h == null) {
335            return false;
336        }
337
338        synchronized (this) {
339            Message p = mMessages;
340            while (p != null) {
341                if (p.target == h && p.what == what && (object == null || p.obj == object)) {
342                    return true;
343                }
344                p = p.next;
345            }
346            return false;
347        }
348    }
349
350    final boolean hasMessages(Handler h, Runnable r, Object object) {
351        if (h == null) {
352            return false;
353        }
354
355        synchronized (this) {
356            Message p = mMessages;
357            while (p != null) {
358                if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
359                    return true;
360                }
361                p = p.next;
362            }
363            return false;
364        }
365    }
366
367    final void removeMessages(Handler h, int what, Object object) {
368        if (h == null) {
369            return;
370        }
371
372        synchronized (this) {
373            Message p = mMessages;
374
375            // Remove all messages at front.
376            while (p != null && p.target == h && p.what == what
377                   && (object == null || p.obj == object)) {
378                Message n = p.next;
379                mMessages = n;
380                p.recycle();
381                p = n;
382            }
383
384            // Remove all messages after front.
385            while (p != null) {
386                Message n = p.next;
387                if (n != null) {
388                    if (n.target == h && n.what == what
389                        && (object == null || n.obj == object)) {
390                        Message nn = n.next;
391                        n.recycle();
392                        p.next = nn;
393                        continue;
394                    }
395                }
396                p = n;
397            }
398        }
399    }
400
401    final void removeMessages(Handler h, Runnable r, Object object) {
402        if (h == null || r == null) {
403            return;
404        }
405
406        synchronized (this) {
407            Message p = mMessages;
408
409            // Remove all messages at front.
410            while (p != null && p.target == h && p.callback == r
411                   && (object == null || p.obj == object)) {
412                Message n = p.next;
413                mMessages = n;
414                p.recycle();
415                p = n;
416            }
417
418            // Remove all messages after front.
419            while (p != null) {
420                Message n = p.next;
421                if (n != null) {
422                    if (n.target == h && n.callback == r
423                        && (object == null || n.obj == object)) {
424                        Message nn = n.next;
425                        n.recycle();
426                        p.next = nn;
427                        continue;
428                    }
429                }
430                p = n;
431            }
432        }
433    }
434
435    final void removeCallbacksAndMessages(Handler h, Object object) {
436        if (h == null) {
437            return;
438        }
439
440        synchronized (this) {
441            Message p = mMessages;
442
443            // Remove all messages at front.
444            while (p != null && p.target == h
445                    && (object == null || p.obj == object)) {
446                Message n = p.next;
447                mMessages = n;
448                p.recycle();
449                p = n;
450            }
451
452            // Remove all messages after front.
453            while (p != null) {
454                Message n = p.next;
455                if (n != null) {
456                    if (n.target == h && (object == null || n.obj == object)) {
457                        Message nn = n.next;
458                        n.recycle();
459                        p.next = nn;
460                        continue;
461                    }
462                }
463                p = n;
464            }
465        }
466    }
467}
468