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.Config;
21import android.util.Log;
22
23import java.util.ArrayList;
24
25/**
26 * Low-level class holding the list of messages to be dispatched by a
27 * {@link Looper}.  Messages are not added directly to a MessageQueue,
28 * but rather through {@link Handler} objects associated with the Looper.
29 *
30 * <p>You can retrieve the MessageQueue for the current thread with
31 * {@link Looper#myQueue() Looper.myQueue()}.
32 */
33public class MessageQueue {
34    Message mMessages;
35    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
36    private IdleHandler[] mPendingIdleHandlers;
37    private boolean mQuiting;
38    boolean mQuitAllowed = true;
39
40    // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
41    private boolean mBlocked;
42
43    @SuppressWarnings("unused")
44    private int mPtr; // used by native code
45
46    private native void nativeInit();
47    private native void nativeDestroy();
48    private native void nativePollOnce(int ptr, int timeoutMillis);
49    private native void nativeWake(int ptr);
50
51    /**
52     * Callback interface for discovering when a thread is going to block
53     * waiting for more messages.
54     */
55    public static interface IdleHandler {
56        /**
57         * Called when the message queue has run out of messages and will now
58         * wait for more.  Return true to keep your idle handler active, false
59         * to have it removed.  This may be called if there are still messages
60         * pending in the queue, but they are all scheduled to be dispatched
61         * after the current time.
62         */
63        boolean queueIdle();
64    }
65
66    /**
67     * Add a new {@link IdleHandler} to this message queue.  This may be
68     * removed automatically for you by returning false from
69     * {@link IdleHandler#queueIdle IdleHandler.queueIdle()} when it is
70     * invoked, or explicitly removing it with {@link #removeIdleHandler}.
71     *
72     * <p>This method is safe to call from any thread.
73     *
74     * @param handler The IdleHandler to be added.
75     */
76    public final void addIdleHandler(IdleHandler handler) {
77        if (handler == null) {
78            throw new NullPointerException("Can't add a null IdleHandler");
79        }
80        synchronized (this) {
81            mIdleHandlers.add(handler);
82        }
83    }
84
85    /**
86     * Remove an {@link IdleHandler} from the queue that was previously added
87     * with {@link #addIdleHandler}.  If the given object is not currently
88     * in the idle list, nothing is done.
89     *
90     * @param handler The IdleHandler to be removed.
91     */
92    public final void removeIdleHandler(IdleHandler handler) {
93        synchronized (this) {
94            mIdleHandlers.remove(handler);
95        }
96    }
97
98    MessageQueue() {
99        nativeInit();
100    }
101
102    @Override
103    protected void finalize() throws Throwable {
104        try {
105            nativeDestroy();
106        } finally {
107            super.finalize();
108        }
109    }
110
111    final Message next() {
112        int pendingIdleHandlerCount = -1; // -1 only during first iteration
113        int nextPollTimeoutMillis = 0;
114
115        for (;;) {
116            if (nextPollTimeoutMillis != 0) {
117                Binder.flushPendingCommands();
118            }
119            nativePollOnce(mPtr, nextPollTimeoutMillis);
120
121            synchronized (this) {
122                // Try to retrieve the next message.  Return if found.
123                final long now = SystemClock.uptimeMillis();
124                final Message msg = mMessages;
125                if (msg != null) {
126                    final long when = msg.when;
127                    if (now >= when) {
128                        mBlocked = false;
129                        mMessages = msg.next;
130                        msg.next = null;
131                        if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg);
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.when != 0) {
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