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    Message mMessages;
34    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
35    private IdleHandler[] mPendingIdleHandlers;
36    private boolean mQuiting;
37    boolean mQuitAllowed = true;
38
39    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
40    private boolean mBlocked;
41
42    @SuppressWarnings("unused")
43    private int mPtr; // used by native code
44
45    private native void nativeInit();
46    private native void nativeDestroy();
47    private native void nativePollOnce(int ptr, int timeoutMillis);
48    private native void nativeWake(int ptr);
49
50    /**
51     * Callback interface for discovering when a thread is going to block
52     * waiting for more messages.
53     */
54    public static interface IdleHandler {
55        /**
56         * Called when the message queue has run out of messages and will now
57         * wait for more.  Return true to keep your idle handler active, false
58         * to have it removed.  This may be called if there are still messages
59         * pending in the queue, but they are all scheduled to be dispatched
60         * after the current time.
61         */
62        boolean queueIdle();
63    }
64
65    /**
66     * Add a new {@link IdleHandler} to this message queue.  This may be
67     * removed automatically for you by returning false from
68     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
69     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
70     *
71     * <p>This method is safe to call from any thread.
72     *
73     * @param handler The IdleHandler to be added.
74     */
75    public final void addIdleHandler(IdleHandler handler) {
76        if (handler == null) {
77            throw new NullPointerException("Can't add a null IdleHandler");
78        }
79        synchronized (this) {
80            mIdleHandlers.add(handler);
81        }
82    }
83
84    /**
85     * Remove an {@link IdleHandler} from the queue that was previously added
86     * with {@link #addIdleHandler}.  If the given object is not currently
87     * in the idle list, nothing is done.
88     *
89     * @param handler The IdleHandler to be removed.
90     */
91    public final void removeIdleHandler(IdleHandler handler) {
92        synchronized (this) {
93            mIdleHandlers.remove(handler);
94        }
95    }
96
97    MessageQueue() {
98        nativeInit();
99    }
100
101    @Override
102    protected void finalize() throws Throwable {
103        try {
104            nativeDestroy();
105        } finally {
106            super.finalize();
107        }
108    }
109
110    final Message next() {
111        int pendingIdleHandlerCount = -1; // -1 only during first iteration
112        int nextPollTimeoutMillis = 0;
113
114        for (;;) {
115            if (nextPollTimeoutMillis != 0) {
116                Binder.flushPendingCommands();
117            }
118            nativePollOnce(mPtr, nextPollTimeoutMillis);
119
120            synchronized (this) {
121                // Try to retrieve the next message.  Return if found.
122                final long now = SystemClock.uptimeMillis();
123                final Message msg = mMessages;
124                if (msg != null) {
125                    final long when = msg.when;
126                    if (now >= when) {
127                        mBlocked = false;
128                        mMessages = msg.next;
129                        msg.next = null;
130                        if (false) Log.v("MessageQueue", "Returning message: " + msg);
131                        msg.markInUse();
132                        return msg;
133                    } else {
134                        nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE);
135                    }
136                } else {
137                    nextPollTimeoutMillis = -1;
138                }
139
140                // If first time, then get the number of idlers to run.
141                if (pendingIdleHandlerCount < 0) {
142                    pendingIdleHandlerCount = mIdleHandlers.size();
143                }
144                if (pendingIdleHandlerCount == 0) {
145                    // No idle handlers to run.  Loop and wait some more.
146                    mBlocked = true;
147                    continue;
148                }
149
150                if (mPendingIdleHandlers == null) {
151                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
152                }
153                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
154            }
155
156            // Run the idle handlers.
157            // We only ever reach this code block during the first iteration.
158            for (int i = 0; i < pendingIdleHandlerCount; i++) {
159                final IdleHandler idler = mPendingIdleHandlers[i];
160                mPendingIdleHandlers[i] = null; // release the reference to the handler
161
162                boolean keep = false;
163                try {
164                    keep = idler.queueIdle();
165                } catch (Throwable t) {
166                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
167                }
168
169                if (!keep) {
170                    synchronized (this) {
171                        mIdleHandlers.remove(idler);
172                    }
173                }
174            }
175
176            // Reset the idle handler count to 0 so we do not run them again.
177            pendingIdleHandlerCount = 0;
178
179            // While calling an idle handler, a new message could have been delivered
180            // so go back and look again for a pending message without waiting.
181            nextPollTimeoutMillis = 0;
182        }
183    }
184
185    final boolean enqueueMessage(Message msg, long when) {
186        if (msg.isInUse()) {
187            throw new AndroidRuntimeException(msg
188                    + " This message is already in use.");
189        }
190        if (msg.target == null && !mQuitAllowed) {
191            throw new RuntimeException("Main thread not allowed to quit");
192        }
193        final boolean needWake;
194        synchronized (this) {
195            if (mQuiting) {
196                RuntimeException e = new RuntimeException(
197                    msg.target + " sending message to a Handler on a dead thread");
198                Log.w("MessageQueue", e.getMessage(), e);
199                return false;
200            } else if (msg.target == null) {
201                mQuiting = true;
202            }
203
204            msg.when = when;
205            //Log.d("MessageQueue", "Enqueing: " + msg);
206            Message p = mMessages;
207            if (p == null || when == 0 || when < p.when) {
208                msg.next = p;
209                mMessages = msg;
210                needWake = mBlocked; // new head, might need to wake up
211            } else {
212                Message prev = null;
213                while (p != null && p.when <= when) {
214                    prev = p;
215                    p = p.next;
216                }
217                msg.next = prev.next;
218                prev.next = msg;
219                needWake = false; // still waiting on head, no need to wake up
220            }
221        }
222        if (needWake) {
223            nativeWake(mPtr);
224        }
225        return true;
226    }
227
228    final boolean removeMessages(Handler h, int what, Object object,
229            boolean doRemove) {
230        synchronized (this) {
231            Message p = mMessages;
232            boolean found = false;
233
234            // Remove all messages at front.
235            while (p != null && p.target == h && p.what == what
236                   && (object == null || p.obj == object)) {
237                if (!doRemove) return true;
238                found = true;
239                Message n = p.next;
240                mMessages = n;
241                p.recycle();
242                p = n;
243            }
244
245            // Remove all messages after front.
246            while (p != null) {
247                Message n = p.next;
248                if (n != null) {
249                    if (n.target == h && n.what == what
250                        && (object == null || n.obj == object)) {
251                        if (!doRemove) return true;
252                        found = true;
253                        Message nn = n.next;
254                        n.recycle();
255                        p.next = nn;
256                        continue;
257                    }
258                }
259                p = n;
260            }
261
262            return found;
263        }
264    }
265
266    final void removeMessages(Handler h, Runnable r, Object object) {
267        if (r == null) {
268            return;
269        }
270
271        synchronized (this) {
272            Message p = mMessages;
273
274            // Remove all messages at front.
275            while (p != null && p.target == h && p.callback == r
276                   && (object == null || p.obj == object)) {
277                Message n = p.next;
278                mMessages = n;
279                p.recycle();
280                p = n;
281            }
282
283            // Remove all messages after front.
284            while (p != null) {
285                Message n = p.next;
286                if (n != null) {
287                    if (n.target == h && n.callback == r
288                        && (object == null || n.obj == object)) {
289                        Message nn = n.next;
290                        n.recycle();
291                        p.next = nn;
292                        continue;
293                    }
294                }
295                p = n;
296            }
297        }
298    }
299
300    final void removeCallbacksAndMessages(Handler h, Object object) {
301        synchronized (this) {
302            Message p = mMessages;
303
304            // Remove all messages at front.
305            while (p != null && p.target == h
306                    && (object == null || p.obj == object)) {
307                Message n = p.next;
308                mMessages = n;
309                p.recycle();
310                p = n;
311            }
312
313            // Remove all messages after front.
314            while (p != null) {
315                Message n = p.next;
316                if (n != null) {
317                    if (n.target == h && (object == null || n.obj == object)) {
318                        Message nn = n.next;
319                        n.recycle();
320                        p.next = nn;
321                        continue;
322                    }
323                }
324                p = n;
325            }
326        }
327    }
328
329    /*
330    private void dumpQueue_l()
331    {
332        Message p = mMessages;
333        System.out.println(this + "  queue is:");
334        while (p != null) {
335            System.out.println("            " + p);
336            p = p.next;
337        }
338    }
339    */
340}
341