blob: bf7e5caf7024c73228522c0fb32dba034ee1c780 [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);
Jeff Brown46b9ac02010-04-22 18:58:52 -070055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 /**
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 */
Jeff Brown67fc67c2013-04-01 13:00:33 -070081 public void addIdleHandler(IdleHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 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 */
Jeff Brown67fc67c2013-04-01 13:00:33 -070097 public void removeIdleHandler(IdleHandler handler) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 synchronized (this) {
99 mIdleHandlers.remove(handler);
100 }
101 }
Jeff Browne799cb72012-02-14 11:53:33 -0800102
Jeff Brown0f85ce32012-02-16 14:41:10 -0800103 MessageQueue(boolean quitAllowed) {
104 mQuitAllowed = quitAllowed;
Jeff Brown864693462013-01-28 14:25:53 -0800105 mPtr = nativeInit();
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700106 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800107
Jeff Brown46b9ac02010-04-22 18:58:52 -0700108 @Override
109 protected void finalize() throws Throwable {
110 try {
Jeff Brown864693462013-01-28 14:25:53 -0800111 dispose();
Jeff Brown46b9ac02010-04-22 18:58:52 -0700112 } finally {
113 super.finalize();
114 }
115 }
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700116
Jeff Brown864693462013-01-28 14:25:53 -0800117 private void dispose() {
118 if (mPtr != 0) {
119 nativeDestroy(mPtr);
120 mPtr = 0;
121 }
122 }
123
Jeff Brown67fc67c2013-04-01 13:00:33 -0700124 Message next() {
Jeff Browned739322010-09-21 15:14:14 -0700125 int pendingIdleHandlerCount = -1; // -1 only during first iteration
126 int nextPollTimeoutMillis = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
Jeff Browned739322010-09-21 15:14:14 -0700128 for (;;) {
129 if (nextPollTimeoutMillis != 0) {
130 Binder.flushPendingCommands();
131 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700132 nativePollOnce(mPtr, nextPollTimeoutMillis);
Christopher Tatefa9e7c02010-05-06 12:07:10 -0700133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 synchronized (this) {
Jeff Browned739322010-09-21 15:14:14 -0700135 // Try to retrieve the next message. Return if found.
136 final long now = SystemClock.uptimeMillis();
Jeff Browne799cb72012-02-14 11:53:33 -0800137 Message prevMsg = null;
138 Message msg = mMessages;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800139 if (msg != null && msg.target == null) {
140 // Stalled by a barrier. Find the next asynchronous message in the queue.
141 do {
142 prevMsg = msg;
143 msg = msg.next;
144 } while (msg != null && !msg.isAsynchronous());
145 }
146 if (msg != null) {
147 if (now < msg.when) {
Jeff Browne799cb72012-02-14 11:53:33 -0800148 // Next message is not ready. Set a timeout to wake up when it is ready.
Jeff Brown0f85ce32012-02-16 14:41:10 -0800149 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
150 } else {
Jeff Browne799cb72012-02-14 11:53:33 -0800151 // Got a message.
Jeff Brown415d8c32010-10-05 15:35:37 -0700152 mBlocked = false;
Jeff Browne799cb72012-02-14 11:53:33 -0800153 if (prevMsg != null) {
154 prevMsg.next = msg.next;
155 } else {
156 mMessages = msg.next;
157 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700158 msg.next = null;
Joe Onorato43a17652011-04-06 19:22:23 -0700159 if (false) Log.v("MessageQueue", "Returning message: " + msg);
Jeff Brown85c3fe42010-09-21 16:45:16 -0700160 msg.markInUse();
Jeff Browned739322010-09-21 15:14:14 -0700161 return msg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800163 } else {
164 // No more messages.
165 nextPollTimeoutMillis = -1;
Jeff Browned739322010-09-21 15:14:14 -0700166 }
167
Jeff Brown024136f2013-04-11 19:21:32 -0700168 // Process the quit message now that all pending messages have been handled.
169 if (mQuiting) {
170 dispose();
171 return null;
172 }
173
Jeff Browne799cb72012-02-14 11:53:33 -0800174 // If first time idle, then get the number of idlers to run.
Jeff Brown0f85ce32012-02-16 14:41:10 -0800175 // Idle handles only run if the queue is empty or if the first message
176 // in the queue (possibly a barrier) is due to be handled in the future.
177 if (pendingIdleHandlerCount < 0
178 && (mMessages == null || now < mMessages.when)) {
Jeff Browned739322010-09-21 15:14:14 -0700179 pendingIdleHandlerCount = mIdleHandlers.size();
180 }
Jeff Browne799cb72012-02-14 11:53:33 -0800181 if (pendingIdleHandlerCount <= 0) {
Jeff Browned739322010-09-21 15:14:14 -0700182 // No idle handlers to run. Loop and wait some more.
Jeff Brown415d8c32010-10-05 15:35:37 -0700183 mBlocked = true;
Jeff Browned739322010-09-21 15:14:14 -0700184 continue;
185 }
186
187 if (mPendingIdleHandlers == null) {
188 mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
189 }
190 mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
191 }
192
193 // Run the idle handlers.
194 // We only ever reach this code block during the first iteration.
195 for (int i = 0; i < pendingIdleHandlerCount; i++) {
196 final IdleHandler idler = mPendingIdleHandlers[i];
197 mPendingIdleHandlers[i] = null; // release the reference to the handler
198
199 boolean keep = false;
200 try {
201 keep = idler.queueIdle();
202 } catch (Throwable t) {
203 Log.wtf("MessageQueue", "IdleHandler threw exception", t);
204 }
205
206 if (!keep) {
207 synchronized (this) {
208 mIdleHandlers.remove(idler);
209 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212
Jeff Browned739322010-09-21 15:14:14 -0700213 // Reset the idle handler count to 0 so we do not run them again.
214 pendingIdleHandlerCount = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215
Jeff Browned739322010-09-21 15:14:14 -0700216 // While calling an idle handler, a new message could have been delivered
217 // so go back and look again for a pending message without waiting.
218 nextPollTimeoutMillis = 0;
219 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 }
221
Jeff Brown8b60e452013-04-18 15:17:48 -0700222 void quit(boolean safe) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800223 if (!mQuitAllowed) {
224 throw new RuntimeException("Main thread not allowed to quit.");
225 }
226
227 synchronized (this) {
228 if (mQuiting) {
229 return;
230 }
231 mQuiting = true;
Jeff Brown8b60e452013-04-18 15:17:48 -0700232
233 if (safe) {
234 removeAllFutureMessagesLocked();
235 } else {
236 removeAllMessagesLocked();
237 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800238 }
239 nativeWake(mPtr);
240 }
241
Jeff Brown67fc67c2013-04-01 13:00:33 -0700242 int enqueueSyncBarrier(long when) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800243 // Enqueue a new sync barrier token.
244 // We don't need to wake the queue because the purpose of a barrier is to stall it.
245 synchronized (this) {
246 final int token = mNextBarrierToken++;
247 final Message msg = Message.obtain();
248 msg.arg1 = token;
249
250 Message prev = null;
251 Message p = mMessages;
252 if (when != 0) {
253 while (p != null && p.when <= when) {
254 prev = p;
255 p = p.next;
256 }
257 }
258 if (prev != null) { // invariant: p == prev.next
259 msg.next = p;
260 prev.next = msg;
261 } else {
262 msg.next = p;
263 mMessages = msg;
264 }
265 return token;
266 }
267 }
268
Jeff Brown67fc67c2013-04-01 13:00:33 -0700269 void removeSyncBarrier(int token) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800270 // Remove a sync barrier token from the queue.
271 // If the queue is no longer stalled by a barrier then wake it.
272 final boolean needWake;
273 synchronized (this) {
274 Message prev = null;
275 Message p = mMessages;
276 while (p != null && (p.target != null || p.arg1 != token)) {
277 prev = p;
278 p = p.next;
279 }
280 if (p == null) {
281 throw new IllegalStateException("The specified message queue synchronization "
282 + " barrier token has not been posted or has already been removed.");
283 }
284 if (prev != null) {
285 prev.next = p.next;
286 needWake = false;
287 } else {
288 mMessages = p.next;
289 needWake = mMessages == null || mMessages.target != null;
290 }
291 p.recycle();
292 }
293 if (needWake) {
294 nativeWake(mPtr);
295 }
296 }
297
Jeff Brown67fc67c2013-04-01 13:00:33 -0700298 boolean enqueueMessage(Message msg, long when) {
Wink Savillea334e7c2010-08-24 10:56:30 -0700299 if (msg.isInUse()) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800300 throw new AndroidRuntimeException(msg + " This message is already in use.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800301 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800302 if (msg.target == null) {
303 throw new AndroidRuntimeException("Message must have a target.");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800305
306 boolean needWake;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 synchronized (this) {
308 if (mQuiting) {
309 RuntimeException e = new RuntimeException(
Jeff Brown0f85ce32012-02-16 14:41:10 -0800310 msg.target + " sending message to a Handler on a dead thread");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 Log.w("MessageQueue", e.getMessage(), e);
312 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 }
314
315 msg.when = when;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 Message p = mMessages;
317 if (p == null || when == 0 || when < p.when) {
Jeff Browne799cb72012-02-14 11:53:33 -0800318 // New head, wake up the event queue if blocked.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 msg.next = p;
320 mMessages = msg;
Jeff Browne799cb72012-02-14 11:53:33 -0800321 needWake = mBlocked;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 } else {
Jeff Browne799cb72012-02-14 11:53:33 -0800323 // Inserted within the middle of the queue. Usually we don't have to wake
Jeff Brown0f85ce32012-02-16 14:41:10 -0800324 // up the event queue unless there is a barrier at the head of the queue
325 // and the message is the earliest asynchronous message in the queue.
326 needWake = mBlocked && p.target == null && msg.isAsynchronous();
327 Message prev;
328 for (;;) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 prev = p;
330 p = p.next;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800331 if (p == null || when < p.when) {
332 break;
333 }
334 if (needWake && p.isAsynchronous()) {
335 needWake = false;
336 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 }
Jeff Brown0f85ce32012-02-16 14:41:10 -0800338 msg.next = p; // invariant: p == prev.next
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 prev.next = msg;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 }
341 }
Jeff Brown415d8c32010-10-05 15:35:37 -0700342 if (needWake) {
343 nativeWake(mPtr);
344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 return true;
346 }
347
Jeff Brown67fc67c2013-04-01 13:00:33 -0700348 boolean hasMessages(Handler h, int what, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800349 if (h == null) {
350 return false;
351 }
352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 synchronized (this) {
354 Message p = mMessages;
Jeff Brown0f85ce32012-02-16 14:41:10 -0800355 while (p != null) {
356 if (p.target == h && p.what == what && (object == null || p.obj == object)) {
357 return true;
358 }
359 p = p.next;
360 }
361 return false;
362 }
363 }
364
Jeff Brown67fc67c2013-04-01 13:00:33 -0700365 boolean hasMessages(Handler h, Runnable r, Object object) {
Romain Guyba6be8a2012-04-23 18:22:09 -0700366 if (h == null) {
367 return false;
368 }
369
370 synchronized (this) {
371 Message p = mMessages;
372 while (p != null) {
373 if (p.target == h && p.callback == r && (object == null || p.obj == object)) {
374 return true;
375 }
376 p = p.next;
377 }
378 return false;
379 }
380 }
381
Jeff Brown67fc67c2013-04-01 13:00:33 -0700382 void removeMessages(Handler h, int what, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800383 if (h == null) {
384 return;
385 }
386
387 synchronized (this) {
388 Message p = mMessages;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
390 // Remove all messages at front.
391 while (p != null && p.target == h && p.what == what
392 && (object == null || p.obj == object)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800393 Message n = p.next;
394 mMessages = n;
395 p.recycle();
396 p = n;
397 }
398
399 // Remove all messages after front.
400 while (p != null) {
401 Message n = p.next;
402 if (n != null) {
403 if (n.target == h && n.what == what
404 && (object == null || n.obj == object)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 Message nn = n.next;
406 n.recycle();
407 p.next = nn;
408 continue;
409 }
410 }
411 p = n;
412 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 }
414 }
415
Jeff Brown67fc67c2013-04-01 13:00:33 -0700416 void removeMessages(Handler h, Runnable r, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800417 if (h == null || r == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 return;
419 }
420
421 synchronized (this) {
422 Message p = mMessages;
423
424 // Remove all messages at front.
425 while (p != null && p.target == h && p.callback == r
426 && (object == null || p.obj == object)) {
427 Message n = p.next;
428 mMessages = n;
429 p.recycle();
430 p = n;
431 }
432
433 // Remove all messages after front.
434 while (p != null) {
435 Message n = p.next;
436 if (n != null) {
437 if (n.target == h && n.callback == r
438 && (object == null || n.obj == object)) {
439 Message nn = n.next;
440 n.recycle();
441 p.next = nn;
442 continue;
443 }
444 }
445 p = n;
446 }
447 }
448 }
449
Jeff Brown67fc67c2013-04-01 13:00:33 -0700450 void removeCallbacksAndMessages(Handler h, Object object) {
Jeff Brown0f85ce32012-02-16 14:41:10 -0800451 if (h == null) {
452 return;
453 }
454
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 synchronized (this) {
456 Message p = mMessages;
457
458 // Remove all messages at front.
459 while (p != null && p.target == h
460 && (object == null || p.obj == object)) {
461 Message n = p.next;
462 mMessages = n;
463 p.recycle();
464 p = n;
465 }
466
467 // Remove all messages after front.
468 while (p != null) {
469 Message n = p.next;
470 if (n != null) {
471 if (n.target == h && (object == null || n.obj == object)) {
472 Message nn = n.next;
473 n.recycle();
474 p.next = nn;
475 continue;
476 }
477 }
478 p = n;
479 }
480 }
481 }
Jeff Brown8b60e452013-04-18 15:17:48 -0700482
483 private void removeAllMessagesLocked() {
484 Message p = mMessages;
485 while (p != null) {
486 Message n = p.next;
487 p.recycle();
488 p = n;
489 }
490 mMessages = null;
491 }
492
493 private void removeAllFutureMessagesLocked() {
494 final long now = SystemClock.uptimeMillis();
495 Message p = mMessages;
496 if (p != null) {
497 if (p.when > now) {
498 removeAllMessagesLocked();
499 } else {
500 Message n;
501 for (;;) {
502 n = p.next;
503 if (n == null) {
504 return;
505 }
506 if (n.when > now) {
507 break;
508 }
509 p = n;
510 }
511 p.next = null;
512 do {
513 p = n;
514 n = p.next;
515 p.recycle();
516 } while (n != null);
517 }
518 }
519 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520}