Revert "Integrate new looper apis into testables"

This reverts commit f715f417d23d0605abcac309135340ac6ce311ce.

Change-Id: I4c0c12d8687426cecccf0f8dad710ea42699727b
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index f516d74..8808988 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -20,9 +20,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothProfile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
 
 import com.android.settingslib.bluetooth.BluetoothEventManager;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -33,13 +31,10 @@
 
 import org.junit.Before;
 import org.junit.Test;
-import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
 public class BluetoothControllerImplTest extends SysuiTestCase {
 
     private LocalBluetoothManager mMockBluetoothManager;
@@ -52,7 +47,7 @@
 
     @Before
     public void setup() throws Exception {
-        mTestableLooper = TestableLooper.get(this);
+        mTestableLooper = new TestableLooper();
         mMockBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager.class);
         mDevices = new ArrayList<>();
         mMockDeviceManager = mock(CachedBluetoothDeviceManager.class);
diff --git a/tests/testables/src/android/testing/AndroidTestingRunner.java b/tests/testables/src/android/testing/AndroidTestingRunner.java
index a425f70..816ed03 100644
--- a/tests/testables/src/android/testing/AndroidTestingRunner.java
+++ b/tests/testables/src/android/testing/AndroidTestingRunner.java
@@ -18,7 +18,7 @@
 import android.support.test.internal.runner.junit4.statement.RunBefores;
 import android.support.test.internal.runner.junit4.statement.UiThreadStatement;
 
-import android.testing.TestableLooper.LooperFrameworkMethod;
+import android.testing.TestableLooper.LooperStatement;
 import android.testing.TestableLooper.RunWithLooper;
 
 import org.junit.After;
@@ -30,7 +30,6 @@
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.Statement;
 
-import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -50,21 +49,28 @@
 
     @Override
     protected Statement methodInvoker(FrameworkMethod method, Object test) {
-        method = looperWrap(method, test, method);
-        final Statement statement = super.methodInvoker(method, test);
-        return shouldRunOnUiThread(method) ? new UiThreadStatement(statement, true) : statement;
+        return shouldRunOnUiThread(method) ? new UiThreadStatement(
+                methodInvokerInt(method, test), true) : methodInvokerInt(method, test);
+    }
+
+    protected Statement methodInvokerInt(FrameworkMethod method, Object test) {
+        RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
+        if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
+        if (annotation != null) {
+            return new LooperStatement(super.methodInvoker(method, test),
+                    annotation.setAsMainLooper(), test);
+        }
+        return super.methodInvoker(method, test);
     }
 
     protected Statement withBefores(FrameworkMethod method, Object target, Statement statement) {
-        List befores = looperWrap(method, target,
-                this.getTestClass().getAnnotatedMethods(Before.class));
+        List befores = this.getTestClass().getAnnotatedMethods(Before.class);
         return befores.isEmpty() ? statement : new RunBefores(method, statement,
                 befores, target);
     }
 
     protected Statement withAfters(FrameworkMethod method, Object target, Statement statement) {
-        List afters = looperWrap(method, target,
-                this.getTestClass().getAnnotatedMethods(After.class));
+        List afters = this.getTestClass().getAnnotatedMethods(After.class);
         return afters.isEmpty() ? statement : new RunAfters(method, statement, afters,
                 target);
     }
@@ -82,30 +88,6 @@
         return annotation == null ? 0L : annotation.timeout();
     }
 
