Offer `TestLooperManager` on Ravenwood.
Common test utilities like `TestableLooper` rely on being able to
drive `Looper` interactions via `TestLooperManager` which can only
be obtained via `Instrumentation`.
This change integrates with the common `InstrumentationRegistry`
library which is how most tests obtain their `Instrumentation`
instance. We play an odd dance with `java_host_for_device` and
`java_device_for_host` to get a version of `androidx.test.monitor`
that we can link against our runtime. (The build system otherwise
gets excited that we're trying to compile an Android-linked library
into what appears to be a Java-linked library, but we know that we're
offering the Android runtime.)
Fix subtle bug in `MessageQueue_host` where a requested wake might
race with the `for (;;)` loop in `MessageQueue.next()`; if a wake is
pending, it's a valid reason to return from `nativePollOnce()`.
Add timeout logic to `RavenwoodRuleImpl` which can be enabled to
dump all our stack traces just before Tradefed times out, to aid in
debugging deadlocks; disabled by default.
Bug: 319647875
Test: atest SystemUiRoboTests
Test: atest SystemUiRavenTests
Test: atest FrameworksCoreTestsRavenwood:TestLooperManagerTest
Change-Id: I13605d32df0c3e6758f541d5e75a6bdaf44368db
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index e013a3e..1ac69f6 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -30,6 +30,9 @@
"junit-src/**/*.java",
"junit-impl-src/**/*.java",
],
+ static_libs: [
+ "androidx.test.monitor-for-device",
+ ],
libs: [
"framework-minus-apex.ravenwood",
"junit",
@@ -61,3 +64,17 @@
"core-xml-for-host",
],
}
+
+java_host_for_device {
+ name: "androidx.test.monitor-for-device",
+ libs: [
+ "androidx.test.monitor-for-host",
+ ],
+}
+
+java_device_for_host {
+ name: "androidx.test.monitor-for-host",
+ libs: [
+ "androidx.test.monitor",
+ ],
+}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index b3dbcde..a797b1d 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,12 +16,35 @@
package android.platform.test.ravenwood;
+import android.app.Instrumentation;
+import android.os.Bundle;
import android.os.HandlerThread;
import android.os.Looper;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
public class RavenwoodRuleImpl {
private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+ /**
+ * When enabled, attempt to dump all thread stacks just before we hit the
+ * overall Tradefed timeout, to aid in debugging deadlocks.
+ */
+ private static final boolean ENABLE_TIMEOUT_STACKS = false;
+ private static final int TIMEOUT_MILLIS = 9_000;
+
+ private static final ScheduledExecutorService sTimeoutExecutor =
+ Executors.newScheduledThreadPool(1);
+
+ private static ScheduledFuture<?> sPendingTimeout;
+
public static boolean isOnRavenwood() {
return true;
}
@@ -41,9 +64,22 @@
main.start();
Looper.setMainLooperForTest(main.getLooper());
}
+
+ InstrumentationRegistry.registerInstance(new Instrumentation(), Bundle.EMPTY);
+
+ if (ENABLE_TIMEOUT_STACKS) {
+ sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks,
+ TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ }
}
public static void reset(RavenwoodRule rule) {
+ if (ENABLE_TIMEOUT_STACKS) {
+ sPendingTimeout.cancel(false);
+ }
+
+ InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+
if (rule.mProvideMainThread) {
Looper.getMainLooper().quit();
Looper.clearMainLooperForTest();
@@ -55,4 +91,19 @@
android.os.Binder.reset$ravenwood();
android.os.Process.reset$ravenwood();
}
+
+ private static void dumpStacks() {
+ final PrintStream out = System.err;
+ out.println("-----BEGIN ALL THREAD STACKS-----");
+ final Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
+ for (Map.Entry<Thread, StackTraceElement[]> stack : stacks.entrySet()) {
+ out.println();
+ Thread t = stack.getKey();
+ out.println(t.toString() + " ID=" + t.getId());
+ for (StackTraceElement e : stack.getValue()) {
+ out.println("\tat " + e);
+ }
+ }
+ out.println("-----END ALL THREAD STACKS-----");
+ }
}
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index eaf01a3..b775f9a 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -72,6 +72,7 @@
android.os.ServiceSpecificException
android.os.SystemClock
android.os.SystemProperties
+android.os.TestLooperManager
android.os.ThreadLocalWorkSource
android.os.TimestampedValue
android.os.Trace
@@ -141,6 +142,8 @@
android.content.ContentProvider
+android.app.Instrumentation
+
android.metrics.LogMaker
android.view.Display$HdrCapabilities