NestedSystemMessageHandler.java revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.content.browser.test;
6
7import android.os.Handler;
8import android.os.Looper;
9import android.os.Message;
10import android.os.MessageQueue;
11
12import org.chromium.base.CalledByNative;
13import org.chromium.base.JNINamespace;
14
15import java.lang.reflect.Field;
16import java.lang.reflect.InvocationTargetException;
17import java.lang.reflect.Method;
18
19/**
20 * Handles processing messages in nested run loops.
21 *
22 * Android does not support nested message loops by default. While running
23 * in nested mode, we use reflection to retreive messages from the MessageQueue
24 * and dispatch them.
25 */
26@JNINamespace("content")
27class NestedSystemMessageHandler {
28    // See org.chromium.base.SystemMessageHandler for more message ids.
29    // The id here should not conflict with the ones in SystemMessageHandler.
30    private static final int QUIT_MESSAGE = 10;
31    private static final Handler mHandler = new Handler();
32
33    private NestedSystemMessageHandler() {
34    }
35
36    /**
37     * Processes messages from the current MessageQueue till the queue becomes idle.
38     */
39    @SuppressWarnings("unused")
40    @CalledByNative
41    private boolean runNestedLoopTillIdle() {
42        boolean quitLoop = false;
43
44        MessageQueue queue = Looper.myQueue();
45        queue.addIdleHandler(new MessageQueue.IdleHandler() {
46            @Override
47            public boolean queueIdle() {
48                mHandler.sendMessage(mHandler.obtainMessage(QUIT_MESSAGE));
49                return false;
50            }
51        });
52
53        Class<?> messageQueueClazz = queue.getClass();
54        Method nextMethod = null;
55        try {
56            nextMethod = messageQueueClazz.getDeclaredMethod("next");
57        } catch (SecurityException e) {
58            e.printStackTrace();
59            return false;
60        } catch (NoSuchMethodException e) {
61            e.printStackTrace();
62            return false;
63        }
64        nextMethod.setAccessible(true);
65
66        while (!quitLoop) {
67            Message msg = null;
68            try {
69                msg = (Message) nextMethod.invoke(queue);
70            } catch (IllegalArgumentException e) {
71                e.printStackTrace();
72                return false;
73            } catch (IllegalAccessException e) {
74                e.printStackTrace();
75                return false;
76            } catch (InvocationTargetException e) {
77                e.printStackTrace();
78                return false;
79            }
80
81            if (msg != null) {
82                if (msg.what == QUIT_MESSAGE) {
83                    quitLoop = true;
84                }
85                Class messageClazz = msg.getClass();
86                Field targetFiled = null;
87                try {
88                    targetFiled = messageClazz.getDeclaredField("target");
89                } catch (SecurityException e) {
90                    e.printStackTrace();
91                    return false;
92                } catch (NoSuchFieldException e) {
93                    e.printStackTrace();
94                    return false;
95                }
96                targetFiled.setAccessible(true);
97
98                Handler target = null;
99                try {
100                    target = (Handler) targetFiled.get(msg);
101                } catch (IllegalArgumentException e) {
102                    e.printStackTrace();
103                    return false;
104                } catch (IllegalAccessException e) {
105                    e.printStackTrace();
106                    return false;
107                }
108
109                if (target == null) {
110                    // No target is a magic identifier for the quit message.
111                    quitLoop = true;
112                } else {
113                    target.dispatchMessage(msg);
114                }
115                msg.recycle();
116            } else {
117                quitLoop = true;
118            }
119        }
120        return true;
121    }
122
123    @SuppressWarnings("unused")
124    @CalledByNative
125    private static NestedSystemMessageHandler create() {
126        return new NestedSystemMessageHandler();
127    }
128}
129