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 /** Test utilities for interacting with the Android Looper. */ 17 public class LooperUtils { 18 private static final Method sNextMethod = getMethod(MessageQueue.class, "next"); 19 private static final Field sMessageTargetField = getField(Message.class, "target"); 20 private static final Field sMessageFlagsField = getField(Message.class, "flags"); 21 getField(Class<?> clazz, String name)22 private static Field getField(Class<?> clazz, String name) { 23 Field f = null; 24 try { 25 f = clazz.getDeclaredField(name); 26 f.setAccessible(true); 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 return f; 31 } 32 getMethod(Class<?> clazz, String name)33 private static Method getMethod(Class<?> clazz, String name) { 34 Method m = null; 35 try { 36 m = clazz.getDeclaredMethod(name); 37 m.setAccessible(true); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 return m; 42 } 43 44 /** Runs a single nested task on the current Looper. */ runSingleNestedLooperTask()45 public static void runSingleNestedLooperTask() 46 throws IllegalArgumentException, 47 IllegalAccessException, 48 SecurityException, 49 InvocationTargetException { 50 MessageQueue queue = Looper.myQueue(); 51 // This call will block if there are no messages in the queue. It will 52 // also run or more pending C++ tasks as a side effect before returning 53 // |msg|. 54 Message msg = (Message) sNextMethod.invoke(queue); 55 if (msg == null) return; 56 Handler target = (Handler) sMessageTargetField.get(msg); 57 58 if (target != null) target.dispatchMessage(msg); 59 60 // Unset in-use flag. 61 Integer oldFlags = (Integer) sMessageFlagsField.get(msg); 62 sMessageFlagsField.set(msg, oldFlags & ~(1 << 0 /* FLAG_IN_USE */)); 63 64 msg.recycle(); 65 } 66 } 67