• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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