blob: 1e8983ee3fe89b58eb4c51d46542a263c95dcf3a [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.util.AndroidRuntimeException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.util.Log;
21
Jeff Brown46b9ac02010-04-22 18:58:52 -070022import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
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 */
Jeff Brown67fc67c2013-04-01 13:00:33 -070032public final class MessageQueue {
Jeff Brown0f85ce32012-02-16 14:41:10 -080033 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039 Message mMessages;
Jeff Brown46b9ac02010-04-22 18:58:52 -070040 private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
Jeff Browned739322010-09-21 15:14:14 -070041 private IdleHandler[] mPendingIdleHandlers;
Jeff Brown415d8c32010-10-05 15:35:37 -070042 private boolean mQuiting;
Christopher Tatefa9e7c02010-05-06 12:07:10 -070043
Jeff Brown415d8c32010-10-05 15:35:37 -070044 // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
45 private boolean mBlocked;
46
Jeff Brown0f85ce32012-02-16 14:41:10 -080047 // The next barrier token.
48 // Barriers are indicated by messages with a null target whose arg1 field carries the token.
49 private int mNextBarrierToken;
Jeff Browne799cb72012-02-14 11:53:33 -080050
Jeff Brown864693462013-01-28 14:25:53 -080051 private native static int nativeInit();
52 private native static void nativeDestroy(int ptr);
53 private native static void nativePollOnce(int ptr, int timeoutMillis);
54 private native static void nativeWake(int ptr);
Dianne Hackbornefa92b22013-05-03 14:11:43 -070055 private native static boolean nativeIsIdling(int ptr);
Jeff Brown46b9ac02010-04-22 18:58:52 -070056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057 /**
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 */
Jeff Brown67fc67c2013-04-01 13:00:33 -070082 public void addIdleHandler(IdleHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 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 */
Jeff Brown67fc67c2013-04-01 13:00:33 -070098 public void removeIdleHandler(IdleHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 synchronized (this) {
100 mIdleHandlers.remove(handler);
101 }
102 }
Jeff Browne799cb72012-02-14 11:53:33 -0800103
Jeff Brown0f85ce32012-02-16 14:41:10 -0800104 MessageQueue(boolean quitAllowed) {
105 mQuitAllowed = quitAllowed;
Jeff Brown864693462013-01-28 14:25:53 -0800106 mPtr = nativeInit();
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700107 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800108
Jeff Brown46b9ac02010-04-22 18:58:52 -0700109 @Override
110 protected void finalize() throws Throwable {
111 try {
Jeff Brown864693462013-01-28 14:25:53 -0800112 dispose();
Jeff Brown46b9ac02010-04-22 18:58:52 -0700113 } finally {
114 super.finalize();
115 }
116 }
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700117
Jeff Brown864693462013-01-28 14:25:53 -0800118 private void dispose() {
119 if (mPtr != 0) {
120 nativeDestroy(mPtr);
121 mPtr = 0;
122 }
123 }
124
Jeff Brown67fc67c2013-04-01 13:00:33 -0700125 Message next() {
Jeff Browned739322010-09-21 15:14:14 -0700126 int pendingIdleHandlerCount = -1; // -1 only during first iteration
127 int nextPollTimeoutMillis = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128
Jeff Browned739322010-09-21 15:14:14 -0700129 for (;;) {
130 if (nextPollTimeoutMillis != 0) {
131 Binder.flushPendingCommands();
132 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700133 nativePollOnce(mPtr, nextPollTimeoutMillis);
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 synchronized (this) {
Jeff Browned739322010-09-21 15:14:14 -0700136 // Try to retrieve the next message. Return if found.
137 final long now = SystemClock.uptimeMillis();
Jeff Browne799cb72012-02-14 11:53:33 -0800138 Message prevMsg = null;
139 Message msg = mMessages;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800140 if (msg != null && msg.target == null) {
141 // Stalled by a barrier. Find the next asynchronous message in the queue.
142 do {
143 prevMsg = msg;
144 msg = msg.next;
145 } while (msg != null && !msg.isAsynchronous());
146 }
147 if (msg != null) {
148 if (now < msg.when) {
Jeff Browne799cb72012-02-14 11:53:33 -0800149 // Next message is not ready. Set a timeout to wake up when it is ready.
Jeff Brown0f85ce32012-02-16 14:41:10 -0800150 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
151 } else {
Jeff Browne799cb72012-02-14 11:53:33 -0800152 // Got a message.
Jeff Brown415d8c32010-10-05 15:35:37 -0700153 mBlocked = false;
Jeff Browne799cb72012-02-14 11:53:33 -0800154 if (prevMsg != null) {
155 prevMsg.next = msg.next;
156 } else {
157 mMessages = msg.next;
158 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700159 msg.next = null;
Joe Onorato43a17652011-04-06 19:22:23 -0700160 if (false) Log.v("MessageQueue", "Returning message: " + msg);
Jeff Brown85c3fe42010-09-21 16:45:16 -0700161 msg.markInUse();
Jeff Browned739322010-09-21 15:14:14 -0700162 return msg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800164 } else {
165 // No more messages.
166 nextPollTimeoutMillis = -1;
Jeff Browned739322010-09-21 15:14:14 -0700167 }
168
Jeff Brown024136f2013-04-11 19:21:32 -0700169 // Process the quit message now that all pending messages have been handled.
170 if (mQuiting) {
171 dispose();
172 return null;
173 }
174
Jeff Browne799cb72012-02-14 11:53:33 -0800175 // If first time idle, then get the number of idlers to run.
Jeff Brown0f85ce32012-02-16 14:41:10 -0800176 // Idle handles only run if the queue is empty or if the first message
177 // in the queue (possibly a barrier) is due to be handled in the future.
178 if (pendingIdleHandlerCount < 0
179 && (mMessages == null || now < mMessages.when)) {
Jeff Browned739322010-09-21 15:14:14 -0700180 pendingIdleHandlerCount = mIdleHandlers.size();
181 }
Jeff Browne799cb72012-02-14 11:53:33 -0800182 if (pendingIdleHandlerCount <= 0) {
Jeff Browned739322010-09-21 15:14:14 -0700183 // No idle handlers to run. Loop and wait some more.
Jeff Brown415d8c32010-10-05 15:35:37 -0700184 mBlocked = true;
Jeff Browned739322010-09-21 15:14:14 -0700185 continue;
186 }
187
188 if (mPendingIdleHandlers == null) {
189 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
190 }
191 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
192 }
193
194 // Run the idle handlers.
195 // We only ever reach this code block during the first iteration.
196 for (int i = 0; i < pendingIdleHandlerCount; i++) {
197 final IdleHandler idler = mPendingIdleHandlers[i];
198 mPendingIdleHandlers[i] = null; // release the reference to the handler
199
200 boolean keep = false;
201 try {
202 keep = idler.queueIdle();
203 } catch (Throwable t) {
204 Log.wtf("MessageQueue", "IdleHandler threw exception", t);
205 }
206
207 if (!keep) {
208 synchronized (this) {
209 mIdleHandlers.remove(idler);
210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213
Jeff Browned739322010-09-21 15:14:14 -0700214 // Reset the idle handler count to 0 so we do not run them again.
215 pendingIdleHandlerCount = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800216
Jeff Browned739322010-09-21 15:14:14 -0700217 // While calling an idle handler, a new message could have been delivered
218 // so go back and look again for a pending message without waiting.
219 nextPollTimeoutMillis = 0;
220 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 }
222
Jeff Brown8b60e452013-04-18 15:17:48 -0700223 void quit(boolean safe) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800224 if (!mQuitAllowed) {
225 throw new RuntimeException("Main thread not allowed to quit.");
226 }
227
228 synchronized (this) {
229 if (mQuiting) {
230 return;
231 }
232 mQuiting = true;
Jeff Brown8b60e452013-04-18 15:17:48 -0700233
234 if (safe) {
235 removeAllFutureMessagesLocked();
236 } else {
237 removeAllMessagesLocked();
238 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800239 }
240 nativeWake(mPtr);
241 }
242
Jeff Brown67fc67c2013-04-01 13:00:33 -0700243 int enqueueSyncBarrier(long when) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800244 // Enqueue a new sync barrier token.
245 // We don't need to wake the queue because the purpose of a barrier is to stall it.
246 synchronized (this) {
247 final int token = mNextBarrierToken++;
248 final Message msg = Message.obtain();
249 msg.arg1 = token;
250
251 Message prev = null;
252 Message p = mMessages;
253 if (when != 0) {
254 while (p != null && p.when <= when) {
255 prev = p;
256 p = p.next;
257 }
258 }
259 if (prev != null) { // invariant: p == prev.next
260 msg.next = p;
261 prev.next = msg;
262 } else {
263 msg.next = p;
264 mMessages = msg;
265 }
266 return token;
267 }
268 }
269
Jeff Brown67fc67c2013-04-01 13:00:33 -0700270 void removeSyncBarrier(int token) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800271 // Remove a sync barrier token from the queue.
272 // If the queue is no longer stalled by a barrier then wake it.
273 final boolean needWake;
274 synchronized (this) {
275 Message prev = null;
276 Message p = mMessages;
277 while (p != null && (p.target != null || p.arg1 != token)) {
278 prev = p;
279 p = p.next;
280 }
281 if (p == null) {
282 throw new IllegalStateException("The specified message queue synchronization "
283 + " barrier token has not been posted or has already been removed.");
284 }
285 if (prev != null) {
286 prev.next = p.next;
287 needWake = false;
288 } else {
289 mMessages = p.next;
290 needWake = mMessages == null || mMessages.target != null;
291 }
292 p.recycle();
293 }
294 if (needWake) {
295 nativeWake(mPtr);
296 }
297 }
298
Jeff Brown67fc67c2013-04-01 13:00:33 -0700299 boolean enqueueMessage(Message msg, long when) {
Wink Savillea334e7c2010-08-24 10:56:30 -0700300 if (msg.isInUse()) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800301 throw new AndroidRuntimeException(msg + " This message is already in use.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800303 if (msg.target == null) {
304 throw new AndroidRuntimeException("Message must have a target.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800306
307 boolean needWake;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 synchronized (this) {
309 if (mQuiting) {
310 RuntimeException e = new RuntimeException(
Jeff Brown0f85ce32012-02-16 14:41:10 -0800311 msg.target + " sending message to a Handler on a dead thread");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 Log.w("MessageQueue", e.getMessage(), e);
313 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315
316 msg.when = when;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 Message p = mMessages;
318 if (p == null || when == 0 || when < p.when) {
Jeff Browne799cb72012-02-14 11:53:33 -0800319 // New head, wake up the event queue if blocked.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 msg.next = p;
321 mMessages = msg;
Jeff Browne799cb72012-02-14 11:53:33 -0800322 needWake = mBlocked;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 } else {
Jeff Browne799cb72012-02-14 11:53:33 -0800324 // Inserted within the middle of the queue. Usually we don't have to wake
Jeff Brown0f85ce32012-02-16 14:41:10 -0800325 // up the event queue unless there is a barrier at the head of the queue
326 // and the message is the earliest asynchronous message in the queue.
327 needWake = mBlocked && p.target == null && msg.isAsynchronous();
328 Message prev;
329 for (;;) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 prev = p;
331 p = p.next;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800332 if (p == null || when < p.when) {
333 break;
334 }
335 if (needWake && p.isAsynchronous()) {
336 needWake = false;
337 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800339 msg.next = p; // invariant: p == prev.next
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 prev.next = msg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 }
342 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700343 if (needWake) {
344 nativeWake(mPtr);
345 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 return true;
347 }
348
Jeff Brown67fc67c2013-04-01 13:00:33 -0700349 boolean hasMessages(Handler h, int what, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800350 if (h == null) {
351 return false;
352 }
353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 synchronized (this) {
355 Message p = mMessages;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800356 while (p != null) {
357 if (p.target == h && p.what == what && (object == null || p.obj == object)) {
358 return true;
359 }
360 p = p.next;
361 }
362 return false;
363 }
364 }
365
Jeff Brown67fc67c2013-04-01 13:00:33 -0700366 boolean hasMessages(Handler h, Runnable r, Object object) {
Romain Guyba6be8a2012-04-23 18:22:09 -0700367 if (h == null) {
368 return false;
369 }
370
371 synchronized (this) {
372 Message p = mMessages;
373 while (p != null) {
374 if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
375 return true;
376 }
377 p = p.next;
378 }
379 return false;
380 }
381 }
382
Dianne Hackbornefa92b22013-05-03 14:11:43 -0700383 boolean isIdling() {
384 return nativeIsIdling(mPtr);
385 }
386
Jeff Brown67fc67c2013-04-01 13:00:33 -0700387 void removeMessages(Handler h, int what, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800388 if (h == null) {
389 return;
390 }
391
392 synchronized (this) {
393 Message p = mMessages;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394
395 // Remove all messages at front.
396 while (p != null && p.target == h && p.what == what
397 && (object == null || p.obj == object)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 Message n = p.next;
399 mMessages = n;
400 p.recycle();
401 p = n;
402 }
403
404 // Remove all messages after front.
405 while (p != null) {
406 Message n = p.next;
407 if (n != null) {
408 if (n.target == h && n.what == what
409 && (object == null || n.obj == object)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 Message nn = n.next;
411 n.recycle();
412 p.next = nn;
413 continue;
414 }
415 }
416 p = n;
417 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 }
419 }
420
Jeff Brown67fc67c2013-04-01 13:00:33 -0700421 void removeMessages(Handler h, Runnable r, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800422 if (h == null || r == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 return;
424 }
425
426 synchronized (this) {
427 Message p = mMessages;
428
429 // Remove all messages at front.
430 while (p != null && p.target == h && p.callback == r
431 && (object == null || p.obj == object)) {
432 Message n = p.next;
433 mMessages = n;
434 p.recycle();
435 p = n;
436 }
437
438 // Remove all messages after front.
439 while (p != null) {
440 Message n = p.next;
441 if (n != null) {
442 if (n.target == h && n.callback == r
443 && (object == null || n.obj == object)) {
444 Message nn = n.next;
445 n.recycle();
446 p.next = nn;
447 continue;
448 }
449 }
450 p = n;
451 }
452 }
453 }
454
Jeff Brown67fc67c2013-04-01 13:00:33 -0700455 void removeCallbacksAndMessages(Handler h, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800456 if (h == null) {
457 return;
458 }
459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 synchronized (this) {
461 Message p = mMessages;
462
463 // Remove all messages at front.
464 while (p != null && p.target == h
465 && (object == null || p.obj == object)) {
466 Message n = p.next;
467 mMessages = n;
468 p.recycle();
469 p = n;
470 }
471
472 // Remove all messages after front.
473 while (p != null) {
474 Message n = p.next;
475 if (n != null) {
476 if (n.target == h && (object == null || n.obj == object)) {
477 Message nn = n.next;
478 n.recycle();
479 p.next = nn;
480 continue;
481 }
482 }
483 p = n;
484 }
485 }
486 }
Jeff Brown8b60e452013-04-18 15:17:48 -0700487
488 private void removeAllMessagesLocked() {
489 Message p = mMessages;
490 while (p != null) {
491 Message n = p.next;
492 p.recycle();
493 p = n;
494 }
495 mMessages = null;
496 }
497
498 private void removeAllFutureMessagesLocked() {
499 final long now = SystemClock.uptimeMillis();
500 Message p = mMessages;
501 if (p != null) {
502 if (p.when > now) {
503 removeAllMessagesLocked();
504 } else {
505 Message n;
506 for (;;) {
507 n = p.next;
508 if (n == null) {
509 return;
510 }
511 if (n.when > now) {
512 break;
513 }
514 p = n;
515 }
516 p.next = null;
517 do {
518 p = n;
519 n = p.next;
520 p.recycle();
521 } while (n != null);
522 }
523 }
524 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800525}