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

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

Merge "Remove MessageQueue reflection from TestLooper" into main

parents 58bf2b4f 80a59fdb
Loading
Loading
Loading
Loading
+53 −75
Original line number Original line Diff line number Diff line
@@ -24,12 +24,16 @@ 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.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;


/**
/**
@@ -44,17 +48,14 @@ 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 {
    protected final Looper mLooper;
    private 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;


@@ -64,14 +65,6 @@ 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);
        }
        }
@@ -106,6 +99,8 @@ 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;
    }
    }


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


    private Message getMessageLinkedList() {
        try {
            MessageQueue queue = mLooper.getQueue();
            return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
                    e);
        }
    }

    public void moveTimeForward(long milliSeconds) {
    public void moveTimeForward(long milliSeconds) {
        try {
        // Drain all Messages from the queue.
            Message msg = getMessageLinkedList();
        Queue<Message> messages = new ArrayDeque<>();
            while (msg != null) {
        while (true) {
                long updatedWhen = msg.getWhen() - milliSeconds;
            Message message = mTestLooperManager.poll();
                if (updatedWhen < 0) {
            if (message == null) {
                    updatedWhen = 0;
                break;
                }
                MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
                msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
            }
            }
        } catch (IllegalAccessException e) {

            throw new RuntimeException("Access failed in TestLooper: set - Message.when", e);
            // Adjust the Message's delivery time.
            long newWhen = message.when - milliSeconds;
            if (newWhen < 0) {
                newWhen = 0;
            }
            }
            message.when = newWhen;
            messages.add(message);
        }
        }


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


    private Message messageQueueNext() {
            Runnable callback = message.getCallback();
        try {
            Handler handler = message.getTarget();
            long now = currentTime();
            long when = message.getWhen();


            Message prevMsg = null;
            // The Message cannot be re-enqueued because it is marked in use.
            Message msg = getMessageLinkedList();
            // Make a copy of the Message and recycle the original.
            if (msg != null && msg.getTarget() == null) {
            // This resets {@link Message#isInUse()} but retains all other content.
                // Stalled by a barrier. Find the next asynchronous message in
            {
                // the queue.
                Message newMessage = Message.obtain();
                do {
                newMessage.copyFrom(message);
                    prevMsg = msg;
                newMessage.setCallback(callback);
                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
                mTestLooperManager.recycle(message);
                } while (msg != null && !msg.isAsynchronous());
                message = newMessage;
            }
            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;
            }
            }

            // Send the Message back to its Handler to be re-enqueued.
            handler.sendMessageAtTime(message, when);
        }
        }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException("Access failed in TestLooper", e);
    }
    }


        return null;
    private long currentTime() {
        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() {
        Message messageList = getMessageLinkedList();
        Long when = mTestLooperManager.peekWhen();

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


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