• 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 /**
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