-    protected List<FrameworkMethod> looperWrap(FrameworkMethod method, Object test,
-            List<FrameworkMethod> methods) {
-        RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
-        if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
-        if (annotation != null) {
-            methods = new ArrayList<>(methods);
-            for (int i = 0; i < methods.size(); i++) {
-                methods.set(i, LooperFrameworkMethod.get(methods.get(i),
-                        annotation.setAsMainLooper(), test));
-            }
-        }
-        return methods;
-    }
-
-    protected FrameworkMethod looperWrap(FrameworkMethod method, Object test,
-            FrameworkMethod base) {
-        RunWithLooper annotation = method.getAnnotation(RunWithLooper.class);
-        if (annotation == null) annotation = mKlass.getAnnotation(RunWithLooper.class);
-        if (annotation != null) {
-            return LooperFrameworkMethod.get(base, annotation.setAsMainLooper(), test);
-        }
-        return base;
-    }
-
     public boolean shouldRunOnUiThread(FrameworkMethod method) {
         if (mKlass.getAnnotation(UiThreadTest.class) != null) {
             return true;
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index 62490bc..8a33cf9 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -15,21 +15,20 @@
 package android.testing;
 
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 import android.os.MessageQueue;
-import android.os.TestLooperManager;
-import android.support.test.InstrumentationRegistry;
 import android.util.ArrayMap;
 
-import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.Statement;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Method;
 import java.util.Map;
 
 /**
@@ -39,35 +38,65 @@
  */
 public class TestableLooper {
 
+    private final Method mNext;
+    private final Method mRecycleUnchecked;
+
     private Looper mLooper;
     private MessageQueue mQueue;
     private boolean mMain;
     private Object mOriginalMain;
     private MessageHandler mMessageHandler;
 
+    private int mParsedCount;
     private Handler mHandler;
     private Message mEmptyMessage;
-    private TestLooperManager mQueueWrapper;
 
-    public TestableLooper(Looper l) throws Exception {
-        this(InstrumentationRegistry.getInstrumentation().acquireLooperManager(l), l);
+    public TestableLooper() throws Exception {
+        this(true);
     }
 
-    private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
-        mQueueWrapper = wrapper;
-        setupQueue(l);
-    }
-
-    private TestableLooper(Looper looper, boolean b) throws Exception {
-        setupQueue(looper);
+    public TestableLooper(boolean setMyLooper) throws Exception {
+        setupQueue(setMyLooper);
+        mNext = mQueue.getClass().getDeclaredMethod("next");
+        mNext.setAccessible(true);
+        mRecycleUnchecked = Message.class.getDeclaredMethod("recycleUnchecked");
+        mRecycleUnchecked.setAccessible(true);
     }
 
     public Looper getLooper() {
         return mLooper;
     }
 
-    private void setupQueue(Looper l) throws Exception {
-        mLooper = l;
+    private void clearLooper() throws NoSuchFieldException, IllegalAccessException {
+        Field field = Looper.class.getDeclaredField("sThreadLocal");
+        field.setAccessible(true);
+        ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
+        sThreadLocal.set(null);
+    }
+
+    private boolean setForCurrentThread() throws NoSuchFieldException, IllegalAccessException {
+        if (Looper.myLooper() != mLooper) {
+            Field field = Looper.class.getDeclaredField("sThreadLocal");
+            field.setAccessible(true);
+            ThreadLocal<Looper> sThreadLocal = (ThreadLocal<Looper>) field.get(null);
+            sThreadLocal.set(mLooper);
+            return true;
+        }
+        return false;
+    }
+
+    private void setupQueue(boolean setMyLooper) throws Exception {
+        if (setMyLooper) {
+            clearLooper();
+            Looper.prepare();
+            mLooper = Looper.myLooper();
+        } else {
+            Constructor<Looper> constructor = Looper.class.getDeclaredConstructor(
+                    boolean.class);
+            constructor.setAccessible(true);
+            mLooper = constructor.newInstance(true);
+        }
+
         mQueue = mLooper.getQueue();
         mHandler = new Handler(mLooper);
     }
@@ -92,7 +121,9 @@
      * tests.
      */
     public void destroy() throws NoSuchFieldException, IllegalAccessException {
-        mQueueWrapper.release();
+        if (Looper.myLooper() == mLooper) {
+            clearLooper();
+        }
         if (mMain && mOriginalMain != null) {
             Field field = mLooper.getClass().getDeclaredField("sMainLooper");
             field.setAccessible(true);
@@ -133,26 +164,26 @@
 
     private boolean parseMessageInt() {
         try {
-            Message result = mQueueWrapper.next();
+            Message result = (Message) mNext.invoke(mQueue);
             if (result != null) {
                 // This is a break message.
                 if (result == mEmptyMessage) {
-                    mQueueWrapper.recycle(result);
+                    mRecycleUnchecked.invoke(result);
                     return false;
                 }
 
                 if (mMessageHandler != null) {
                     if (mMessageHandler.onMessageHandled(result)) {
                         result.getTarget().dispatchMessage(result);
-                        mQueueWrapper.recycle(result);
+                        mRecycleUnchecked.invoke(result);
                     } else {
-                        mQueueWrapper.recycle(result);
+                        mRecycleUnchecked.invoke(result);
                         // Message handler indicated it doesn't want us to continue.
                         return false;
                     }
                 } else {
                     result.getTarget().dispatchMessage(result);
-                    mQueueWrapper.recycle(result);
+                    mRecycleUnchecked.invoke(result);
                 }
             } else {
                 // No messages, don't continue parsing
@@ -168,14 +199,10 @@
      * Runs an executable with myLooper set and processes all messages added.
      */
     public void runWithLooper(RunnableWithException runnable) throws Exception {
-        new Handler(getLooper()).post(() -> {
-            try {
-                runnable.run();
-            } catch (Exception e) {
-                throw new RuntimeException(e);
-            }
-        });
+        boolean set = setForCurrentThread();
+        runnable.run();
         processAllMessages();
+        if (set) clearLooper();
     }
 
     public interface RunnableWithException {
@@ -194,131 +221,33 @@
         return sLoopers.get(test);
     }
 
-    public static class LooperFrameworkMethod extends FrameworkMethod {
-        private HandlerThread mHandlerThread;
+    public static class LooperStatement extends Statement {
+        private final boolean mSetAsMain;
+        private final Statement mBase;
+        private final TestableLooper mLooper;
 
-        private final TestableLooper mTestableLooper;
-        private final Looper mLooper;
-        private final Handler mHandler;
-
-        public LooperFrameworkMethod(FrameworkMethod base, boolean setAsMain, Object test) {
-            super(base.getMethod());
+        public LooperStatement(Statement base, boolean setAsMain, Object test) {
+            mBase = base;
             try {
-                mLooper = setAsMain ? Looper.getMainLooper() : createLooper();
-                mTestableLooper = new TestableLooper(mLooper, false);
+                mLooper = new TestableLooper(false);
+                sLoopers.put(test, mLooper);
+                mSetAsMain = setAsMain;
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
-            sLoopers.put(test, mTestableLooper);
-            mHandler = new Handler(mLooper);
-        }
-
-        public LooperFrameworkMethod(TestableLooper other, FrameworkMethod base) {
-            super(base.getMethod());
-            mLooper = other.mLooper;
-            mTestableLooper = other;
-            mHandler = new Handler(mLooper);
-        }
-
-        public static FrameworkMethod get(FrameworkMethod base, boolean setAsMain, Object test) {
-            if (sLoopers.containsKey(test)) {
-                return new LooperFrameworkMethod(sLoopers.get(test), base);
-            }
-            return new LooperFrameworkMethod(base, setAsMain, test);
         }
 
         @Override
-        public Object invokeExplosively(Object target, Object... params) throws Throwable {
-            if (Looper.myLooper() == mLooper) {
-                // Already on the right thread from another statement, just execute then.
-                return super.invokeExplosively(target, params);
+        public void evaluate() throws Throwable {
+            mLooper.setForCurrentThread();
+            if (mSetAsMain) {
+                mLooper.setAsMainLooper();
             }
-            boolean set = mTestableLooper.mQueueWrapper == null;
-            if (set) {
-                mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
-                        .acquireLooperManager(mLooper);
-            }
+
             try {
-                Object[] ret = new Object[1];
-                // Run the execution on the looper thread.
-                Runnable execute = () -> {
-                    try {
-                        ret[0] = super.invokeExplosively(target, params);
-                    } catch (Throwable throwable) {
-                        throw new LooperException(throwable);
-                    }
-                };
-                mHandler.post(execute);
-                // Try to wait for the message to be queued.
-                for (int i = 0; i < 10; i++) {
-                    if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
-                        Thread.sleep(1);
-                    }
-                }
-                if (!mTestableLooper.mQueueWrapper.hasMessages(mHandler, null, execute)) {
-                    throw new RuntimeException("Message didn't queue...");
-                }
-                Message m = mTestableLooper.mQueueWrapper.next();
-                // Parse all other messages until we get to ours.
-                while (m.getTarget() != mHandler) {
-                    try {
-                        mTestableLooper.mQueueWrapper.execute(m);
-                    } catch (LooperException e) {
-                        throw e.getSource();
-                    } finally {
-                        mTestableLooper.mQueueWrapper.recycle(m);
-                    }
-                    m = mTestableLooper.mQueueWrapper.next();
-                }
-                // Dispatch our message.
-                try {
-                    mTestableLooper.mQueueWrapper.execute(m);
-                } catch (LooperException e) {
-                    throw e.getSource();
-                } catch (RuntimeException re) {
-                    // If the TestLooperManager has to post, it will wrap what it throws in a
-                    // RuntimeException, make sure we grab the actual source.
-                    if (re.getCause() instanceof LooperException) {
-                        throw ((LooperException) re.getCause()).getSource();
-                    } else {
-                        throw re.getCause();
-                    }
-                } finally {
-                    mTestableLooper.mQueueWrapper.recycle(m);
-                }
-                return ret[0];
+                mBase.evaluate();
             } finally {
-                if (set) {
-                    mTestableLooper.mQueueWrapper.release();
-                    mTestableLooper.mQueueWrapper = null;
-                }
-            }
-        }
-
-        private Looper createLooper() {
-            // TODO: Find way to share these.
-            mHandlerThread = new HandlerThread(TestableLooper.class.getSimpleName());
-            mHandlerThread.start();
-            return mHandlerThread.getLooper();
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            super.finalize();
-            if (mHandlerThread != null) {
-                mHandlerThread.quit();
-            }
-        }
-
-        private static class LooperException extends RuntimeException {
-            private final Throwable mSource;
-
-            public LooperException(Throwable t) {
-                mSource = t;
-            }
-
-            public Throwable getSource() {
-                return mSource;
+                mLooper.destroy();
             }
         }
     }
diff --git a/tests/testables/tests/src/android/testing/TestableLooperTest.java b/tests/testables/tests/src/android/testing/TestableLooperTest.java
index 12f1d0a..18e5fff 100644
--- a/tests/testables/tests/src/android/testing/TestableLooperTest.java
+++ b/tests/testables/tests/src/android/testing/TestableLooperTest.java
@@ -24,16 +24,17 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.testing.TestableLooper.MessageHandler;
 import android.testing.TestableLooper.RunWithLooper;
 
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class TestableLooperTest {
@@ -45,6 +46,11 @@
         mTestableLooper = TestableLooper.get(this);
     }
 
+    @After
+    public void tearDown() throws Exception {
+        mTestableLooper.destroy();
+    }
+
     @Test
     public void testMessageExecuted() throws Exception {
         Handler h = new Handler();
@@ -127,23 +133,39 @@
     @Test
     public void testMainLooper() throws Exception {
         assertNotEquals(Looper.myLooper(), Looper.getMainLooper());
+
+        Looper originalMain = Looper.getMainLooper();
+        mTestableLooper.setAsMainLooper();
+        assertEquals(Looper.myLooper(), Looper.getMainLooper());
+        Runnable r = mock(Runnable.class);
+
+        new Handler(Looper.getMainLooper()).post(r);
+        mTestableLooper.processAllMessages();
+
+        verify(r).run();
+        mTestableLooper.destroy();
+
+        assertEquals(originalMain, Looper.getMainLooper());
+    }
+
+    @Test
+    public void testNotMyLooper() throws Exception {
+        TestableLooper looper = new TestableLooper(false);
+
+        assertEquals(Looper.myLooper(), mTestableLooper.getLooper());
+        assertNotEquals(Looper.myLooper(), looper.getLooper());
+
         Runnable r = mock(Runnable.class);
         Runnable r2 = mock(Runnable.class);
-        TestableLooper testableLooper = new TestableLooper(Looper.getMainLooper());
+        new Handler().post(r);
+        new Handler(looper.getLooper()).post(r2);
 
-        try {
-            testableLooper.setMessageHandler(m -> {
-                if (m.getCallback() == r) return true;
-                return false;
-            });
-            new Handler(Looper.getMainLooper()).post(r);
-            testableLooper.processAllMessages();
+        looper.processAllMessages();
+        verify(r2).run();
+        verify(r, never()).run();
 
-            verify(r).run();
-            verify(r2, never()).run();
-        } finally {
-            testableLooper.destroy();
-        }
+        mTestableLooper.processAllMessages();
+        verify(r).run();
     }
 
     @Test