summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jeff Sharkey <jsharkey@google.com> 2023-11-16 16:10:54 -0700
committer Jeff Sharkey <jsharkey@google.com> 2023-11-21 09:36:02 -0700
commit1c7db6b9a1562404b870daa19d353d159e9608a9 (patch)
tree8f57aa4b4e62972c57549ca5fe950dab8ac53c45
parentc914beed5fe5055d4aea2a227b064c45fdbd96ce (diff)
Support Handler/Looper for Ravenwood, with CTS.
Now that we have a solid foundation of classes available, one of the tricker pieces is supporting Handler/Looper under Ravenwood. At its core, the native implementation of MessageQueue can be emulated using core JVM primitives, which is enough to reliably pass CTS. Advanced features like FileDescriptor events will need to wait until we eventually have real JNI support. Fix obscure bug with SystemClock; must be positive number. Always start our "fake" pointers from 1 to prevent `nullptr` oddness. Bug: 292141694 Test: atest-dev CtsOsTestCasesRavenwood CtsOsTestCases Change-Id: I0f82b659973443968ef2609a7e3151f381abff29
-rw-r--r--core/java/android/os/Handler.java1
-rw-r--r--core/java/android/os/HandlerThread.java1
-rw-r--r--core/java/android/os/Looper.java45
-rw-r--r--core/java/android/os/Message.java1
-rw-r--r--core/java/android/os/MessageQueue.java6
-rw-r--r--core/java/android/os/Process.java58
-rw-r--r--core/java/android/os/SystemClock.java20
-rw-r--r--core/java/android/os/ThreadLocalWorkSource.java1
-rw-r--r--ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java9
-rw-r--r--ravenwood/framework-minus-apex-ravenwood-policies.txt1
-rw-r--r--ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java22
-rw-r--r--ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java23
-rw-r--r--ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java4
-rw-r--r--ravenwood/ravenwood-annotation-allowed-classes.txt7
-rw-r--r--ravenwood/ravenwood-standard-options.txt1
-rw-r--r--tools/hoststubgen/hoststubgen/framework-policy-override.txt3
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java94
-rw-r--r--tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java2
18 files changed, 269 insertions, 30 deletions
diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java
index 22d6fcdc7983..92b630f3063a 100644
--- a/core/java/android/os/Handler.java
+++ b/core/java/android/os/Handler.java
@@ -63,6 +63,7 @@ import java.lang.reflect.Modifier;
* your new thread. The given Runnable or Message will then be scheduled
* in the Handler's message queue and processed when appropriate.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class Handler {
/*
* Set this flag to true to detect anonymous, local or member classes
diff --git a/core/java/android/os/HandlerThread.java b/core/java/android/os/HandlerThread.java
index 4dd797a70fcb..fcd57313a28d 100644
--- a/core/java/android/os/HandlerThread.java
+++ b/core/java/android/os/HandlerThread.java
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
* <p>
* Note that just like with a regular {@link Thread}, {@link #start()} must still be called.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public class HandlerThread extends Thread {
int mPriority;
int mTid = -1;
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 712d328e9dc9..ddf2b61324ad 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -24,6 +24,8 @@ import android.util.Printer;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
+import java.util.Objects;
+
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
@@ -54,6 +56,7 @@ import android.util.proto.ProtoOutputStream;
* }
* }</pre>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Looper {
/*
* API Implementation Note:
@@ -144,6 +147,30 @@ public final class Looper {
}
/**
+ * Force the application's main looper to the given value. The main looper is typically
+ * configured automatically by the OS, so this capability is only intended to enable testing.
+ *
+ * @hide
+ */
+ public static void setMainLooperForTest(@NonNull Looper looper) {
+ synchronized (Looper.class) {
+ sMainLooper = Objects.requireNonNull(looper);
+ }
+ }
+
+ /**
+ * Clear the application's main looper to be undefined. The main looper is typically
+ * configured automatically by the OS, so this capability is only intended to enable testing.
+ *
+ * @hide
+ */
+ public static void clearMainLooperForTest() {
+ synchronized (Looper.class) {
+ sMainLooper = null;
+ }
+ }
+
+ /**
* Set the transaction observer for all Loopers in this process.
*
* @hide
@@ -282,11 +309,7 @@ public final class Looper {
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
- final int thresholdOverride =
- SystemProperties.getInt("log.looper."
- + Process.myUid() + "."
- + Thread.currentThread().getName()
- + ".slow", -1);
+ final int thresholdOverride = getThresholdOverride();
me.mSlowDeliveryDetected = false;
@@ -297,6 +320,18 @@ public final class Looper {
}
}
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static int getThresholdOverride() {
+ return SystemProperties.getInt("log.looper."
+ + Process.myUid() + "."
+ + Thread.currentThread().getName()
+ + ".slow", -1);
+ }
+
+ private static int getThresholdOverride$ravenwood() {
+ return -1;
+ }
+
private static boolean showSlowLog(long threshold, long measureStart, long measureEnd,
String what, Message msg) {
final long actualTime = measureEnd - measureStart;
diff --git a/core/java/android/os/Message.java b/core/java/android/os/Message.java
index 72fb4ae03a63..da647e2b78cb 100644
--- a/core/java/android/os/Message.java
+++ b/core/java/android/os/Message.java
@@ -33,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting;
* {@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class Message implements Parcelable {
/**
* User-defined message code so that the recipient can identify
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 9d8a71bf4abd..c60f949da408 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -40,6 +40,9 @@ import java.util.ArrayList;
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+@android.ravenwood.annotation.RavenwoodNativeSubstitutionClass(
+ "com.android.hoststubgen.nativesubstitution.MessageQueue_host")
public final class MessageQueue {
private static final String TAG = "MessageQueue";
private static final boolean DEBUG = false;
@@ -194,6 +197,7 @@ public final class MessageQueue {
* @see OnFileDescriptorEventListener
* @see #removeOnFileDescriptorEventListener
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void addOnFileDescriptorEventListener(@NonNull FileDescriptor fd,
@OnFileDescriptorEventListener.Events int events,
@NonNull OnFileDescriptorEventListener listener) {
@@ -221,6 +225,7 @@ public final class MessageQueue {
* @see OnFileDescriptorEventListener
* @see #addOnFileDescriptorEventListener
*/
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
public void removeOnFileDescriptorEventListener(@NonNull FileDescriptor fd) {
if (fd == null) {
throw new IllegalArgumentException("fd must not be null");
@@ -231,6 +236,7 @@ public final class MessageQueue {
}
}
+ @android.ravenwood.annotation.RavenwoodThrow(blockedBy = android.os.ParcelFileDescriptor.class)
private void updateOnFileDescriptorEventListenerLocked(FileDescriptor fd, int events,
OnFileDescriptorEventListener listener) {
final int fdNum = fd.getInt$();
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 13572fb1bbbb..11660f9930c3 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -842,15 +842,19 @@ public class Process {
return "amd64".equals(System.getProperty("os.arch"));
}
- private static SomeArgs sIdentity$ravenwood;
+ private static ThreadLocal<SomeArgs> sIdentity$ravenwood;
/** @hide */
@android.ravenwood.annotation.RavenwoodKeep
- public static void init$ravenwood(int uid, int pid) {
- final SomeArgs args = SomeArgs.obtain();
- args.argi1 = uid;
- args.argi2 = pid;
- sIdentity$ravenwood = args;
+ public static void init$ravenwood(final int uid, final int pid) {
+ sIdentity$ravenwood = ThreadLocal.withInitial(() -> {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = uid;
+ args.argi2 = pid;
+ args.argi3 = Long.hashCode(Thread.currentThread().getId());
+ args.argi4 = THREAD_PRIORITY_DEFAULT;
+ return args;
+ });
}
/** @hide */
@@ -870,7 +874,7 @@ public class Process {
/** @hide */
public static final int myPid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi2;
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi2;
}
/**
@@ -886,10 +890,16 @@ public class Process {
* Returns the identifier of the calling thread, which be used with
* {@link #setThreadPriority(int, int)}.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final int myTid() {
return Os.gettid();
}
+ /** @hide */
+ public static final int myTid$ravenwood() {
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi3;
+ }
+
/**
* Returns the identifier of this process's uid. This is the kernel uid
* that the process is running under, which is the identity of its
@@ -903,7 +913,7 @@ public class Process {
/** @hide */
public static final int myUid$ravenwood() {
- return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).argi1;
+ return Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get().argi1;
}
/**
@@ -1086,9 +1096,22 @@ public class Process {
* not have permission to modify the given thread, or to use the given
* priority.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void setThreadPriority(int tid, int priority)
throws IllegalArgumentException, SecurityException;
+ /** @hide */
+ public static final void setThreadPriority$ravenwood(int tid, int priority) {
+ final SomeArgs args =
+ Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
+ if (args.argi3 == tid) {
+ args.argi4 = priority;
+ } else {
+ throw new UnsupportedOperationException(
+ "Cross-thread priority management not yet available in Ravenwood");
+ }
+ }
+
/**
* Call with 'false' to cause future calls to {@link #setThreadPriority(int)} to
* throw an exception if passed a background-level thread priority. This is only
@@ -1226,9 +1249,15 @@ public class Process {
*
* @see #setThreadPriority(int, int)
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native void setThreadPriority(int priority)
throws IllegalArgumentException, SecurityException;
+ /** @hide */
+ public static final void setThreadPriority$ravenwood(int priority) {
+ setThreadPriority(myTid(), priority);
+ }
+
/**
* Return the current priority of a thread, based on Linux priorities.
*
@@ -1242,9 +1271,22 @@ public class Process {
* @throws IllegalArgumentException Throws IllegalArgumentException if
* <var>tid</var> does not exist.
*/
+ @android.ravenwood.annotation.RavenwoodReplace
public static final native int getThreadPriority(int tid)
throws IllegalArgumentException;
+ /** @hide */
+ public static final int getThreadPriority$ravenwood(int tid) {
+ final SomeArgs args =
+ Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
+ if (args.argi3 == tid) {
+ return args.argi4;
+ } else {
+ throw new UnsupportedOperationException(
+ "Cross-thread priority management not yet available in Ravenwood");
+ }
+ }
+
/**
* Return the current scheduling policy of a thread, based on Linux.
*
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 49a0bd3289aa..2e6cccbd435f 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -110,6 +110,11 @@ public final class SystemClock {
private static volatile IAlarmManager sIAlarmManager;
/**
+ * Since {@code nanoTime()} is arbitrary, anchor our Ravenwood clocks against it.
+ */
+ private static final long sAnchorNanoTime$ravenwood = System.nanoTime();
+
+ /**
* This class is uninstantiable.
*/
@UnsupportedAppUsage
@@ -193,9 +198,7 @@ public final class SystemClock {
/** @hide */
public static long uptimeMillis$ravenwood() {
- // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
- return System.currentTimeMillis() - (1672556400L * 1_000)
- - (DateUtils.WEEK_IN_MILLIS * 1_000);
+ return uptimeNanos() / 1_000_000;
}
/**
@@ -210,9 +213,7 @@ public final class SystemClock {
/** @hide */
public static long uptimeNanos$ravenwood() {
- // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
- return System.nanoTime() - (1672556400L * 1_000_000_000)
- - (DateUtils.WEEK_IN_MILLIS * 1_000_000_000);
+ return System.nanoTime() - sAnchorNanoTime$ravenwood;
}
/**
@@ -241,8 +242,7 @@ public final class SystemClock {
/** @hide */
public static long elapsedRealtime$ravenwood() {
- // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
- return System.currentTimeMillis() - (1672556400L * 1_000);
+ return elapsedRealtimeNanos() / 1_000_000;
}
/**
@@ -271,8 +271,8 @@ public final class SystemClock {
/** @hide */
public static long elapsedRealtimeNanos$ravenwood() {
- // Ravenwood booted in Jan 2023, and has been in deep sleep for one week
- return System.nanoTime() - (1672556400L * 1_000_000_000);
+ // Elapsed realtime is uptime plus an hour that we've been "asleep"
+ return uptimeNanos() + (DateUtils.HOUR_IN_MILLIS * 1_000_000);
}
/**
diff --git a/core/java/android/os/ThreadLocalWorkSource.java b/core/java/android/os/ThreadLocalWorkSource.java
index e9adb209ddc6..7c4a2be09bf8 100644
--- a/core/java/android/os/ThreadLocalWorkSource.java
+++ b/core/java/android/os/ThreadLocalWorkSource.java
@@ -37,6 +37,7 @@ package android.os;
*
* @hide Only for use within system server.
*/
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
public final class ThreadLocalWorkSource {
public static final int UID_NONE = Message.UID_NONE;
private static final ThreadLocal<int []> sWorkSourceUid =
diff --git a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
index a234a9b6fc7c..0bb1f39cd453 100644
--- a/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
+++ b/ravenwood/annotations-src/android/ravenwood/annotation/RavenwoodThrow.java
@@ -34,4 +34,13 @@ import java.lang.annotation.Target;
@Target({METHOD, CONSTRUCTOR})
@Retention(RetentionPolicy.CLASS)
public @interface RavenwoodThrow {
+ /**
+ * One or more classes that aren't yet supported by Ravenwood, which is why this method throws.
+ */
+ Class<?>[] blockedBy() default {};
+
+ /**
+ * General free-form description of why this method throws.
+ */
+ String reason() default "";
}
diff --git a/ravenwood/framework-minus-apex-ravenwood-policies.txt b/ravenwood/framework-minus-apex-ravenwood-policies.txt
index 48e93280f73f..b92663c83889 100644
--- a/ravenwood/framework-minus-apex-ravenwood-policies.txt
+++ b/ravenwood/framework-minus-apex-ravenwood-policies.txt
@@ -109,6 +109,7 @@ class android.os.Bundle stubclass
class android.os.PersistableBundle stubclass
# Misc
+class android.os.HandlerExecutor stubclass
class android.os.PatternMatcher stubclass
class android.os.ParcelUuid stubclass
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 1caef26d50eb..be0c09ee4a25 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,13 +16,35 @@
package android.platform.test.ravenwood;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+import java.util.Objects;
+
public class RavenwoodRuleImpl {
+ private static final String MAIN_THREAD_NAME = "RavenwoodMain";
+
+ public static boolean isUnderRavenwood() {
+ return true;
+ }
+
public static void init(RavenwoodRule rule) {
android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
android.os.Binder.init$ravenwood();
+
+ if (rule.mProvideMainThread) {
+ final HandlerThread main = new HandlerThread(MAIN_THREAD_NAME);
+ main.start();
+ Looper.setMainLooperForTest(main.getLooper());
+ }
}
public static void reset(RavenwoodRule rule) {
+ if (rule.mProvideMainThread) {
+ Looper.getMainLooper().quit();
+ Looper.clearMainLooperForTest();
+ }
+
android.os.Process.reset$ravenwood();
android.os.Binder.reset$ravenwood();
}
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 79f9e58aaa1a..9db5b9895749 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -34,6 +34,8 @@ import java.util.concurrent.atomic.AtomicInteger;
public class RavenwoodRule implements TestRule {
private static AtomicInteger sNextPid = new AtomicInteger(100);
+ private static final boolean IS_UNDER_RAVENWOOD = RavenwoodRuleImpl.isUnderRavenwood();
+
private static final int SYSTEM_UID = 1000;
private static final int NOBODY_UID = 9999;
private static final int FIRST_APPLICATION_UID = 10000;
@@ -45,6 +47,8 @@ public class RavenwoodRule implements TestRule {
int mUid = NOBODY_UID;
int mPid = sNextPid.getAndIncrement();
+ boolean mProvideMainThread = false;
+
public RavenwoodRule() {
}
@@ -72,6 +76,15 @@ public class RavenwoodRule implements TestRule {
return this;
}
+ /**
+ * Configure a "main" thread to be available for the duration of the test, as defined
+ * by {@code Looper.getMainLooper()}. Has no effect under non-Ravenwood environments.
+ */
+ public Builder setProvideMainThread(boolean provideMainThread) {
+ mRule.mProvideMainThread = provideMainThread;
+ return this;
+ }
+
public RavenwoodRule build() {
return mRule;
}
@@ -81,8 +94,7 @@ public class RavenwoodRule implements TestRule {
* Return if the current process is running under a Ravenwood test environment.
*/
public boolean isUnderRavenwood() {
- // TODO: give ourselves a better environment signal
- return System.getProperty("java.class.path").contains("ravenwood");
+ return IS_UNDER_RAVENWOOD;
}
@Override
@@ -90,17 +102,16 @@ public class RavenwoodRule implements TestRule {
return new Statement() {
@Override
public void evaluate() throws Throwable {
- final boolean isUnderRavenwood = isUnderRavenwood();
if (description.getAnnotation(IgnoreUnderRavenwood.class) != null) {
- Assume.assumeFalse(isUnderRavenwood);
+ Assume.assumeFalse(IS_UNDER_RAVENWOOD);
}
- if (isUnderRavenwood) {
+ if (IS_UNDER_RAVENWOOD) {
RavenwoodRuleImpl.init(RavenwoodRule.this);
}
try {
base.evaluate();
} finally {
- if (isUnderRavenwood) {
+ if (IS_UNDER_RAVENWOOD) {
RavenwoodRuleImpl.reset(RavenwoodRule.this);
}
}
diff --git a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index ecaff8084888..fb71e9d1ac6f 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -17,6 +17,10 @@
package android.platform.test.ravenwood;
public class RavenwoodRuleImpl {
+ public static boolean isUnderRavenwood() {
+ return false;
+ }
+
public static void init(RavenwoodRule rule) {
// Must be provided by impl to reference runtime internals
throw new UnsupportedOperationException();
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index a791f682fecf..e943786a5369 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -12,9 +12,16 @@ android.util.Xml
android.os.Binder
android.os.Binder$IdentitySupplier
+android.os.Handler
+android.os.HandlerExecutor
+android.os.HandlerThread
android.os.IBinder
+android.os.Looper
+android.os.Message
+android.os.MessageQueue
android.os.Process
android.os.SystemClock
+android.os.ThreadLocalWorkSource
android.os.UserHandle
android.content.ClipData
diff --git a/ravenwood/ravenwood-standard-options.txt b/ravenwood/ravenwood-standard-options.txt
index f842f33bc95b..8ad21fa53a51 100644
--- a/ravenwood/ravenwood-standard-options.txt
+++ b/ravenwood/ravenwood-standard-options.txt
@@ -7,6 +7,7 @@
# Uncomment below lines to enable each feature.
# --enable-non-stub-method-check
+--no-non-stub-method-check
#--default-method-call-hook
# com.android.hoststubgen.hosthelper.HostTestUtils.logMethodCall
diff --git a/tools/hoststubgen/hoststubgen/framework-policy-override.txt b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
index 493ad56a5cbb..af3789e270a4 100644
--- a/tools/hoststubgen/hoststubgen/framework-policy-override.txt
+++ b/tools/hoststubgen/hoststubgen/framework-policy-override.txt
@@ -100,3 +100,6 @@ class android.os.BadTypeParcelableException stubclass
class android.os.BaseBundle stubclass
class android.os.Bundle stubclass
class android.os.PersistableBundle stubclass
+
+class android.os.MessageQueue stubclass
+class android.os.MessageQueue !com.android.hoststubgen.nativesubstitution.MessageQueue_host
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
new file mode 100644
index 000000000000..2e47d48f4fa0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.hoststubgen.nativesubstitution;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class MessageQueue_host {
+ private static final AtomicLong sNextId = new AtomicLong(1);
+ private static final Map<Long, MessageQueue_host> sInstances = new ConcurrentHashMap<>();
+
+ private boolean mDeleted = false;
+
+ private final Object mPoller = new Object();
+ private volatile boolean mPolling;
+
+ private void validate() {
+ if (mDeleted) {
+ // TODO: Put more info
+ throw new RuntimeException("MessageQueue already destroyed");
+ }
+ }
+
+ private static MessageQueue_host getInstance(long id) {
+ MessageQueue_host q = sInstances.get(id);
+ if (q == null) {
+ throw new RuntimeException("MessageQueue doesn't exist with id=" + id);
+ }
+ q.validate();
+ return q;
+ }
+
+ public static long nativeInit() {
+ final long id = sNextId.getAndIncrement();
+ final MessageQueue_host q = new MessageQueue_host();
+ sInstances.put(id, q);
+ return id;
+ }
+
+ public static void nativeDestroy(long ptr) {
+ getInstance(ptr).mDeleted = true;
+ sInstances.remove(ptr);
+ }
+
+ public static void nativePollOnce(android.os.MessageQueue queue, long ptr, int timeoutMillis) {
+ var q = getInstance(ptr);
+ synchronized (q.mPoller) {
+ q.mPolling = true;
+ try {
+ if (timeoutMillis == 0) {
+ // Calling epoll_wait() with 0 returns immediately
+ } else if (timeoutMillis == -1) {
+ q.mPoller.wait();
+ } else {
+ q.mPoller.wait(timeoutMillis);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ q.mPolling = false;
+ }
+ }
+
+ public static void nativeWake(long ptr) {
+ var q = getInstance(ptr);
+ synchronized (q.mPoller) {
+ q.mPoller.notifyAll();
+ }
+ }
+
+ public static boolean nativeIsPolling(long ptr) {
+ var q = getInstance(ptr);
+ return q.mPolling;
+ }
+
+ public static void nativeSetFileDescriptorEvents(long ptr, int fd, int events) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
index 4a3a79803b65..98d5778f047e 100644
--- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
+++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/Parcel_host.java
@@ -36,7 +36,7 @@ public class Parcel_host {
private Parcel_host() {
}
- private static final AtomicLong sNextId = new AtomicLong(0);
+ private static final AtomicLong sNextId = new AtomicLong(1);
private static final Map<Long, Parcel_host> sInstances = new ConcurrentHashMap<>();