Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9aaa4bc3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Revert "Remove MessageQueue reflection from TestLooper"" into main

parents 209a782c 317ab7ef
Loading
Loading
Loading
Loading
+75 −53
Original line number Original line Diff line number Diff line
@@ -24,16 +24,12 @@ import android.os.Looper;
import android.os.Message;
import android.os.Message;
import android.os.MessageQueue;
import android.os.MessageQueue;
import android.os.SystemClock;
import android.os.SystemClock;
import android.os.TestLooperManager;
import android.util.Log;
import android.util.Log;


import androidx.test.InstrumentationRegistry;

import java.lang.reflect.Constructor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.lang.reflect.Method;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


/**
/**
@@ -48,14 +44,17 @@ import java.util.concurrent.Executor;
 *     The Robolectric class also allows advancing time.
 *     The Robolectric class also allows advancing time.
 */
 */
public class TestLooper {
public class TestLooper {
    private final Looper mLooper;
    protected final Looper mLooper;
    private final TestLooperManager mTestLooperManager;
    private final Clock mClock;


    private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
    private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
    private static final Field THREAD_LOCAL_LOOPER_FIELD;
    private static final Field THREAD_LOCAL_LOOPER_FIELD;
    private static final Field MESSAGE_QUEUE_MESSAGES_FIELD;
    private static final Field MESSAGE_NEXT_FIELD;
    private static final Field MESSAGE_WHEN_FIELD;
    private static final Method MESSAGE_MARK_IN_USE_METHOD;
    private static final String TAG = "TestLooper";
    private static final String TAG = "TestLooper";


    private final Clock mClock;


    private AutoDispatchThread mAutoDispatchThread;
    private AutoDispatchThread mAutoDispatchThread;


@@ -65,6 +64,14 @@ public class TestLooper {
            LOOPER_CONSTRUCTOR.setAccessible(true);
            LOOPER_CONSTRUCTOR.setAccessible(true);
            THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
            THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
            THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
            THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
            MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
            MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
            MESSAGE_NEXT_FIELD.setAccessible(true);
            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
            MESSAGE_WHEN_FIELD.setAccessible(true);
            MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
            MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
        } catch (NoSuchFieldException | NoSuchMethodException e) {
        } catch (NoSuchFieldException | NoSuchMethodException e) {
            throw new RuntimeException("Failed to initialize TestLooper", e);
            throw new RuntimeException("Failed to initialize TestLooper", e);
        }
        }
@@ -99,8 +106,6 @@ public class TestLooper {
            throw new RuntimeException("Reflection error constructing or accessing looper", e);
            throw new RuntimeException("Reflection error constructing or accessing looper", e);
        }
        }


        mTestLooperManager =
            InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
        mClock = clock;
        mClock = clock;
    }
    }


@@ -112,61 +117,78 @@ public class TestLooper {
        return new HandlerExecutor(new Handler(getLooper()));
        return new HandlerExecutor(new Handler(getLooper()));
    }
    }


    public void moveTimeForward(long milliSeconds) {
    private Message getMessageLinkedList() {
        // Drain all Messages from the queue.
        try {
        Queue<Message> messages = new ArrayDeque<>();
            MessageQueue queue = mLooper.getQueue();
        while (true) {
            return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
            Message message = mTestLooperManager.poll();
        } catch (IllegalAccessException e) {
            if (message == null) {
            throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
                break;
                    e);
        }
    }
    }


            // Adjust the Message's delivery time.
    public void moveTimeForward(long milliSeconds) {
            long newWhen = message.when - milliSeconds;
        try {
            if (newWhen < 0) {
            Message msg = getMessageLinkedList();
                newWhen = 0;
            while (msg != null) {
                long updatedWhen = msg.getWhen() - milliSeconds;
                if (updatedWhen < 0) {
                    updatedWhen = 0;
                }
                }
            message.when = newWhen;
                MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
            messages.add(message);
                msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Access failed in TestLooper: set - Message.when", e);
        }
        }

        // Repost all Messages back to the queuewith a new time.
        while (true) {
            Message message = messages.poll();
            if (message == null) {
                break;
    }
    }


            Runnable callback = message.getCallback();
    private long currentTime() {
            Handler handler = message.getTarget();
        return mClock.uptimeMillis();
            long when = message.getWhen();

            // The Message cannot be re-enqueued because it is marked in use.
            // Make a copy of the Message and recycle the original.
            // This resets {@link Message#isInUse()} but retains all other content.
            {
                Message newMessage = Message.obtain();
                newMessage.copyFrom(message);
                newMessage.setCallback(callback);
                mTestLooperManager.recycle(message);
                message = newMessage;
    }
    }


            // Send the Message back to its Handler to be re-enqueued.
    private Message messageQueueNext() {
            handler.sendMessageAtTime(message, when);
        try {
            long now = currentTime();

            Message prevMsg = null;
            Message msg = getMessageLinkedList();
            if (msg != null && msg.getTarget() == null) {
                // Stalled by a barrier. Find the next asynchronous message in
                // the queue.
                do {
                    prevMsg = msg;
                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now >= msg.getWhen()) {
                    // Got a message.
                    if (prevMsg != null) {
                        MESSAGE_NEXT_FIELD.set(prevMsg, MESSAGE_NEXT_FIELD.get(msg));
                    } else {
                        MESSAGE_QUEUE_MESSAGES_FIELD.set(mLooper.getQueue(),
                                MESSAGE_NEXT_FIELD.get(msg));
                    }
                    MESSAGE_NEXT_FIELD.set(msg, null);
                    MESSAGE_MARK_IN_USE_METHOD.invoke(msg);
                    return msg;
                }
                }
            }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException("Access failed in TestLooper", e);
        }


    private long currentTime() {
        return null;
        return mClock.uptimeMillis();
    }
    }


    /**
    /**
     * @return true if there are pending messages in the message queue
     * @return true if there are pending messages in the message queue
     */
     */
    public synchronized boolean isIdle() {
    public synchronized boolean isIdle() {
        Long when = mTestLooperManager.peekWhen();
        Message messageList = getMessageLinkedList();
        return when != null && currentTime() >= when;

        return messageList != null && currentTime() >= messageList.getWhen();
    }
    }


    /**
    /**
@@ -174,7 +196,7 @@ public class TestLooper {
     */
     */
    public synchronized Message nextMessage() {
    public synchronized Message nextMessage() {
        if (isIdle()) {
        if (isIdle()) {
            return mTestLooperManager.poll();
            return messageQueueNext();
        } else {
        } else {
            return null;
            return null;
        }
        }
@@ -186,7 +208,7 @@ public class TestLooper {
     */
     */
    public synchronized void dispatchNext() {
    public synchronized void dispatchNext() {
        assertTrue(isIdle());
        assertTrue(isIdle());
        Message msg = mTestLooperManager.poll();
        Message msg = messageQueueNext();
        if (msg == null) {
        if (msg == null) {
            return;
            return;
        }
        }