// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.content.browser.test; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.MessageQueue; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Handles processing messages in nested run loops. * * Android does not support nested message loops by default. While running * in nested mode, we use reflection to retreive messages from the MessageQueue * and dispatch them. */ @JNINamespace("content") class NestedSystemMessageHandler { // See org.chromium.base.SystemMessageHandler for more message ids. // The id here should not conflict with the ones in SystemMessageHandler. private static final int QUIT_MESSAGE = 10; private static final Handler mHandler = new Handler(); private NestedSystemMessageHandler() { } /** * Processes messages from the current MessageQueue till the queue becomes idle. */ @SuppressWarnings("unused") @CalledByNative private boolean runNestedLoopTillIdle() { boolean quitLoop = false; MessageQueue queue = Looper.myQueue(); queue.addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { mHandler.sendMessage(mHandler.obtainMessage(QUIT_MESSAGE)); return false; } }); Class<?> messageQueueClazz = queue.getClass(); Method nextMethod = null; try { nextMethod = messageQueueClazz.getDeclaredMethod("next"); } catch (SecurityException e) { e.printStackTrace(); return false; } catch (NoSuchMethodException e) { e.printStackTrace(); return false; } nextMethod.setAccessible(true); while (!quitLoop) { Message msg = null; try { msg = (Message) nextMethod.invoke(queue); } catch (IllegalArgumentException e) { e.printStackTrace(); return false; } catch (IllegalAccessException e) { e.printStackTrace(); return false; } catch (InvocationTargetException e) { e.printStackTrace(); return false; } if (msg != null) { if (msg.what == QUIT_MESSAGE) { quitLoop = true; } Class messageClazz = msg.getClass(); Field targetFiled = null; try { targetFiled = messageClazz.getDeclaredField("target"); } catch (SecurityException e) { e.printStackTrace(); return false; } catch (NoSuchFieldException e) { e.printStackTrace(); return false; } targetFiled.setAccessible(true); Handler target = null; try { target = (Handler) targetFiled.get(msg); } catch (IllegalArgumentException e) { e.printStackTrace(); return false; } catch (IllegalAccessException e) { e.printStackTrace(); return false; } if (target == null) { // No target is a magic identifier for the quit message. quitLoop = true; } else { target.dispatchMessage(msg); } msg.recycle(); } else { quitLoop = true; } } return true; } @SuppressWarnings("unused") @CalledByNative private static NestedSystemMessageHandler create() { return new NestedSystemMessageHandler(); } }