1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.base.test.util; 6 7 import android.os.Handler; 8 import android.os.Looper; 9 import android.os.Message; 10 import android.os.MessageQueue; 11 12 import java.lang.reflect.Field; 13 import java.lang.reflect.InvocationTargetException; 14 import java.lang.reflect.Method; 15 16 /** 17 * Test utilities for interacting with the Android Looper. 18 */ 19 public class LooperUtils { 20 private static final Method sNextMethod = getMethod(MessageQueue.class, "next"); 21 private static final Field sMessageTargetField = getField(Message.class, "target"); 22 private static final Field sMessageFlagsField = getField(Message.class, "flags"); 23 getField(Class<?> clazz, String name)24 private static Field getField(Class<?> clazz, String name) { 25 Field f = null; 26 try { 27 f = clazz.getDeclaredField(name); 28 f.setAccessible(true); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 return f; 33 } 34 getMethod(Class<?> clazz, String name)35 private static Method getMethod(Class<?> clazz, String name) { 36 Method m = null; 37 try { 38 m = clazz.getDeclaredMethod(name); 39 m.setAccessible(true); 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } 43 return m; 44 } 45 46 /** 47 * Runs a single nested task on the current Looper. 48 */ runSingleNestedLooperTask()49 public static void runSingleNestedLooperTask() throws IllegalArgumentException, 50 IllegalAccessException, SecurityException, 51 InvocationTargetException { 52 MessageQueue queue = Looper.myQueue(); 53 // This call will block if there are no messages in the queue. It will 54 // also run or more pending C++ tasks as a side effect before returning 55 // |msg|. 56 Message msg = (Message) sNextMethod.invoke(queue); 57 if (msg == null) return; 58 Handler target = (Handler) sMessageTargetField.get(msg); 59 60 if (target != null) target.dispatchMessage(msg); 61 62 // Unset in-use flag. 63 Integer oldFlags = (Integer) sMessageFlagsField.get(msg); 64 sMessageFlagsField.set(msg, oldFlags & ~(1 << 0 /* FLAG_IN_USE */)); 65 66 msg.recycle(); 67 } 68 } 69