Merge "Update `getFreeformTasksInZOrder` for DesktopModeTaskRepository to be multi-display aware." into main
diff --git a/Ravenwood.bp b/Ravenwood.bp
index 412f2b7..11da20a 100644
--- a/Ravenwood.bp
+++ b/Ravenwood.bp
@@ -284,6 +284,8 @@
         "100-framework-minus-apex.ravenwood",
         "200-kxml2-android",
 
+        "ravenwood-runtime-common-ravenwood",
+
         "android.test.mock.ravenwood",
         "ravenwood-helper-runtime",
         "hoststubgen-helper-runtime.ravenwood",
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 515ddc8..a4128a3 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -19,13 +19,16 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.perftests.utils.ShellHelper;
+import android.util.Log;
 
 import java.util.ArrayList;
+import java.util.concurrent.TimeoutException;
 
 // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
 public class BenchmarkRunner {
-
-    private static final long COOL_OFF_PERIOD_MS = 1000;
+    private static final String TAG = BenchmarkRunner.class.getSimpleName();
+    private static final int TIMEOUT_IN_SECONDS = 45;
+    private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90;
 
     private static final int NUM_ITERATIONS = 4;
 
@@ -79,8 +82,7 @@
     }
 
     private void prepareForNextRun() {
-        SystemClock.sleep(COOL_OFF_PERIOD_MS);
-        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
+        waitCoolDownPeriod();
         mStartTimeNs = System.nanoTime();
         mPausedDurationNs = 0;
     }
@@ -102,7 +104,7 @@
      * to avoid unnecessary waiting.
      */
     public void resumeTiming() {
-        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
+        waitCoolDownPeriod();
         resumeTimer();
     }
 
@@ -162,4 +164,58 @@
         }
         return null;
     }
+
+    /** Waits for the CPU cores and the broadcast queue to be idle. */
+    public void waitCoolDownPeriod() {
+        waitForCpuIdle();
+        waitForBroadcastIdle();
+    }
+
+    private void waitForBroadcastIdle() {
+        try {
+            ShellHelper.runShellCommandWithTimeout(
+                    "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECONDS);
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Ending waitForBroadcastIdle because it didn't finish in "
+                    + TIMEOUT_IN_SECONDS + " seconds", e);
+        }
+    }
+
+    private void waitForCpuIdle() {
+        int count = 0;
+        int idleCpuPercentage;
+        while (count++ < TIMEOUT_IN_SECONDS) {
+            idleCpuPercentage = getIdleCpuPercentage();
+            Log.d(TAG, "Waiting for CPU idle #" + count + "=" + idleCpuPercentage + "%");
+            if (idleCpuPercentage > CPU_IDLE_THRESHOLD_PERCENTAGE) {
+                return;
+            }
+            SystemClock.sleep(1000);
+        }
+        Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in "
+                + TIMEOUT_IN_SECONDS + " seconds");
+    }
+
+    private int getIdleCpuPercentage() {
+        String output = ShellHelper.runShellCommand("top -m 1 -n 1");
+
+        String[] tokens = output.split("\\s+");
+
+        float totalCpu = -1;
+        float idleCpu = -1;
+        for (String token : tokens) {
+            if (token.contains("%cpu")) {
+                totalCpu = Float.parseFloat(token.split("%")[0]);
+            } else if (token.contains("%idle")) {
+                idleCpu = Float.parseFloat(token.split("%")[0]);
+            }
+        }
+
+        if (totalCpu < 0 || idleCpu < 0) {
+            Log.e(TAG, "Could not get idle cpu percentage, output=" + output);
+            return -1;
+        }
+
+        return (int) (100 * idleCpu / totalCpu);
+    }
 }
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 762e2af..98ab0c2 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -188,21 +188,6 @@
         }
     }
 
-    /** Tests creating a new user, with wait times between iterations. */
-    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
-    public void createUser_realistic() throws RemoteException {
-        while (mRunner.keepRunning()) {
-            Log.i(TAG, "Starting timer");
-            final int userId = createUserNoFlags();
-
-            mRunner.pauseTiming();
-            Log.i(TAG, "Stopping timer");
-            removeUser(userId);
-            waitCoolDownPeriod();
-            mRunner.resumeTimingForNextIteration();
-        }
-    }
-
     /** Tests creating and starting a new user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void createAndStartUser() throws RemoteException {
@@ -239,7 +224,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -254,7 +238,6 @@
             mRunner.pauseTiming();
             final int userId = createUserNoFlags();
 
-            waitForBroadcastIdle();
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -309,9 +292,6 @@
 
             preStartUser(userId, numberOfIterationsToSkip);
 
-            waitForBroadcastIdle();
-            waitCoolDownPeriod();
-
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -353,9 +333,6 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
 
-            waitForBroadcastIdle();
-            waitCoolDownPeriod();
-
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -420,7 +397,6 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
 
-            waitCoolDownPeriod();
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -454,7 +430,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -466,6 +441,7 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int userId = createUserNoFlags();
+
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -479,27 +455,6 @@
         }
     }
 
-    /** Tests switching to an uninitialized user with wait times between iterations. */
-    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
-    public void switchUser_realistic() throws Exception {
-        while (mRunner.keepRunning()) {
-            mRunner.pauseTiming();
-            final int startUser = ActivityManager.getCurrentUser();
-            final int userId = createUserNoFlags();
-            waitCoolDownPeriod();
-            Log.d(TAG, "Starting timer");
-            mRunner.resumeTiming();
-
-            switchUser(userId);
-
-            mRunner.pauseTiming();
-            Log.d(TAG, "Stopping timer");
-            switchUserNoCheck(startUser);
-            removeUser(userId);
-            mRunner.resumeTimingForNextIteration();
-        }
-    }
-
     /** Tests switching to a previously-started, but no-longer-running, user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser_stopped() throws RemoteException {
@@ -507,6 +462,7 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
+
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -536,7 +492,6 @@
 
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
-            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -562,7 +517,6 @@
                 /* useStaticWallpaper */true);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
-            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -606,7 +560,6 @@
         final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
-            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -614,7 +567,6 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
-            waitForBroadcastIdle();
             switchUserNoCheck(startUser);
             mRunner.resumeTimingForNextIteration();
         }
@@ -631,7 +583,6 @@
                 /* useStaticWallpaper */ true);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
-            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -639,7 +590,6 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
-            waitForBroadcastIdle();
             switchUserNoCheck(startUser);
             mRunner.resumeTimingForNextIteration();
         }
@@ -675,13 +625,11 @@
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void stopUser_realistic() throws RemoteException {
         final int userId = createUserNoFlags();
-        waitCoolDownPeriod();
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             runThenWaitForBroadcasts(userId, ()-> {
                 mIam.startUserInBackground(userId);
             }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
-            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -703,7 +651,7 @@
             final int startUser = mAm.getCurrentUser();
             final int userId = createUserNoFlags();
 
-            waitForBroadcastIdle();
+            mRunner.waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -726,7 +674,7 @@
             final int startUser = ActivityManager.getCurrentUser();
             final int userId = createUserNoFlags();
 
-            waitCoolDownPeriod();
+            mRunner.waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                 mRunner.resumeTiming();
                 Log.d(TAG, "Starting timer");
@@ -752,7 +700,7 @@
                 switchUser(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
 
-            waitForBroadcastIdle();
+            mRunner.waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                 runThenWaitForBroadcasts(userId, () -> {
                     mRunner.resumeTiming();
@@ -781,7 +729,7 @@
                 switchUser(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
 
-            waitCoolDownPeriod();
+            mRunner.waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                 runThenWaitForBroadcasts(userId, () -> {
                     mRunner.resumeTiming();
@@ -827,7 +775,6 @@
             Log.d(TAG, "Stopping timer");
             attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -868,7 +815,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -913,7 +859,6 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
         removeUser(userId);
@@ -965,7 +910,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1030,7 +974,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1071,7 +1014,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1124,7 +1066,6 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
-            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1164,7 +1105,6 @@
             runThenWaitForBroadcasts(userId, () -> {
                 startUserInBackgroundAndWaitForUnlock(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
-            waitCoolDownPeriod();
             mRunner.resumeTiming();
             Log.d(TAG, "Starting timer");
 
@@ -1280,6 +1220,7 @@
      * If lack of success should fail the test, use {@link #switchUser(int)} instead.
      */
     private boolean switchUserNoCheck(int userId) throws RemoteException {
+        mRunner.waitCoolDownPeriod();
         final boolean[] success = {true};
         mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> {
             mAm.switchUser(userId);
@@ -1296,7 +1237,7 @@
      */
     private void stopUserAfterWaitingForBroadcastIdle(int userId)
             throws RemoteException {
-        waitForBroadcastIdle();
+        mRunner.waitCoolDownPeriod();
         stopUser(userId);
     }
 
@@ -1438,6 +1379,8 @@
      */
     private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
             String... actions) {
+        mRunner.waitCoolDownPeriod();
+
         final String unreceivedAction =
                 mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);
 
@@ -1538,28 +1481,4 @@
         assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
         return TextUtils.firstNotEmpty(oldValue, "invalid");
     }
-
-    private void waitForBroadcastIdle() {
-        try {
-            ShellHelper.runShellCommandWithTimeout(
-                    "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
-        } catch (TimeoutException e) {
-            Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
-        }
-    }
-
-    private void sleep(long ms) {
-        try {
-            Thread.sleep(ms);
-        } catch (InterruptedException e) {
-            // Ignore
-        }
-    }
-
-    private void waitCoolDownPeriod() {
-        // Heuristic value based on local tests. Stability increased compared to no waiting.
-        final int tenSeconds = 1000 * 10;
-        waitForBroadcastIdle();
-        sleep(tenSeconds);
-    }
 }
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index fc3870e..998e295 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -26,7 +26,7 @@
  * receive this error from an app, at a minimum, you should
  * recover by resetting the connection. For instance, you should
  * drop the binder, clean up associated state, and reset your
- * connection to the service which through this error. In order
+ * connection to the service which threw this error. In order
  * to simplify your error recovery paths, you may also want to
  * "simply" restart your process. However, this may not be an
  * option if the service you are talking to is unreliable or
@@ -34,9 +34,11 @@
  *
  * If this isn't from a service death and is instead from a
  * low-level binder error, it will be from:
- * - a oneway call queue filling up (too many oneway calls)
- * - from the binder buffer being filled up, so that the transaction
- *   is rejected.
+ * <ul>
+ * <li> a one-way call queue filling up (too many one-way calls)
+ * <li> from the binder buffer being filled up, so that the transaction
+ *      is rejected.
+ * </ul>
  *
  * In these cases, more information about the error will be
  * logged. However, there isn't a good way to differentiate
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 17dfdda..71957ee 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -52,6 +52,8 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.ravenwood.RavenwoodEnvironment;
+
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
@@ -388,7 +390,6 @@
      * new file descriptor shared state such as file position with the
      * original file descriptor.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
     public static ParcelFileDescriptor dup(FileDescriptor orig) throws IOException {
         try {
             final FileDescriptor fd = new FileDescriptor();
@@ -406,7 +407,6 @@
      * new file descriptor shared state such as file position with the
      * original file descriptor.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
     public ParcelFileDescriptor dup() throws IOException {
         if (mWrapped != null) {
             return mWrapped.dup();
@@ -425,7 +425,6 @@
      * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
      * for a dup of the given fd.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
     public static ParcelFileDescriptor fromFd(int fd) throws IOException {
         final FileDescriptor original = new FileDescriptor();
         setFdInt(original, fd);
@@ -485,7 +484,7 @@
      *
      * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Socket.getFileDescriptor$()")
     public static ParcelFileDescriptor fromSocket(Socket socket) {
         FileDescriptor fd = socket.getFileDescriptor$();
         try {
@@ -519,7 +518,7 @@
      *
      * @throws UncheckedIOException if {@link #dup(FileDescriptor)} throws IOException.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "DatagramSocket.getFileDescriptor$()")
     public static ParcelFileDescriptor fromDatagramSocket(DatagramSocket datagramSocket) {
         FileDescriptor fd = datagramSocket.getFileDescriptor$();
         try {
@@ -534,7 +533,6 @@
      * ParcelFileDescriptor in the returned array is the read side; the second
      * is the write side.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
     public static ParcelFileDescriptor[] createPipe() throws IOException {
         try {
             final FileDescriptor[] fds = Os.pipe2(ifAtLeastQ(O_CLOEXEC));
@@ -556,7 +554,6 @@
      * calling {@link #checkError()}, usually after detecting an EOF.
      * This can also be used to detect remote crashes.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
     public static ParcelFileDescriptor[] createReliablePipe() throws IOException {
         try {
             final FileDescriptor[] comm = createCommSocketPair();
@@ -573,7 +570,7 @@
      * Create two ParcelFileDescriptors structured as a pair of sockets
      * connected to each other. The two sockets are indistinguishable.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.socketpair()")
     public static ParcelFileDescriptor[] createSocketPair() throws IOException {
         return createSocketPair(SOCK_STREAM);
     }
@@ -581,7 +578,7 @@
     /**
      * @hide
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.socketpair()")
     public static ParcelFileDescriptor[] createSocketPair(int type) throws IOException {
         try {
             final FileDescriptor fd0 = new FileDescriptor();
@@ -604,7 +601,7 @@
      * calling {@link #checkError()}, usually after detecting an EOF.
      * This can also be used to detect remote crashes.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.socketpair()")
     public static ParcelFileDescriptor[] createReliableSocketPair() throws IOException {
         return createReliableSocketPair(SOCK_STREAM);
     }
@@ -612,7 +609,7 @@
     /**
      * @hide
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.socketpair()")
     public static ParcelFileDescriptor[] createReliableSocketPair(int type) throws IOException {
         try {
             final FileDescriptor[] comm = createCommSocketPair();
@@ -627,7 +624,7 @@
         }
     }
 
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.socketpair()")
     private static FileDescriptor[] createCommSocketPair() throws IOException {
         try {
             // Use SOCK_SEQPACKET so that we have a guarantee that the status
@@ -656,7 +653,7 @@
      */
     @UnsupportedAppUsage
     @Deprecated
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(blockedBy = MemoryFile.class)
     public static ParcelFileDescriptor fromData(byte[] data, String name) throws IOException {
         if (data == null) return null;
         MemoryFile file = new MemoryFile(name, data.length);
@@ -712,7 +709,7 @@
      * @hide
      */
     @TestApi
-    @RavenwoodThrow(reason = "Requires kernel support")
+    @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
     public static File getFile(FileDescriptor fd) throws IOException {
         try {
             final String path = Os.readlink("/proc/self/fd/" + getFdInt(fd));
@@ -744,7 +741,7 @@
      * Return the total size of the file representing this fd, as determined by
      * {@code stat()}. Returns -1 if the fd is not a file.
      */
-    @RavenwoodThrow(reason = "Requires JNI support")
+    @RavenwoodThrow(reason = "Os.readlink() and Os.stat()")
     public long getStatSize() {
         if (mWrapped != null) {
             return mWrapped.getStatSize();
@@ -769,7 +766,6 @@
      * @hide
      */
     @UnsupportedAppUsage
-    @RavenwoodThrow(reason = "Requires JNI support")
     public long seekTo(long pos) throws IOException {
         if (mWrapped != null) {
             return mWrapped.seekTo(pos);
@@ -1037,7 +1033,6 @@
      * take care of calling {@link ParcelFileDescriptor#close
      * ParcelFileDescriptor.close()} for you when the stream is closed.
      */
-    @RavenwoodKeepWholeClass
     public static class AutoCloseInputStream extends FileInputStream {
         private final ParcelFileDescriptor mPfd;
 
@@ -1326,12 +1321,15 @@
 
     }
 
-    @RavenwoodThrow
+    @RavenwoodReplace
     private static boolean isAtLeastQ() {
         return (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q);
     }
 
-    @RavenwoodThrow
+    private static boolean isAtLeastQ$ravenwood() {
+        return RavenwoodEnvironment.workaround().isTargetSdkAtLeastQ();
+    }
+
     private static int ifAtLeastQ(int value) {
         return isAtLeastQ() ? value : 0;
     }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 1fc98cf..a9846fb 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1262,15 +1262,11 @@
                     mHost.getInputMethodManager(), null /* icProto */);
         }
 
-        final var statsToken = (types & ime()) == 0 ? null
-                : ImeTracker.forLogging().onStart(ImeTracker.TYPE_USER,
-                        ImeTracker.ORIGIN_CLIENT,
-                        SoftInputShowHideReason.CONTROL_WINDOW_INSETS_ANIMATION,
-                        mHost.isHandlingPointerEvent() /* fromUser */);
+        // TODO(b/342111149): Create statsToken here once ImeTracker#onStart becomes async.
         controlAnimationUnchecked(types, cancellationSignal, listener, mFrame, fromIme, durationMs,
                 interpolator, animationType,
                 getLayoutInsetsDuringAnimationMode(types, fromPredictiveBack),
-                false /* useInsetsAnimationThread */, statsToken);
+                false /* useInsetsAnimationThread */, null);
     }
 
     private void controlAnimationUnchecked(@InsetsType int types,
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 1494425..8fe1813 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -27,6 +27,7 @@
     public static final String TAG = "RavenwoodEnvironment";
 
     private static RavenwoodEnvironment sInstance = new RavenwoodEnvironment();
+    private static Workaround sWorkaround = new Workaround();
 
     private RavenwoodEnvironment() {
         if (isRunningOnRavenwood()) {
@@ -76,4 +77,30 @@
     private boolean isRunningOnRavenwood$ravenwood() {
         return true;
     }
+
+    /**
+     * See {@link Workaround}. It's only usablke on Ravenwood.
+     */
+    public static Workaround workaround() {
+        if (getInstance().isRunningOnRavenwood()) {
+            return sWorkaround;
+        }
+        throw new IllegalStateException("Workaround can only be used on Ravenwood");
+    }
+
+    /**
+     * A set of APIs used to work around missing features on Ravenwood. Ideally, this class should
+     * be empty, and all its APIs should be able to be implemented properly.
+     */
+    public static class Workaround {
+        Workaround() {
+        }
+
+        /**
+         * @return whether the app's target SDK level is at least Q.
+         */
+        public boolean isTargetSdkAtLeastQ() {
+            return true;
+        }
+    }
 }
diff --git a/libs/WindowManager/Shell/res/drawable/circular_progress.xml b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
index 294b1f0..0d64527 100644
--- a/libs/WindowManager/Shell/res/drawable/circular_progress.xml
+++ b/libs/WindowManager/Shell/res/drawable/circular_progress.xml
@@ -24,7 +24,7 @@
             android:toDegrees="275">
             <shape
                 android:shape="ring"
-                android:thickness="3dp"
+                android:thickness="2dp"
                 android:innerRadius="14dp"
                 android:useLevel="true">
             </shape>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
index 4d06dd3..84e1449 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml
@@ -28,13 +28,11 @@
     <LinearLayout
         android:id="@+id/open_menu_button"
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:tint="?androidprv:attr/materialColorOnSurface"
-        android:background="?android:selectableItemBackground"
+        android:layout_height="40dp"
         android:orientation="horizontal"
         android:clickable="true"
         android:focusable="true"
-        android:paddingStart="12dp">
+        android:layout_marginStart="12dp">
         <ImageView
             android:id="@+id/application_icon"
             android:layout_width="@dimen/desktop_mode_caption_icon_radius"
@@ -85,8 +83,6 @@
         android:layout_height="40dp"
         android:layout_gravity="end"
         android:layout_marginHorizontal="8dp"
-        android:paddingHorizontal="5dp"
-        android:paddingVertical="3dp"
         android:clickable="true"
         android:focusable="true"/>
 
@@ -97,7 +93,6 @@
         android:paddingHorizontal="10dp"
         android:paddingVertical="8dp"
         android:layout_marginEnd="8dp"
-        android:background="?android:selectableItemBackgroundBorderless"
         android:contentDescription="@string/close_button_text"
         android:src="@drawable/desktop_mode_header_ic_close"
         android:scaleType="centerCrop"
diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
index 77507a4..cf1b894 100644
--- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
+++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml
@@ -16,20 +16,28 @@
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
-    <ProgressBar
-        android:id="@+id/progress_bar"
-        style="?android:attr/progressBarStyleHorizontal"
-        android:progressDrawable="@drawable/circular_progress"
-        android:layout_width="34dp"
-        android:layout_height="34dp"
-        android:indeterminate="false"
-        android:visibility="invisible"/>
+
+    <FrameLayout
+        android:layout_width="44dp"
+        android:layout_height="40dp">
+        <ProgressBar
+            android:id="@+id/progress_bar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:progressDrawable="@drawable/circular_progress"
+            android:layout_width="32dp"
+            android:layout_height="32dp"
+            android:indeterminate="false"
+            android:layout_marginHorizontal="6dp"
+            android:layout_marginVertical="4dp"
+            android:visibility="invisible"/>
+    </FrameLayout>
 
     <ImageButton
         android:id="@+id/maximize_window"
-        android:layout_width="34dp"
-        android:layout_height="34dp"
-        android:padding="5dp"
+        android:layout_width="44dp"
+        android:layout_height="40dp"
+        android:paddingHorizontal="10dp"
+        android:paddingVertical="8dp"
         android:contentDescription="@string/maximize_button_text"
         android:src="@drawable/decor_desktop_mode_maximize_button_dark"
         android:scaleType="fitCenter" />
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 8bc3004..f27f46c 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -553,6 +553,25 @@
     <!-- The corner radius of a task that was dragged from fullscreen. -->
     <dimen name="desktop_mode_dragged_task_radius">28dp</dimen>
 
+    <!-- The corner radius of the app chip, maximize and close button's ripple drawable -->
+    <dimen name="desktop_mode_header_buttons_ripple_radius">16dp</dimen>
+    <!-- The vertical inset to apply to the app chip's ripple drawable -->
+    <dimen name="desktop_mode_header_app_chip_ripple_inset_vertical">4dp</dimen>
+
+    <!-- The corner radius of the maximize button's ripple drawable -->
+    <dimen name="desktop_mode_header_maximize_ripple_radius">18dp</dimen>
+    <!-- The vertical inset to apply to the maximize button's ripple drawable -->
+    <dimen name="desktop_mode_header_maximize_ripple_inset_vertical">4dp</dimen>
+    <!-- The horizontal inset to apply to the maximize button's ripple drawable -->
+    <dimen name="desktop_mode_header_maximize_ripple_inset_horizontal">6dp</dimen>
+
+    <!-- The corner radius of the close button's ripple drawable -->
+    <dimen name="desktop_mode_header_close_ripple_radius">18dp</dimen>
+    <!-- The vertical inset to apply to the close button's ripple drawable -->
+    <dimen name="desktop_mode_header_close_ripple_inset_vertical">4dp</dimen>
+    <!-- The horizontal inset to apply to the close button's ripple drawable -->
+    <dimen name="desktop_mode_header_close_ripple_inset_horizontal">6dp</dimen>
+
     <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) -->
     <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item>
     <!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) -->
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 53bdda1..78f0ef7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -28,7 +28,6 @@
 import android.widget.FrameLayout
 import android.widget.ImageButton
 import android.widget.ProgressBar
-import androidx.annotation.ColorInt
 import androidx.core.animation.doOnEnd
 import androidx.core.animation.doOnStart
 import androidx.core.content.ContextCompat
@@ -106,30 +105,17 @@
     fun setAnimationTints(
         darkMode: Boolean,
         iconForegroundColor: ColorStateList? = null,
-        baseForegroundColor: Int? = null
+        baseForegroundColor: Int? = null,
+        rippleDrawable: RippleDrawable? = null
     ) {
         if (Flags.enableThemedAppHeaders()) {
             requireNotNull(iconForegroundColor) { "Icon foreground color must be non-null" }
             requireNotNull(baseForegroundColor) { "Base foreground color must be non-null" }
+            requireNotNull(rippleDrawable) { "Ripple drawable must be non-null" }
             maximizeWindow.imageTintList = iconForegroundColor
-            maximizeWindow.background = RippleDrawable(
-                ColorStateList(
-                    arrayOf(
-                        intArrayOf(android.R.attr.state_hovered),
-                        intArrayOf(android.R.attr.state_pressed),
-                        intArrayOf(),
-                    ),
-                    intArrayOf(
-                        replaceColorAlpha(baseForegroundColor, OPACITY_8),
-                        replaceColorAlpha(baseForegroundColor, OPACITY_12),
-                        Color.TRANSPARENT
-                    )
-                ),
-                null,
-                null
-            )
+            maximizeWindow.background = rippleDrawable
             progressBar.progressTintList = ColorStateList.valueOf(baseForegroundColor)
-                .withAlpha(OPACITY_12)
+                .withAlpha(OPACITY_15)
             progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.TRANSPARENT)
         } else {
             if (darkMode) {
@@ -146,18 +132,7 @@
         }
     }
 
-    @ColorInt
-    private fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int {
-        return Color.argb(
-            alpha,
-            Color.red(color),
-            Color.green(color),
-            Color.blue(color)
-        )
-    }
-
     companion object {
-        private const val OPACITY_8 = 20
-        private const val OPACITY_12 = 31
+        private const val OPACITY_15 = 38
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
index 1c4b742..3c12da2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/DesktopModeAppControlsWindowDecorationViewHolder.kt
@@ -9,6 +9,9 @@
 import android.graphics.Color
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RippleDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.graphics.drawable.shapes.RoundRectShape
 import android.view.View
 import android.view.View.OnLongClickListener
 import android.widget.ImageButton
@@ -46,6 +49,38 @@
         onMaximizeHoverAnimationFinishedListener: () -> Unit
 ) : DesktopModeWindowDecorationViewHolder(rootView) {
 
+    /**
+     * The corner radius to apply to the app chip, maximize and close button's background drawable.
+     **/
+    private val headerButtonsRippleRadius = context.resources
+        .getDimensionPixelSize(R.dimen.desktop_mode_header_buttons_ripple_radius)
+
+    /**
+     * The app chip, maximize and close button's height extends to the top & bottom edges of the
+     * header, and their width may be larger than their height. This is by design to increase the
+     * clickable and hover-able bounds of the view as much as possible. However, to prevent the
+     * ripple drawable from being as large as the views (and asymmetrical), insets are applied to
+     * the background ripple drawable itself to give the appearance of a smaller button
+     * (with padding between itself and the header edges / sibling buttons) but without affecting
+     * its touchable region.
+     */
+    private val appChipDrawableInsets = DrawableInsets(
+        vertical = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_app_chip_ripple_inset_vertical)
+    )
+    private val maximizeDrawableInsets = DrawableInsets(
+        vertical = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_vertical),
+        horizontal = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_maximize_ripple_inset_horizontal)
+    )
+    private val closeDrawableInsets = DrawableInsets(
+        vertical = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_close_ripple_inset_vertical),
+        horizontal = context.resources
+            .getDimensionPixelSize(R.dimen.desktop_mode_header_close_ripple_inset_horizontal)
+    )
+
     private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
     private val captionHandle: View = rootView.requireViewById(R.id.caption_handle)
     private val openMenuButton: View = rootView.requireViewById(R.id.open_menu_button)
@@ -97,7 +132,19 @@
         maximizeWindowButton.imageAlpha = alpha
         closeWindowButton.imageAlpha = alpha
         expandMenuButton.imageAlpha = alpha
-
+        context.withStyledAttributes(
+            set = null,
+            attrs = intArrayOf(
+                android.R.attr.selectableItemBackground,
+                android.R.attr.selectableItemBackgroundBorderless
+            ),
+            defStyleAttr = 0,
+            defStyleRes = 0
+        ) {
+            openMenuButton.background = getDrawable(0)
+            maximizeWindowButton.background = getDrawable(1)
+            closeWindowButton.background = getDrawable(1)
+        }
         maximizeButtonView.setAnimationTints(isDarkMode())
     }
 
@@ -126,18 +173,40 @@
         val foregroundColor = headerStyle.foreground.color
         val foregroundAlpha = headerStyle.foreground.opacity
         val colorStateList = ColorStateList.valueOf(foregroundColor).withAlpha(foregroundAlpha)
-        closeWindowButton.imageTintList = colorStateList
-        expandMenuButton.imageTintList = colorStateList
-        with (appNameTextView) {
-            isVisible = header.type == Header.Type.DEFAULT
-            setTextColor(colorStateList)
+        // App chip.
+        openMenuButton.apply {
+            background = createRippleDrawable(
+                color = foregroundColor,
+                cornerRadius = headerButtonsRippleRadius,
+                drawableInsets = appChipDrawableInsets,
+            )
+            expandMenuButton.imageTintList = colorStateList
+            appNameTextView.apply {
+                isVisible = header.type == Header.Type.DEFAULT
+                setTextColor(colorStateList)
+            }
+            appIconImageView.imageAlpha = foregroundAlpha
         }
-        appIconImageView.imageAlpha = foregroundAlpha
+        // Maximize button.
         maximizeButtonView.setAnimationTints(
             darkMode = header.appTheme == Header.Theme.DARK,
             iconForegroundColor = colorStateList,
-            baseForegroundColor = foregroundColor
+            baseForegroundColor = foregroundColor,
+            rippleDrawable = createRippleDrawable(
+                color = foregroundColor,
+                cornerRadius = headerButtonsRippleRadius,
+                drawableInsets = maximizeDrawableInsets
+            )
         )
+        // Close button.
+        closeWindowButton.apply {
+            imageTintList = colorStateList
+            background = createRippleDrawable(
+                color = foregroundColor,
+                cornerRadius = headerButtonsRippleRadius,
+                drawableInsets = closeDrawableInsets
+            )
+        }
     }
 
     override fun onHandleMenuOpened() {}
@@ -390,10 +459,61 @@
         context.withStyledAttributes(null, intArrayOf(attr), 0, 0) {
             return getColor(0, 0)
         }
-        return Color.BLACK
+        return Color.WHITE
     }
 
-    data class Header(
+    @ColorInt
+    private fun replaceColorAlpha(@ColorInt color: Int, alpha: Int): Int {
+        return Color.argb(
+            alpha,
+            Color.red(color),
+            Color.green(color),
+            Color.blue(color)
+        )
+    }
+
+    private fun createRippleDrawable(
+        @ColorInt color: Int,
+        cornerRadius: Int,
+        drawableInsets: DrawableInsets,
+    ): RippleDrawable {
+        return RippleDrawable(
+            ColorStateList(
+                arrayOf(
+                    intArrayOf(android.R.attr.state_hovered),
+                    intArrayOf(android.R.attr.state_pressed),
+                    intArrayOf(),
+                ),
+                intArrayOf(
+                    replaceColorAlpha(color, OPACITY_11),
+                    replaceColorAlpha(color, OPACITY_15),
+                    Color.TRANSPARENT
+                )
+            ),
+            null /* content */,
+            LayerDrawable(arrayOf(
+                ShapeDrawable().apply {
+                    shape = RoundRectShape(
+                        FloatArray(8) { cornerRadius.toFloat() },
+                        null /* inset */,
+                        null /* innerRadii */
+                    )
+                    paint.color = Color.WHITE
+                }
+            )).apply {
+                require(numberOfLayers == 1) { "Must only contain one layer" }
+                setLayerInset(0 /* index */,
+                    drawableInsets.l, drawableInsets.t, drawableInsets.r, drawableInsets.b)
+            }
+        )
+    }
+
+    private data class DrawableInsets(val l: Int, val t: Int, val r: Int, val b: Int) {
+        constructor(vertical: Int = 0, horizontal: Int = 0) :
+                this(horizontal, vertical, horizontal, vertical)
+    }
+
+    private data class Header(
         val type: Type,
         val systemTheme: Theme,
         val appTheme: Theme,
@@ -408,7 +528,7 @@
 
     private fun Header.Theme.isDark(): Boolean = this == Header.Theme.DARK
 
-    data class HeaderStyle(
+    private data class HeaderStyle(
         val background: Background,
         val foreground: Foreground
     ) {
@@ -497,6 +617,8 @@
         private const val FOCUSED_OPACITY = 255
 
         private const val OPACITY_100 = 255
+        private const val OPACITY_11 = 28
+        private const val OPACITY_15 = 38
         private const val OPACITY_30 = 77
         private const val OPACITY_55 = 140
         private const val OPACITY_65 = 166
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index 838630f..0589c0f12 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -523,7 +523,7 @@
     private final int mConnectionState;
     private final String mClientPackageName;
     private final String mPackageName;
-    private final int mVolumeHandling;
+    @PlaybackVolume private final int mVolumeHandling;
     private final int mVolumeMax;
     private final int mVolume;
     private final String mAddress;
@@ -855,25 +855,7 @@
     }
 
     private void dumpVolume(@NonNull PrintWriter pw, @NonNull String prefix) {
-        String volumeHandlingName;
-
-        switch (mVolumeHandling) {
-            case PLAYBACK_VOLUME_FIXED:
-                volumeHandlingName = "FIXED";
-                break;
-            case PLAYBACK_VOLUME_VARIABLE:
-                volumeHandlingName = "VARIABLE";
-                break;
-            default:
-                volumeHandlingName = "UNKNOWN";
-                break;
-        }
-
-        String volume = String.format(Locale.US,
-                "volume(current=%d, max=%d, handling=%s(%d))",
-                mVolume, mVolumeMax, volumeHandlingName, mVolumeHandling);
-
-        pw.println(prefix + volume);
+        pw.println(prefix + getVolumeString(mVolume, mVolumeMax, mVolumeHandling));
     }
 
     @Override
@@ -936,47 +918,42 @@
     @Override
     public String toString() {
         // Note: mExtras is not printed here.
-        StringBuilder result =
-                new StringBuilder()
-                        .append("MediaRoute2Info{ ")
-                        .append("id=")
-                        .append(getId())
-                        .append(", name=")
-                        .append(getName())
-                        .append(", type=")
-                        .append(getDeviceTypeString(getType()))
-                        .append(", isSystem=")
-                        .append(isSystemRoute())
-                        .append(", features=")
-                        .append(getFeatures())
-                        .append(", iconUri=")
-                        .append(getIconUri())
-                        .append(", description=")
-                        .append(getDescription())
-                        .append(", connectionState=")
-                        .append(getConnectionState())
-                        .append(", clientPackageName=")
-                        .append(getClientPackageName())
-                        .append(", volumeHandling=")
-                        .append(getVolumeHandling())
-                        .append(", volumeMax=")
-                        .append(getVolumeMax())
-                        .append(", volume=")
-                        .append(getVolume())
-                        .append(", address=")
-                        .append(getAddress())
-                        .append(", deduplicationIds=")
-                        .append(String.join(",", getDeduplicationIds()))
-                        .append(", providerId=")
-                        .append(getProviderId())
-                        .append(", isVisibilityRestricted=")
-                        .append(mIsVisibilityRestricted)
-                        .append(", allowedPackages=")
-                        .append(String.join(",", mAllowedPackages))
-                        .append(", suitabilityStatus=")
-                        .append(mSuitabilityStatus)
-                        .append(" }");
-        return result.toString();
+        return new StringBuilder()
+                .append("MediaRoute2Info{ ")
+                .append("id=")
+                .append(getId())
+                .append(", name=")
+                .append(getName())
+                .append(", type=")
+                .append(getDeviceTypeString(getType()))
+                .append(", isSystem=")
+                .append(isSystemRoute())
+                .append(", features=")
+                .append(getFeatures())
+                .append(", iconUri=")
+                .append(getIconUri())
+                .append(", description=")
+                .append(getDescription())
+                .append(", connectionState=")
+                .append(getConnectionState())
+                .append(", clientPackageName=")
+                .append(getClientPackageName())
+                .append(", ")
+                .append(getVolumeString(mVolume, mVolumeMax, mVolumeHandling))
+                .append(", address=")
+                .append(getAddress())
+                .append(", deduplicationIds=")
+                .append(String.join(",", getDeduplicationIds()))
+                .append(", providerId=")
+                .append(getProviderId())
+                .append(", isVisibilityRestricted=")
+                .append(mIsVisibilityRestricted)
+                .append(", allowedPackages=")
+                .append(String.join(",", mAllowedPackages))
+                .append(", suitabilityStatus=")
+                .append(mSuitabilityStatus)
+                .append(" }")
+                .toString();
     }
 
     @Override
@@ -1008,6 +985,36 @@
         dest.writeInt(mSuitabilityStatus);
     }
 
+    /**
+     * Returns a human readable string describing the given volume values.
+     *
+     * @param volume The current volume.
+     * @param maxVolume The maximum volume.
+     * @param volumeHandling The {@link PlaybackVolume}.
+     */
+    /* package */ static String getVolumeString(
+            int volume, int maxVolume, @PlaybackVolume int volumeHandling) {
+        String volumeHandlingName;
+        switch (volumeHandling) {
+            case PLAYBACK_VOLUME_FIXED:
+                volumeHandlingName = "FIXED";
+                break;
+            case PLAYBACK_VOLUME_VARIABLE:
+                volumeHandlingName = "VARIABLE";
+                break;
+            default:
+                volumeHandlingName = "UNKNOWN";
+                break;
+        }
+        return String.format(
+                Locale.US,
+                "volume(current=%d, max=%d, handling=%s(%d))",
+                volume,
+                maxVolume,
+                volumeHandlingName,
+                volumeHandling);
+    }
+
     private static String getDeviceTypeString(@Type int deviceType) {
         switch (deviceType) {
             case TYPE_BUILTIN_SPEAKER:
@@ -1079,7 +1086,7 @@
         private int mConnectionState;
         private String mClientPackageName;
         private String mPackageName;
-        private int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
+        @PlaybackVolume private int mVolumeHandling = PLAYBACK_VOLUME_FIXED;
         private int mVolumeMax;
         private int mVolume;
         private String mAddress;
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index e62d112..8fa0e49 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -402,9 +402,6 @@
     @Nullable
     public RoutingSessionInfo getRoutingSessionForMediaController(MediaController mediaController) {
         MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
-        if (playbackInfo == null) {
-            return null;
-        }
         if (playbackInfo.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL) {
             return getSystemRoutingSession(mediaController.getPackageName());
         }
@@ -959,10 +956,6 @@
     private boolean areSessionsMatched(MediaController mediaController,
             RoutingSessionInfo sessionInfo) {
         MediaController.PlaybackInfo playbackInfo = mediaController.getPlaybackInfo();
-        if (playbackInfo == null) {
-            return false;
-        }
-
         String volumeControlId = playbackInfo.getVolumeControlId();
         if (volumeControlId == null) {
             return false;
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index a3c8b68..a2a16ef 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -107,7 +107,7 @@
     @NonNull
     final List<String> mTransferableRoutes;
 
-    final int mVolumeHandling;
+    @MediaRoute2Info.PlaybackVolume final int mVolumeHandling;
     final int mVolumeMax;
     final int mVolume;
 
@@ -207,9 +207,10 @@
         return controlHints;
     }
 
+    @MediaRoute2Info.PlaybackVolume
     private static int defineVolumeHandling(
             boolean isSystemSession,
-            int volumeHandling,
+            @MediaRoute2Info.PlaybackVolume int volumeHandling,
             List<String> selectedRoutes,
             boolean volumeAdjustmentForRemoteGroupSessions) {
         if (!isSystemSession
@@ -439,9 +440,7 @@
         pw.println(indent + "mSelectableRoutes=" + mSelectableRoutes);
         pw.println(indent + "mDeselectableRoutes=" + mDeselectableRoutes);
         pw.println(indent + "mTransferableRoutes=" + mTransferableRoutes);
-        pw.println(indent + "mVolumeHandling=" + mVolumeHandling);
-        pw.println(indent + "mVolumeMax=" + mVolumeMax);
-        pw.println(indent + "mVolume=" + mVolume);
+        pw.println(indent + MediaRoute2Info.getVolumeString(mVolume, mVolumeMax, mVolumeHandling));
         pw.println(indent + "mControlHints=" + mControlHints);
         pw.println(indent + "mIsSystemSession=" + mIsSystemSession);
         pw.println(indent + "mTransferReason=" + mTransferReason);
@@ -503,41 +502,36 @@
 
     @Override
     public String toString() {
-        StringBuilder result =
-                new StringBuilder()
-                        .append("RoutingSessionInfo{ ")
-                        .append("sessionId=")
-                        .append(getId())
-                        .append(", name=")
-                        .append(getName())
-                        .append(", clientPackageName=")
-                        .append(getClientPackageName())
-                        .append(", selectedRoutes={")
-                        .append(String.join(",", getSelectedRoutes()))
-                        .append("}")
-                        .append(", selectableRoutes={")
-                        .append(String.join(",", getSelectableRoutes()))
-                        .append("}")
-                        .append(", deselectableRoutes={")
-                        .append(String.join(",", getDeselectableRoutes()))
-                        .append("}")
-                        .append(", transferableRoutes={")
-                        .append(String.join(",", getTransferableRoutes()))
-                        .append("}")
-                        .append(", volumeHandling=")
-                        .append(getVolumeHandling())
-                        .append(", volumeMax=")
-                        .append(getVolumeMax())
-                        .append(", volume=")
-                        .append(getVolume())
-                        .append(", transferReason=")
-                        .append(getTransferReason())
-                        .append(", transferInitiatorUserHandle=")
-                        .append(getTransferInitiatorUserHandle())
-                        .append(", transferInitiatorPackageName=")
-                        .append(getTransferInitiatorPackageName())
-                        .append(" }");
-        return result.toString();
+        return new StringBuilder()
+                .append("RoutingSessionInfo{ ")
+                .append("sessionId=")
+                .append(getId())
+                .append(", name=")
+                .append(getName())
+                .append(", clientPackageName=")
+                .append(getClientPackageName())
+                .append(", selectedRoutes={")
+                .append(String.join(",", getSelectedRoutes()))
+                .append("}")
+                .append(", selectableRoutes={")
+                .append(String.join(",", getSelectableRoutes()))
+                .append("}")
+                .append(", deselectableRoutes={")
+                .append(String.join(",", getDeselectableRoutes()))
+                .append("}")
+                .append(", transferableRoutes={")
+                .append(String.join(",", getTransferableRoutes()))
+                .append("}")
+                .append(", ")
+                .append(MediaRoute2Info.getVolumeString(mVolume, mVolumeMax, mVolumeHandling))
+                .append(", transferReason=")
+                .append(getTransferReason())
+                .append(", transferInitiatorUserHandle=")
+                .append(getTransferInitiatorUserHandle())
+                .append(", transferInitiatorPackageName=")
+                .append(getTransferInitiatorPackageName())
+                .append(" }")
+                .toString();
     }
 
     /**
@@ -586,6 +580,7 @@
         private final List<String> mDeselectableRoutes;
         @NonNull
         private final List<String> mTransferableRoutes;
+        @MediaRoute2Info.PlaybackVolume
         private int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
         private int mVolumeMax;
         private int mVolume;
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ac680a9..e940674 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -752,6 +752,7 @@
     plugins: [
         "dagger2-compiler",
     ],
+    strict_mode: false,
 }
 
 // in-place tests which use Robolectric in the tests directory
@@ -771,7 +772,6 @@
     ],
     static_libs: [
         "RoboTestLibraries",
-        "mockito-kotlin2",
     ],
     libs: [
         "android.test.runner",
@@ -787,6 +787,7 @@
     plugins: [
         "dagger2-compiler",
     ],
+    strict_mode: false,
 }
 
 android_ravenwood_test {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
index 99f7d0f..887e349 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenContent.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import javax.inject.Inject
 
 /**
  * Renders the content of the lockscreen.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index cf2e895..e6132c6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -169,6 +169,7 @@
     viewModel: NotificationsPlaceholderViewModel,
     maxScrimTop: () -> Float,
     shouldPunchHoleBehindScrim: Boolean,
+    shouldFillMaxSize: Boolean = true,
     shadeMode: ShadeMode,
     modifier: Modifier = Modifier,
 ) {
@@ -327,14 +328,14 @@
         }
         Box(
             modifier =
-                Modifier.fillMaxSize()
-                    .graphicsLayer {
+                Modifier.graphicsLayer {
                         alpha =
                             if (shouldPunchHoleBehindScrim) {
                                 (expansionFraction / EXPANSION_FOR_MAX_SCRIM_ALPHA).coerceAtMost(1f)
                             } else 1f
                     }
                     .background(MaterialTheme.colorScheme.surface)
+                    .thenIf(shouldFillMaxSize) { Modifier.fillMaxSize() }
                     .debugBackground(viewModel, DEBUG_BOX_COLOR)
         ) {
             NotificationPlaceholder(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index e17a146..ae53d56 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -17,21 +17,31 @@
 package com.android.systemui.notifications.ui.composable
 
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
 import com.android.systemui.notifications.ui.viewmodel.NotificationsShadeSceneViewModel
+import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
@@ -41,36 +51,53 @@
 class NotificationsShadeScene
 @Inject
 constructor(
-    viewModel: NotificationsShadeSceneViewModel,
+    sceneViewModel: NotificationsShadeSceneViewModel,
     private val overlayShadeViewModel: OverlayShadeViewModel,
+    private val shadeHeaderViewModel: ShadeHeaderViewModel,
+    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
+    private val tintedIconManagerFactory: TintedIconManager.Factory,
+    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+    private val statusBarIconController: StatusBarIconController,
+    private val shadeSession: SaveableSession,
+    private val stackScrollView: Lazy<NotificationScrollView>,
     private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
 ) : ComposableScene {
 
     override val key = Scenes.NotificationsShade
 
     override val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
-        viewModel.destinationScenes
+        sceneViewModel.destinationScenes
 
     @Composable
     override fun SceneScope.Content(
         modifier: Modifier,
     ) {
         OverlayShade(
-            viewModel = overlayShadeViewModel,
             modifier = modifier,
+            viewModel = overlayShadeViewModel,
             horizontalArrangement = Arrangement.Start,
             lockscreenContent = lockscreenContent,
         ) {
-            Text(
-                text = "Notifications list",
-                modifier = Modifier.padding(NotificationsShade.Dimensions.Padding)
-            )
-        }
-    }
-}
+            Column {
+                ExpandedShadeHeader(
+                    viewModel = shadeHeaderViewModel,
+                    createTintedIconManager = tintedIconManagerFactory::create,
+                    createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+                    statusBarIconController = statusBarIconController,
+                    modifier = Modifier.padding(horizontal = 16.dp),
+                )
 
-object NotificationsShade {
-    object Dimensions {
-        val Padding = 16.dp
+                NotificationScrollingStack(
+                    shadeSession = shadeSession,
+                    stackScrollView = stackScrollView.get(),
+                    viewModel = notificationsPlaceholderViewModel,
+                    maxScrimTop = { 0f },
+                    shouldPunchHoleBehindScrim = false,
+                    shouldFillMaxSize = false,
+                    shadeMode = ShadeMode.Dual,
+                    modifier = Modifier.width(416.dp),
+                )
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 04f76f5..4d946bf 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -33,9 +33,9 @@
 import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import dagger.Lazy
+import java.util.Optional
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
-import java.util.Optional
 
 @SysUISingleton
 class QuickSettingsShadeScene
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index ce4032a..bebfd85 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -16,7 +16,6 @@
 
 package com.android.keyguard.logging
 
-import android.hardware.biometrics.BiometricSourceType
 import com.android.systemui.biometrics.AuthRippleController
 import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
 import com.android.systemui.log.LogBuffer
@@ -81,6 +80,23 @@
         )
     }
 
+    fun delayShowingTrustAgentError(
+        msg: CharSequence,
+        fpEngaged: Boolean,
+        faceRunning: Boolean,
+    ) {
+        buffer.log(
+            BIO_TAG,
+            LogLevel.DEBUG,
+            {
+                str1 = msg.toString()
+                bool1 = fpEngaged
+                bool2 = faceRunning
+            },
+            { "Delay showing trustAgentError:$str1. fpEngaged:$bool1 faceRunning:$bool2 " }
+        )
+    }
+
     fun logUpdateDeviceEntryIndication(
         animate: Boolean,
         visible: Boolean,
@@ -118,10 +134,9 @@
         )
     }
 
-    fun logDropNonFingerprintMessage(
+    fun logDropFaceMessage(
         message: CharSequence,
         followUpMessage: CharSequence?,
-        biometricSourceType: BiometricSourceType?,
     ) {
         buffer.log(
             KeyguardIndicationController.TAG,
@@ -129,12 +144,8 @@
             {
                 str1 = message.toString()
                 str2 = followUpMessage?.toString()
-                str3 = biometricSourceType?.name
             },
-            {
-                "droppingNonFingerprintMessage message=$str1 " +
-                    "followUpMessage:$str2 biometricSourceType:$str3"
-            }
+            { "droppingFaceMessage message=$str1 followUpMessage:$str2" }
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
index 2e29c3b..7503a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/UdfpsTouchOverlayBinder.kt
@@ -46,15 +46,22 @@
         view.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 launch {
-                    viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
+                        viewModel.shouldHandleTouches.collect { shouldHandleTouches ->
+                            Log.d(
+                                "UdfpsTouchOverlayBinder",
+                                "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+                            )
+                            view.isInvisible = !shouldHandleTouches
+                            udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
+                        }
+                    }
+                    .invokeOnCompletion {
                         Log.d(
                             "UdfpsTouchOverlayBinder",
-                            "[$view]: update shouldHandleTouches=$shouldHandleTouches"
+                            "[$view-detached]: update shouldHandleTouches=false"
                         )
-                        view.isInvisible = !shouldHandleTouches
-                        udfpsOverlayInteractor.setHandleTouches(shouldHandleTouches)
+                        udfpsOverlayInteractor.setHandleTouches(false)
                     }
-                }
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
index ec574d2..a5eafa9 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFingerprintAuthInteractor.kt
@@ -28,6 +28,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filterIsInstance
 import kotlinx.coroutines.flow.flatMapLatest
@@ -43,9 +44,15 @@
     biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
     fingerprintPropertyRepository: FingerprintPropertyRepository,
 ) {
-    /** Whether fingerprint authentication is currently running or not */
+    /**
+     * Whether fingerprint authentication is currently running or not. This does not mean the user
+     * [isEngaged] with the fingerprint.
+     */
     val isRunning: Flow<Boolean> = repository.isRunning
 
+    /** Whether the user is actively engaging with the fingerprint sensor */
+    val isEngaged: StateFlow<Boolean> = repository.isEngaged
+
     /** Provide the current status of fingerprint authentication. */
     val authenticationStatus: Flow<FingerprintAuthenticationStatus> =
         repository.authenticationStatus
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 0ebc92e..b1589da 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -40,9 +40,13 @@
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.buffer
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 
@@ -57,6 +61,9 @@
      */
     val isRunning: Flow<Boolean>
 
+    /** Whether the fingerprint sensor is actively authenticating. */
+    val isEngaged: StateFlow<Boolean>
+
     /**
      * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
      */
@@ -176,6 +183,17 @@
                     mainDispatcher
                 ) // keyguardUpdateMonitor requires registration on main thread.
 
+    override val isEngaged: StateFlow<Boolean> =
+        authenticationStatus
+            .map { it.isEngaged }
+            .filterNotNull()
+            .map { it }
+            .stateIn(
+                scope = scope,
+                started = WhileSubscribed(),
+                initialValue = false,
+            )
+
     // TODO(b/322555228) Remove after consolidating device entry auth messages with BP auth messages
     //  in BiometricStatusRepository
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
index d8b7b4a..e92dec0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FingerprintAuthenticationModels.kt
@@ -19,6 +19,7 @@
 import android.hardware.biometrics.BiometricFingerprintConstants
 import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD
 import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
+import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_UNKNOWN
 import android.hardware.fingerprint.FingerprintManager
 import android.os.SystemClock.elapsedRealtime
 import com.android.systemui.biometrics.shared.model.AuthenticationReason
@@ -26,26 +27,43 @@
 /**
  * Fingerprint authentication status provided by
  * [com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository]
+ *
+ * @isEngaged whether fingerprint is actively engaged by the user. This is distinct from fingerprint
+ * running on the device. Can be null if the status does not have an associated isEngaged state.
  */
-sealed class FingerprintAuthenticationStatus
+sealed class FingerprintAuthenticationStatus(val isEngaged: Boolean?)
 
 /** Fingerprint authentication success status. */
 data class SuccessFingerprintAuthenticationStatus(
     val userId: Int,
     val isStrongBiometric: Boolean,
-) : FingerprintAuthenticationStatus()
+) : FingerprintAuthenticationStatus(isEngaged = false)
 
 /** Fingerprint authentication help message. */
 data class HelpFingerprintAuthenticationStatus(
     val msgId: Int,
     val msg: String?,
-) : FingerprintAuthenticationStatus()
+) : FingerprintAuthenticationStatus(isEngaged = null)
 
 /** Fingerprint acquired message. */
 data class AcquiredFingerprintAuthenticationStatus(
     val authenticationReason: AuthenticationReason,
     val acquiredInfo: Int
-) : FingerprintAuthenticationStatus() {
+) :
+    FingerprintAuthenticationStatus(
+        isEngaged =
+            if (acquiredInfo == FINGERPRINT_ACQUIRED_START) {
+                true
+            } else if (
+                acquiredInfo == FINGERPRINT_ACQUIRED_UNKNOWN ||
+                    acquiredInfo == FINGERPRINT_ACQUIRED_GOOD
+            ) {
+                null
+            } else {
+                // soft errors that indicate fingerprint activity ended
+                false
+            }
+    ) {
 
     val fingerprintCaptureStarted: Boolean = acquiredInfo == FINGERPRINT_ACQUIRED_START
 
@@ -53,7 +71,8 @@
 }
 
 /** Fingerprint authentication failed message. */
-data object FailFingerprintAuthenticationStatus : FingerprintAuthenticationStatus()
+data object FailFingerprintAuthenticationStatus :
+    FingerprintAuthenticationStatus(isEngaged = false)
 
 /** Fingerprint authentication error message */
 data class ErrorFingerprintAuthenticationStatus(
@@ -61,7 +80,7 @@
     val msg: String? = null,
     // present to break equality check if the same error occurs repeatedly.
     val createdAt: Long = elapsedRealtime(),
-) : FingerprintAuthenticationStatus() {
+) : FingerprintAuthenticationStatus(isEngaged = false) {
     fun isCancellationError(): Boolean =
         msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_CANCELED ||
             msgId == BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 4c60090..01adab3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -223,7 +223,8 @@
     private Bitmap mScreenBitmap;
     private SaveImageInBackgroundTask mSaveInBgTask;
     private boolean mScreenshotTakenInPortrait;
-    private boolean mBlockAttach;
+    private boolean mAttachRequested;
+    private boolean mDetachRequested;
     private Animator mScreenshotAnimation;
     private RequestCallback mCurrentRequestCallback;
     private ScreenshotActionsProvider mActionsProvider;
@@ -672,7 +673,7 @@
                 () -> {
                     final Intent intent = ActionIntentCreator.INSTANCE.createLongScreenshotIntent(
                             owner, mContext);
-                    mActionIntentExecutor.launchIntentAsync(intent, owner, true, null, null);
+                    mContext.startActivity(intent);
                 },
                 mViewProxy::restoreNonScrollingUi,
                 mViewProxy::startLongScreenshotTransition);
@@ -687,7 +688,7 @@
                     new ViewTreeObserver.OnWindowAttachListener() {
                         @Override
                         public void onWindowAttached() {
-                            mBlockAttach = false;
+                            mAttachRequested = false;
                             decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
                             action.run();
                         }
@@ -703,13 +704,13 @@
     @MainThread
     private void attachWindow() {
         View decorView = mWindow.getDecorView();
-        if (decorView.isAttachedToWindow() || mBlockAttach) {
+        if (decorView.isAttachedToWindow() || mAttachRequested) {
             return;
         }
         if (DEBUG_WINDOW) {
             Log.d(TAG, "attachWindow");
         }
-        mBlockAttach = true;
+        mAttachRequested = true;
         mWindowManager.addView(decorView, mWindowLayoutParams);
         decorView.requestApplyInsets();
 
@@ -727,6 +728,11 @@
                 Log.d(TAG, "Removing screenshot window");
             }
             mWindowManager.removeViewImmediate(decorView);
+            mDetachRequested = false;
+        }
+        if (mAttachRequested && !mDetachRequested) {
+            mDetachRequested = true;
+            withWindowAttached(this::removeWindow);
         }
 
         mViewProxy.stopInputListening();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 53c10a3..fe16fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -49,11 +49,13 @@
     sharedNotificationContainerInteractor: SharedNotificationContainerInteractor,
     shadeRepository: ShadeRepository,
 ) : BaseShadeInteractor {
+    override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
+
     override val shadeExpansion: StateFlow<Float> =
-        sceneBasedExpansion(sceneInteractor, Scenes.Shade)
+        sceneBasedExpansion(sceneInteractor, notificationsScene)
             .stateIn(scope, SharingStarted.Eagerly, 0f)
 
-    private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, Scenes.QuickSettings)
+    private val sceneBasedQsExpansion = sceneBasedExpansion(sceneInteractor, quickSettingsScene)
 
     override val qsExpansion: StateFlow<Float> =
         combine(
@@ -81,7 +83,7 @@
                 when (state) {
                     is ObservableTransitionState.Idle -> false
                     is ObservableTransitionState.Transition ->
-                        state.toScene == Scenes.QuickSettings && state.fromScene != Scenes.Shade
+                        state.toScene == quickSettingsScene && state.fromScene != notificationsScene
                 }
             }
             .distinctUntilChanged()
@@ -90,7 +92,7 @@
         sceneInteractor.transitionState
             .map { state ->
                 when (state) {
-                    is ObservableTransitionState.Idle -> state.currentScene == Scenes.QuickSettings
+                    is ObservableTransitionState.Idle -> state.currentScene == quickSettingsScene
                     is ObservableTransitionState.Transition -> false
                 }
             }
@@ -106,12 +108,10 @@
             .stateIn(scope, SharingStarted.Eagerly, false)
 
     override val isUserInteractingWithShade: Flow<Boolean> =
-        sceneBasedInteracting(sceneInteractor, Scenes.Shade)
+        sceneBasedInteracting(sceneInteractor, notificationsScene)
 
     override val isUserInteractingWithQs: Flow<Boolean> =
-        sceneBasedInteracting(sceneInteractor, Scenes.QuickSettings)
-
-    override val shadeMode: StateFlow<ShadeMode> = shadeRepository.shadeMode
+        sceneBasedInteracting(sceneInteractor, quickSettingsScene)
 
     /**
      * Returns a flow that uses scene transition progress to and from a scene that is pulled down
@@ -154,4 +154,20 @@
                 }
             }
             .distinctUntilChanged()
+
+    private val notificationsScene: SceneKey
+        get() =
+            if (shadeMode.value is ShadeMode.Dual) {
+                Scenes.NotificationsShade
+            } else {
+                Scenes.Shade
+            }
+
+    private val quickSettingsScene: SceneKey
+        get() =
+            if (shadeMode.value is ShadeMode.Dual) {
+                Scenes.QuickSettingsShade
+            } else {
+                Scenes.QuickSettings
+            }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 8d8a36a..47939ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -98,6 +98,8 @@
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.keyguard.KeyguardIndication;
@@ -183,11 +185,14 @@
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private KeyguardInteractor mKeyguardInteractor;
     private final BiometricMessageInteractor mBiometricMessageInteractor;
+    private DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
+    private DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
     private String mPersistentUnlockMessage;
     private String mAlignmentIndication;
     private boolean mForceIsDismissible;
     private CharSequence mTrustGrantedIndication;
     private CharSequence mTransientIndication;
+    private CharSequence mTrustAgentErrorMessage;
     private CharSequence mBiometricMessage;
     private CharSequence mBiometricMessageFollowUp;
     private BiometricSourceType mBiometricMessageSource;
@@ -235,6 +240,13 @@
     final Consumer<Set<Integer>> mCoExAcquisitionMsgIdsToShowCallback =
             (Set<Integer> coExFaceAcquisitionMsgIdsToShow) -> mCoExFaceAcquisitionMsgIdsToShow =
                     coExFaceAcquisitionMsgIdsToShow;
+    @VisibleForTesting
+    final Consumer<Boolean> mIsFingerprintEngagedCallback =
+            (Boolean isEngaged) -> {
+                if (!isEngaged) {
+                    showTrustAgentErrorMessage(mTrustAgentErrorMessage);
+                }
+            };
     private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() {
         @Override
         public void onScreenTurnedOn() {
@@ -295,7 +307,9 @@
             FeatureFlags flags,
             IndicationHelper indicationHelper,
             KeyguardInteractor keyguardInteractor,
-            BiometricMessageInteractor biometricMessageInteractor
+            BiometricMessageInteractor biometricMessageInteractor,
+            DeviceEntryFingerprintAuthInteractor deviceEntryFingerprintAuthInteractor,
+            DeviceEntryFaceAuthInteractor deviceEntryFaceAuthInteractor
     ) {
         mContext = context;
         mBroadcastDispatcher = broadcastDispatcher;
@@ -325,6 +339,8 @@
         mIndicationHelper = indicationHelper;
         mKeyguardInteractor = keyguardInteractor;
         mBiometricMessageInteractor = biometricMessageInteractor;
+        mDeviceEntryFingerprintAuthInteractor = deviceEntryFingerprintAuthInteractor;
+        mDeviceEntryFaceAuthInteractor = deviceEntryFaceAuthInteractor;
 
         mFaceAcquiredMessageDeferral = faceHelpMessageDeferral.create();
 
@@ -409,6 +425,8 @@
         collectFlow(mIndicationArea,
                 mBiometricMessageInteractor.getCoExFaceAcquisitionMsgIdsToShow(),
                 mCoExAcquisitionMsgIdsToShowCallback);
+        collectFlow(mIndicationArea, mDeviceEntryFingerprintAuthInteractor.isEngaged(),
+                mIsFingerprintEngagedCallback);
     }
 
     /**
@@ -944,19 +962,25 @@
 
         if (!isSuccessMessage
                 && mBiometricMessageSource == FINGERPRINT
-                && biometricSourceType != FINGERPRINT) {
-            // drop all non-fingerprint biometric messages if there's a fingerprint message showing
-            mKeyguardLogger.logDropNonFingerprintMessage(
+                && biometricSourceType == FACE) {
+            // drop any face messages if there's a fingerprint message showing
+            mKeyguardLogger.logDropFaceMessage(
                     biometricMessage,
-                    biometricMessageFollowUp,
-                    biometricSourceType
+                    biometricMessageFollowUp
             );
             return;
         }
 
-        mBiometricMessage = biometricMessage;
-        mBiometricMessageFollowUp = biometricMessageFollowUp;
-        mBiometricMessageSource = biometricSourceType;
+        if (mBiometricMessageSource != null && biometricSourceType == null) {
+            // If there's a current biometric message showing and a non-biometric message
+            // arrives, update the followup message with the non-biometric message.
+            // Keep the biometricMessage and biometricMessageSource the same.
+            mBiometricMessageFollowUp = biometricMessage;
+        } else {
+            mBiometricMessage = biometricMessage;
+            mBiometricMessageFollowUp = biometricMessageFollowUp;
+            mBiometricMessageSource = biometricSourceType;
+        }
 
         mHandler.removeMessages(MSG_SHOW_ACTION_TO_UNLOCK);
         hideBiometricMessageDelayed(
@@ -1455,7 +1479,7 @@
 
         @Override
         public void onTrustAgentErrorMessage(CharSequence message) {
-            showBiometricMessage(message, null);
+            showTrustAgentErrorMessage(message);
         }
 
         @Override
@@ -1467,6 +1491,10 @@
                 hideBiometricMessage();
                 mBiometricErrorMessageToShowOnScreenOn = null;
             }
+
+            if (!running && biometricSourceType == FACE) {
+                showTrustAgentErrorMessage(mTrustAgentErrorMessage);
+            }
         }
 
         @Override
@@ -1533,6 +1561,25 @@
         return getCurrentUser() == userId;
     }
 
+    /**
+     * Only show trust agent messages after biometrics are no longer active.
+     */
+    private void showTrustAgentErrorMessage(CharSequence message) {
+        if (message == null) {
+            mTrustAgentErrorMessage = null;
+            return;
+        }
+        boolean fpEngaged = mDeviceEntryFingerprintAuthInteractor.isEngaged().getValue();
+        boolean faceRunning = mDeviceEntryFaceAuthInteractor.isRunning();
+        if (fpEngaged || faceRunning) {
+            mKeyguardLogger.delayShowingTrustAgentError(message, fpEngaged, faceRunning);
+            mTrustAgentErrorMessage = message;
+        } else {
+            mTrustAgentErrorMessage = null;
+            showBiometricMessage(message, null);
+        }
+    }
+
     protected void showTrustGrantedMessage(boolean dismissKeyguard, @Nullable String message) {
         mTrustGrantedIndication = message;
         updateDeviceEntryIndication(false);
@@ -1639,6 +1686,7 @@
             new KeyguardStateController.Callback() {
         @Override
         public void onUnlockedChanged() {
+            mTrustAgentErrorMessage = null;
             updateDeviceEntryIndication(false);
         }
 
@@ -1649,6 +1697,7 @@
                 mKeyguardLogger.log(TAG, LogLevel.DEBUG, "clear messages");
                 mTopIndicationView.clearMessages();
                 mRotateTextViewController.clearMessages();
+                mTrustAgentErrorMessage = null;
             } else {
                 updateDeviceEntryIndication(false);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index 6137381..c2ce114 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.notification.stack.ui.viewmodel
 
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -25,6 +26,7 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimClipping
 import com.android.systemui.statusbar.notification.stack.shared.model.ShadeScrimShape
@@ -34,6 +36,7 @@
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flowOf
@@ -73,16 +76,16 @@
                     }
                     is ObservableTransitionState.Transition -> {
                         if (
-                            (transitionState.fromScene == Scenes.Shade &&
-                                transitionState.toScene == Scenes.QuickSettings) ||
-                                (transitionState.fromScene == Scenes.QuickSettings &&
-                                    transitionState.toScene == Scenes.Shade)
+                            (transitionState.fromScene == notificationsScene &&
+                                transitionState.toScene == quickSettingsScene) ||
+                                (transitionState.fromScene == quickSettingsScene &&
+                                    transitionState.toScene == notificationsScene)
                         ) {
                             1f
                         } else if (
                             (transitionState.fromScene == Scenes.Gone ||
                                 transitionState.fromScene == Scenes.Lockscreen) &&
-                                transitionState.toScene == Scenes.QuickSettings
+                                transitionState.toScene == quickSettingsScene
                         ) {
                             // during QS expansion, increase fraction at same rate as scrim alpha,
                             // but start when scrim alpha is at EXPANSION_FOR_DELAYED_STACK_FADE_IN.
@@ -152,7 +155,9 @@
 
     /** Whether the notification stack is scrollable or not. */
     val isScrollable: Flow<Boolean> =
-        sceneInteractor.currentScene.map { it == Scenes.Shade }.dumpWhileCollecting("isScrollable")
+        sceneInteractor.currentScene
+            .map { it == notificationsScene }
+            .dumpWhileCollecting("isScrollable")
 
     /** Whether the notification stack is displayed in doze mode. */
     val isDozing: Flow<Boolean> by lazy {
@@ -162,4 +167,22 @@
             keyguardInteractor.get().isDozing.dumpWhileCollecting("isDozing")
         }
     }
+
+    private val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode
+
+    private val notificationsScene: SceneKey
+        get() =
+            if (shadeMode.value is ShadeMode.Dual) {
+                Scenes.NotificationsShade
+            } else {
+                Scenes.Shade
+            }
+
+    private val quickSettingsScene: SceneKey
+        get() =
+            if (shadeMode.value is ShadeMode.Dual) {
+                Scenes.QuickSettingsShade
+            } else {
+                Scenes.QuickSettings
+            }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 12f252d..9d9cc2a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.statusbar.pipeline.satellite.data.prod
 
 import android.os.OutcomeReceiver
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
 import android.telephony.satellite.NtnSignalStrengthCallback
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
@@ -38,6 +40,7 @@
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Unknown
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.time.SystemClock
 import java.util.Optional
 import javax.inject.Inject
@@ -51,12 +54,15 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.suspendCancellableCoroutine
@@ -92,13 +98,19 @@
 
     @OptIn(ExperimentalCoroutinesApi::class)
     companion object {
-        /** Convenience function to switch to the supported flow */
+        /**
+         * Convenience function to switch to the supported flow. [retrySignal] is a flow that emits
+         * [Unit] whenever the [supported] flow needs to be restarted
+         */
         fun <T> Flow<SatelliteSupport>.whenSupported(
             supported: (SatelliteManager) -> Flow<T>,
             orElse: Flow<T>,
-        ): Flow<T> = flatMapLatest {
-            when (it) {
-                is Supported -> supported(it.satelliteManager)
+            retrySignal: Flow<Unit>,
+        ): Flow<T> = flatMapLatest { satelliteSupport ->
+            when (satelliteSupport) {
+                is Supported -> {
+                    retrySignal.flatMapLatest { supported(satelliteSupport.satelliteManager) }
+                }
                 else -> orElse
             }
         }
@@ -132,6 +144,7 @@
 @Inject
 constructor(
     satelliteManagerOpt: Optional<SatelliteManager>,
+    telephonyManager: TelephonyManager,
     @Background private val bgDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
     @OemSatelliteInputLog private val logBuffer: LogBuffer,
@@ -201,11 +214,65 @@
         }
     }
 
+    /**
+     * Note that we are given an "unbound" [TelephonyManager] (meaning it was not created with a
+     * specific `subscriptionId`). Therefore this is the radio power state of the
+     * DEFAULT_SUBSCRIPTION_ID subscription. This subscription, I am led to believe, is the one that
+     * would be used for the SatelliteManager subscription.
+     *
+     * By watching power state changes, we can detect if the telephony process crashes.
+     *
+     * See b/337258696 for details
+     */
+    private val radioPowerState: StateFlow<Int> =
+        conflatedCallbackFlow {
+                val cb =
+                    object : TelephonyCallback(), TelephonyCallback.RadioPowerStateListener {
+                        override fun onRadioPowerStateChanged(powerState: Int) {
+                            trySend(powerState)
+                        }
+                    }
+
+                telephonyManager.registerTelephonyCallback(bgDispatcher.asExecutor(), cb)
+
+                awaitClose { telephonyManager.unregisterTelephonyCallback(cb) }
+            }
+            .flowOn(bgDispatcher)
+            .stateIn(
+                scope,
+                SharingStarted.WhileSubscribed(),
+                TelephonyManager.RADIO_POWER_UNAVAILABLE
+            )
+
+    /**
+     * In the event that a telephony phone process has crashed, we expect to see a radio power state
+     * change from ON to something else. This trigger can be used to re-start a flow via
+     * [whenSupported]
+     *
+     * This flow emits [Unit] when started so that newly-started collectors always run, and only
+     * restart when the state goes from ON -> !ON
+     */
+    private val telephonyProcessCrashedEvent: Flow<Unit> =
+        radioPowerState
+            .pairwise()
+            .mapNotNull { (prev: Int, new: Int) ->
+                if (
+                    prev == TelephonyManager.RADIO_POWER_ON &&
+                        new != TelephonyManager.RADIO_POWER_ON
+                ) {
+                    Unit
+                } else {
+                    null
+                }
+            }
+            .onStart { emit(Unit) }
+
     override val connectionState =
         satelliteSupport
             .whenSupported(
                 supported = ::connectionStateFlow,
-                orElse = flowOf(SatelliteConnectionState.Off)
+                orElse = flowOf(SatelliteConnectionState.Off),
+                retrySignal = telephonyProcessCrashedEvent,
             )
             .stateIn(scope, SharingStarted.Eagerly, SatelliteConnectionState.Off)
 
@@ -232,7 +299,11 @@
 
     override val signalStrength =
         satelliteSupport
-            .whenSupported(supported = ::signalStrengthFlow, orElse = flowOf(0))
+            .whenSupported(
+                supported = ::signalStrengthFlow,
+                orElse = flowOf(0),
+                retrySignal = telephonyProcessCrashedEvent,
+            )
             .stateIn(scope, SharingStarted.Eagerly, 0)
 
     // By using the SupportedSatelliteManager here, we expect registration never to fail
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index fe066ca2..59678a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -20,14 +20,19 @@
 
 import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX;
 import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
+import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT;
 import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -65,6 +70,8 @@
 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.deviceentry.domain.interactor.BiometricMessageInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFingerprintAuthInteractor;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.keyguard.KeyguardIndication;
@@ -150,6 +157,10 @@
     @Mock
     protected BiometricMessageInteractor mBiometricMessageInteractor;
     @Mock
+    protected DeviceEntryFaceAuthInteractor mDeviceEntryFaceAuthInteractor;
+    @Mock
+    protected DeviceEntryFingerprintAuthInteractor mDeviceEntryFingerprintAuthInteractor;
+    @Mock
     protected ScreenLifecycle mScreenLifecycle;
     @Mock
     protected AuthController mAuthController;
@@ -237,6 +248,7 @@
                 .thenReturn(mock(StateFlow.class));
 
         when(mFaceHelpMessageDeferralFactory.create()).thenReturn(mFaceHelpMessageDeferral);
+        when(mDeviceEntryFingerprintAuthInteractor.isEngaged()).thenReturn(mock(StateFlow.class));
 
         mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor);
 
@@ -279,7 +291,9 @@
                 mFlags,
                 mIndicationHelper,
                 KeyguardInteractorFactory.create(mFlags).getKeyguardInteractor(),
-                mBiometricMessageInteractor
+                mBiometricMessageInteractor,
+                mDeviceEntryFingerprintAuthInteractor,
+                mDeviceEntryFaceAuthInteractor
         );
         mController.init();
         mController.setIndicationArea(mIndicationArea);
@@ -306,4 +320,22 @@
         mExecutor.runAllReady();
         reset(mRotateTextViewController);
     }
+
+    void verifyNoMessage(int type) {
+        if (type == INDICATION_TYPE_TRANSIENT) {
+            verify(mRotateTextViewController, never()).showTransient(anyString());
+        } else {
+            verify(mRotateTextViewController, never()).updateIndication(eq(type),
+                    any(KeyguardIndication.class), anyBoolean());
+        }
+    }
+
+    void verifyIndicationShown(int indicationType, String message) {
+        verify(mRotateTextViewController)
+                .updateIndication(eq(indicationType),
+                        mKeyguardIndicationCaptor.capture(),
+                        eq(true));
+        assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
+                .isEqualTo(message);
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index cfe9e2a..80011dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -1514,19 +1514,6 @@
     }
 
     @Test
-    public void onTrustAgentErrorMessageDroppedBecauseFingerprintMessageShowing() {
-        createController();
-        mController.setVisible(true);
-        mController.getKeyguardCallback().onBiometricHelp(BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED,
-                "fp not recognized", BiometricSourceType.FINGERPRINT);
-        clearInvocations(mRotateTextViewController);
-
-        mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage");
-        verifyNoMessage(INDICATION_TYPE_TRUST);
-        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
-    }
-
-    @Test
     public void trustGrantedMessageShowsEvenWhenFingerprintMessageShowing() {
         createController();
         mController.setVisible(true);
@@ -1591,24 +1578,6 @@
         verify(mRotateTextViewController).showTransient(eq(message));
     }
 
-    private void verifyNoMessage(int type) {
-        if (type == INDICATION_TYPE_TRANSIENT) {
-            verify(mRotateTextViewController, never()).showTransient(anyString());
-        } else {
-            verify(mRotateTextViewController, never()).updateIndication(eq(type),
-                    anyObject(), anyBoolean());
-        }
-    }
-
-    private void verifyIndicationShown(int indicationType, String message) {
-        verify(mRotateTextViewController)
-                .updateIndication(eq(indicationType),
-                        mKeyguardIndicationCaptor.capture(),
-                        eq(true));
-        assertThat(mKeyguardIndicationCaptor.getValue().getMessage().toString())
-                .isEqualTo(message);
-    }
-
     private void fingerprintUnlockIsNotPossible() {
         setupFingerprintUnlockPossible(false);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
index 4a14f88..a68ba06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt
@@ -21,12 +21,17 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP
+import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -62,6 +67,33 @@
             verify(mIndicationArea, times(3)).visibility = View.VISIBLE
         }
 
+    @Test
+    fun onTrustAgentErrorMessageDelayed_fingerprintEngaged() {
+        createController()
+        mController.setVisible(true)
+
+        // GIVEN fingerprint is engaged
+        whenever(mDeviceEntryFingerprintAuthInteractor.isEngaged).thenReturn(MutableStateFlow(true))
+
+        // WHEN a trust agent error message arrives
+        mKeyguardUpdateMonitorCallback.onTrustAgentErrorMessage("testMessage")
+        mExecutor.runAllReady()
+
+        // THEN no message shows immediately since fingerprint is engaged
+        verifyNoMessage(INDICATION_TYPE_TRUST)
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE)
+        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP)
+
+        // WHEN fingerprint is no longer engaged
+        whenever(mDeviceEntryFingerprintAuthInteractor.isEngaged)
+            .thenReturn(MutableStateFlow(false))
+        mController.mIsFingerprintEngagedCallback.accept(false)
+        mExecutor.runAllReady()
+
+        // THEN the message will show
+        verifyIndicationShown(INDICATION_TYPE_BIOMETRIC_MESSAGE, "testMessage")
+    }
+
     companion object {
         private val IMMEDIATE = Dispatchers.Main.immediate
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
index 7ca3b1c..6300953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.satellite.data
 
+import android.telephony.TelephonyManager
 import android.telephony.satellite.SatelliteManager
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -50,11 +51,13 @@
     private val demoModeController =
         mock<DemoModeController>().apply { whenever(this.isInDemoMode).thenReturn(false) }
     private val satelliteManager = mock<SatelliteManager>()
+    private val telephonyManager = mock<TelephonyManager>()
     private val systemClock = FakeSystemClock()
 
     private val realImpl =
         DeviceBasedSatelliteRepositoryImpl(
             Optional.of(satelliteManager),
+            telephonyManager,
             testDispatcher,
             testScope.backgroundScope,
             FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 6b0ad4b..6651676 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -18,6 +18,8 @@
 
 import android.os.OutcomeReceiver
 import android.os.Process
+import android.telephony.TelephonyCallback
+import android.telephony.TelephonyManager
 import android.telephony.satellite.NtnSignalStrength
 import android.telephony.satellite.NtnSignalStrengthCallback
 import android.telephony.satellite.SatelliteManager
@@ -36,6 +38,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.MIN_UPTIME
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -59,6 +62,7 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.doAnswer
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -69,6 +73,7 @@
     private lateinit var underTest: DeviceBasedSatelliteRepositoryImpl
 
     @Mock private lateinit var satelliteManager: SatelliteManager
+    @Mock private lateinit var telephonyManager: TelephonyManager
 
     private val systemClock = FakeSystemClock()
     private val dispatcher = StandardTestDispatcher()
@@ -86,6 +91,7 @@
             underTest =
                 DeviceBasedSatelliteRepositoryImpl(
                     Optional.empty(),
+                    telephonyManager,
                     dispatcher,
                     testScope.backgroundScope,
                     FakeLogBuffer.Factory.create(),
@@ -362,6 +368,68 @@
             verify(satelliteManager).registerForModemStateChanged(any(), any())
         }
 
+    @Test
+    fun telephonyCrash_repoReregistersConnectionStateListener() =
+        testScope.runTest {
+            setupDefaultRepo()
+
+            // GIVEN connection state is requested
+            val connectionState by collectLastValue(underTest.connectionState)
+
+            runCurrent()
+
+            val telephonyCallback =
+                MobileTelephonyHelpers.getTelephonyCallbackForType<
+                    TelephonyCallback.RadioPowerStateListener
+                >(
+                    telephonyManager
+                )
+
+            // THEN listener is registered once
+            verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
+
+            // WHEN a crash event happens (detected by radio state change)
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+            runCurrent()
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+            runCurrent()
+
+            // THEN listeners are unregistered and re-registered
+            verify(satelliteManager, times(1)).unregisterForModemStateChanged(any())
+            verify(satelliteManager, times(2)).registerForModemStateChanged(any(), any())
+        }
+
+    @Test
+    fun telephonyCrash_repoReregistersSignalStrengthListener() =
+        testScope.runTest {
+            setupDefaultRepo()
+
+            // GIVEN signal strength is requested
+            val signalStrength by collectLastValue(underTest.signalStrength)
+
+            runCurrent()
+
+            val telephonyCallback =
+                MobileTelephonyHelpers.getTelephonyCallbackForType<
+                    TelephonyCallback.RadioPowerStateListener
+                >(
+                    telephonyManager
+                )
+
+            // THEN listeners are registered the first time
+            verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
+
+            // WHEN a crash event happens (detected by radio state change)
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_ON)
+            runCurrent()
+            telephonyCallback.onRadioPowerStateChanged(TelephonyManager.RADIO_POWER_OFF)
+            runCurrent()
+
+            // THEN listeners are unregistered and re-registered
+            verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any())
+            verify(satelliteManager, times(2)).registerForNtnSignalStrengthChanged(any(), any())
+        }
+
     private fun setUpRepo(
         uptime: Long = MIN_UPTIME,
         satMan: SatelliteManager? = satelliteManager,
@@ -380,6 +448,7 @@
         underTest =
             DeviceBasedSatelliteRepositoryImpl(
                 if (satMan != null) Optional.of(satMan) else Optional.empty(),
+                telephonyManager,
                 dispatcher,
                 testScope.backgroundScope,
                 FakeLogBuffer.Factory.create(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 93e0b41..d558c96 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -40,6 +40,9 @@
     private val _isRunning = MutableStateFlow(false)
     override val isRunning: Flow<Boolean>
         get() = _isRunning
+
+    override val isEngaged: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
     fun setIsRunning(value: Boolean) {
         _isRunning.value = value
     }
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 95cbb6b..48bc803 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -56,11 +56,52 @@
     visibility: ["//visibility:public"],
 }
 
+// This and the next module contain the same classes with different implementations.
+// "ravenwood-runtime-common-device" will be statically linked in device side tests.
+// "ravenwood-runtime-common-ravenwood" will only exist in ravenwood-runtime, which will take
+// precedence even if the test jar (accidentally) contains "ravenwood-runtime-common-device".
+// "ravenwood-runtime-common" uses it to detect if the rutime is Ravenwood or not.
+java_library {
+    name: "ravenwood-runtime-common-ravenwood",
+    host_supported: true,
+    sdk_version: "core_current",
+    srcs: [
+        "runtime-common-ravenwood-src/**/*.java",
+    ],
+    visibility: ["//frameworks/base"],
+}
+
+java_library {
+    name: "ravenwood-runtime-common-device",
+    host_supported: true,
+    sdk_version: "core_current",
+    srcs: [
+        "runtime-common-device-src/**/*.java",
+    ],
+    visibility: ["//visibility:private"],
+}
+
+java_library {
+    name: "ravenwood-runtime-common",
+    host_supported: true,
+    sdk_version: "core_current",
+    srcs: [
+        "runtime-common-src/**/*.java",
+    ],
+    libs: [
+        "ravenwood-runtime-common-ravenwood",
+    ],
+    visibility: ["//visibility:private"],
+}
+
 java_library_host {
     name: "ravenwood-helper-libcore-runtime.host",
     srcs: [
         "runtime-helper-src/libcore-fake/**/*.java",
     ],
+    static_libs: [
+        "ravenwood-runtime-common",
+    ],
     visibility: ["//visibility:private"],
 }
 
@@ -77,6 +118,9 @@
     srcs: [
         "runtime-helper-src/framework/**/*.java",
     ],
+    static_libs: [
+        "ravenwood-runtime-common",
+    ],
     libs: [
         "framework-minus-apex.ravenwood",
         "ravenwood-junit",
@@ -105,6 +149,7 @@
     ],
     static_libs: [
         "androidx.test.monitor-for-device",
+        "ravenwood-runtime-common",
     ],
     libs: [
         "android.test.mock",
@@ -145,6 +190,10 @@
         "junit-flag-src/**/*.java",
     ],
     sdk_version: "test_current",
+    static_libs: [
+        "ravenwood-runtime-common",
+        "ravenwood-runtime-common-device",
+    ],
     libs: [
         "junit",
         "flag-junit",
@@ -199,7 +248,7 @@
     ],
 
     srcs: [
-        "runtime-helper-src/jni/*.cpp",
+        "runtime-jni/*.cpp",
     ],
 
     shared_libs: [
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 5506a46..49e793f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -86,10 +86,6 @@
                 sPendingUncaughtException.compareAndSet(null, throwable);
             };
 
-    public static boolean isOnRavenwood() {
-        return true;
-    }
-
     public static void init(RavenwoodRule rule) {
         if (ENABLE_UNCAUGHT_EXCEPTION_DETECTION) {
             maybeThrowPendingUncaughtException(false);
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
index 9d12f85..68b5aeb 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java
@@ -29,6 +29,8 @@
 import android.platform.test.annotations.EnabledOnRavenwood;
 import android.platform.test.annotations.IgnoreUnderRavenwood;
 
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
 import org.junit.Assume;
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -54,7 +56,7 @@
  * before a test class is fully initialized.
  */
 public class RavenwoodRule implements TestRule {
-    static final boolean IS_ON_RAVENWOOD = RavenwoodRuleImpl.isOnRavenwood();
+    static final boolean IS_ON_RAVENWOOD = RavenwoodCommonUtils.isOnRavenwood();
 
     /**
      * When probing is enabled, all tests will be unconditionally run on Ravenwood to detect
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index c3786ee..5f1b0c2 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -16,6 +16,8 @@
 
 package android.platform.test.ravenwood;
 
+import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
+
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
@@ -101,6 +103,8 @@
         setValue("ro.soc.model", "Ravenwood");
 
         setValue("ro.debuggable", "1");
+
+        setValue(RAVENWOOD_SYSPROP, "1");
     }
 
     /** Copy constructor */
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
index 99ab327..19c1bff 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodUtils.java
@@ -15,9 +15,7 @@
  */
 package android.platform.test.ravenwood;
 
-import java.io.File;
-import java.io.PrintStream;
-import java.util.Arrays;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 /**
  * Utilities for writing (bivalent) ravenwood tests.
@@ -26,15 +24,6 @@
     private RavenwoodUtils() {
     }
 
-    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
-
-    // LibcoreRavenwoodUtils calls it with reflections.
-    public static void loadRavenwoodNativeRuntime() {
-        if (RavenwoodRule.isOnRavenwood()) {
-            RavenwoodUtils.loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
-        }
-    }
-
     /**
      * Load a JNI library respecting {@code java.library.path}
      * (which reflects {@code LD_LIBRARY_PATH}).
@@ -56,85 +45,6 @@
      * it uses {@code JNI_OnLoad()} as the entry point name on both.
      */
     public static void loadJniLibrary(String libname) {
-        if (RavenwoodRule.isOnRavenwood()) {
-            loadLibraryOnRavenwood(libname);
-        } else {
-            // Just delegate to the loadLibrary().
-            System.loadLibrary(libname);
-        }
-    }
-
-    private static void loadLibraryOnRavenwood(String libname) {
-        var path = System.getProperty("java.library.path");
-        var filename = "lib" + libname + ".so";
-
-        System.out.println("Looking for library " + libname + ".so in java.library.path:" + path);
-
-        try {
-            if (path == null) {
-                throw new UnsatisfiedLinkError("Cannot load library " + libname + "."
-                        + " Property java.library.path not set!");
-            }
-            for (var dir : path.split(":")) {
-                var file = new File(dir + "/" + filename);
-                if (file.exists()) {
-                    System.load(file.getAbsolutePath());
-                    return;
-                }
-            }
-            throw new UnsatisfiedLinkError("Library " + libname + " not found in "
-                    + "java.library.path: " + path);
-        } catch (Throwable e) {
-            dumpFiles(System.out);
-            throw e;
-        }
-    }
-
-    private static void dumpFiles(PrintStream out) {
-        try {
-            var path = System.getProperty("java.library.path");
-            out.println("# java.library.path=" + path);
-
-            for (var dir : path.split(":")) {
-                listFiles(out, new File(dir), "");
-
-                var gparent = new File((new File(dir)).getAbsolutePath() + "../../..")
-                        .getCanonicalFile();
-                if (gparent.getName().contains("testcases")) {
-                    // Special case: if we found this directory, dump its contents too.
-                    listFiles(out, gparent, "");
-                }
-            }
-
-            var gparent = new File("../..").getCanonicalFile();
-            out.println("# ../..=" + gparent);
-            listFiles(out, gparent, "");
-        } catch (Throwable th) {
-            out.println("Error: " + th.toString());
-            th.printStackTrace(out);
-        }
-    }
-
-    private static void listFiles(PrintStream out, File dir, String prefix) {
-        if (!dir.isDirectory()) {
-            out.println(prefix + dir.getAbsolutePath() + " is not a directory!");
-            return;
-        }
-        out.println(prefix + ":" + dir.getAbsolutePath() + "/");
-        // First, list the files.
-        for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
-            out.println(prefix + "  " + file.getName() + "" + (file.isDirectory() ? "/" : ""));
-        }
-
-        // Then recurse.
-        if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) {
-            // There would be too many files, so don't recurse.
-            return;
-        }
-        for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
-            if (file.isDirectory()) {
-                listFiles(out, file, prefix + "  ");
-            }
-        }
+        RavenwoodCommonUtils.loadJniLibrary(libname);
     }
 }
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 773a89a..483b98a 100644
--- a/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-stub-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -20,10 +20,6 @@
 import org.junit.runners.model.Statement;
 
 public class RavenwoodRuleImpl {
-    public static boolean isOnRavenwood() {
-        return false;
-    }
-
     public static void init(RavenwoodRule rule) {
         // No-op when running on a real device
     }
diff --git a/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java b/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
new file mode 100644
index 0000000..1714716
--- /dev/null
+++ b/ravenwood/runtime-common-device-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common.divergence;
+
+/**
+ * A class that behaves differently on the device side and on Ravenwood, because we have
+ * two build modules with different implementation and we link the different ones at runtime.
+ */
+public final class RavenwoodDivergence {
+    private RavenwoodDivergence() {
+    }
+
+    public static boolean isOnRavenwood() {
+        return false;
+    }
+}
diff --git a/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java b/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
new file mode 100644
index 0000000..59f474a
--- /dev/null
+++ b/ravenwood/runtime-common-ravenwood-src/com/android/ravenwood/common/divergence/RavenwoodDivergence.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common.divergence;
+
+/**
+ * A class that behaves differently on the device side and on Ravenwood, because we have
+ * two build modules with different implementation and we link the different ones at runtime.
+ */
+public final class RavenwoodDivergence {
+    private RavenwoodDivergence() {
+    }
+
+    public static boolean isOnRavenwood() {
+        return true;
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
new file mode 100644
index 0000000..ee28099
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/JvmWorkaround.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+/**
+ * Collection of methods to workaround limitation in the hostside JVM.
+ */
+public abstract class JvmWorkaround {
+    JvmWorkaround() {
+    }
+
+    // We only support OpenJDK for now.
+    private static JvmWorkaround sInstance =
+            RavenwoodCommonUtils.isOnRavenwood() ? new OpenJdkWorkaround() : new NullWorkaround();
+
+    public static JvmWorkaround getInstance() {
+        return sInstance;
+    }
+
+    /**
+     * Equivalent to Android's FileDescriptor.setInt$().
+     */
+    public abstract void setFdInt(FileDescriptor fd, int fdInt);
+
+
+    /**
+     * Equivalent to Android's FileDescriptor.getInt$().
+     */
+    public abstract int getFdInt(FileDescriptor fd);
+
+    /**
+     * Placeholder implementation for the host side.
+     *
+     * Even on the host side, we don't want to throw just because the class is loaded,
+     * which could cause weird random issues, so we throw from individual methods rather
+     * than from the constructor.
+     */
+    private static class NullWorkaround extends JvmWorkaround {
+        private RuntimeException calledOnHostside() {
+            throw new RuntimeException("This method shouldn't be called on the host side");
+        }
+
+        @Override
+        public void setFdInt(FileDescriptor fd, int fdInt) {
+            throw calledOnHostside();
+        }
+
+        @Override
+        public int getFdInt(FileDescriptor fd) {
+            throw calledOnHostside();
+        }
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
new file mode 100644
index 0000000..9aedaab
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/OpenJdkWorkaround.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+class OpenJdkWorkaround extends JvmWorkaround {
+    @Override
+    public void setFdInt(FileDescriptor fd, int fdInt) {
+        try {
+            final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+                    "getJavaIOFileDescriptorAccess").invoke(null);
+            Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+                    "set", FileDescriptor.class, int.class).invoke(obj, fd, fdInt);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+                    + " perhaps JRE has changed?", e);
+        }
+    }
+
+    @Override
+    public int getFdInt(FileDescriptor fd) {
+        try {
+            final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
+                    "getJavaIOFileDescriptorAccess").invoke(null);
+            return (int) Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
+                    "get", FileDescriptor.class).invoke(obj, fd);
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
+                    + " perhaps JRE has changed?", e);
+        }
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java
new file mode 100644
index 0000000..61d54cb
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodBadIntegrityException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+public class RavenwoodBadIntegrityException extends RavenwoodRuntimeException {
+    public RavenwoodBadIntegrityException(String message) {
+        super(message);
+    }
+
+    public RavenwoodBadIntegrityException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
new file mode 100644
index 0000000..c8cc8d9
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+import com.android.ravenwood.common.divergence.RavenwoodDivergence;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.PrintStream;
+import java.util.Arrays;
+
+public class RavenwoodCommonUtils {
+    private static final String TAG = "RavenwoodCommonUtils";
+
+    private RavenwoodCommonUtils() {
+    }
+
+    private static final Object sLock = new Object();
+
+    /** Name of `libravenwood_runtime` */
+    private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime";
+
+    /** Directory name of `out/host/linux-x86/testcases/ravenwood-runtime` */
+    private static final String RAVENWOOD_RUNTIME_DIR_NAME = "ravenwood-runtime";
+
+    private static boolean sEnableExtraRuntimeCheck =
+            "1".equals(System.getenv("RAVENWOOD_ENABLE_EXTRA_RUNTIME_CHECK"));
+
+    private static final boolean IS_ON_RAVENWOOD = RavenwoodDivergence.isOnRavenwood();
+
+    private static final String RAVEWOOD_RUNTIME_PATH = getRavenwoodRuntimePathInternal();
+
+    public static final String RAVENWOOD_SYSPROP = "ro.is_on_ravenwood";
+
+    // @GuardedBy("sLock")
+    private static boolean sIntegrityChecked = false;
+
+    /**
+     * @return if we're running on Ravenwood.
+     */
+    public static boolean isOnRavenwood() {
+        return IS_ON_RAVENWOOD;
+    }
+
+    /**
+     * Throws if the runtime is not Ravenwood.
+     */
+    public static void ensureOnRavenwood() {
+        if (!isOnRavenwood()) {
+            throw new RavenwoodRuntimeException("This is only supposed to be used on Ravenwood");
+        }
+    }
+
+    /**
+     * @return if the various extra runtime check should be enabled.
+     */
+    public static boolean shouldEnableExtraRuntimeCheck() {
+        return sEnableExtraRuntimeCheck;
+    }
+
+    /**
+     * Load the main runtime JNI library.
+     */
+    public static void loadRavenwoodNativeRuntime() {
+        ensureOnRavenwood();
+        loadJniLibrary(RAVENWOOD_NATIVE_RUNTIME_NAME);
+    }
+
+    /**
+     * Internal implementation of
+     * {@link android.platform.test.ravenwood.RavenwoodUtils#loadJniLibrary(String)}
+     */
+    public static void loadJniLibrary(String libname) {
+        if (RavenwoodCommonUtils.isOnRavenwood()) {
+            loadJniLibraryInternal(libname);
+        } else {
+            System.loadLibrary(libname);
+        }
+    }
+
+    /**
+     * Function equivalent to ART's System.loadLibrary. See RavenwoodUtils for why we need it.
+     */
+    private static void loadJniLibraryInternal(String libname) {
+        var path = System.getProperty("java.library.path");
+        var filename = "lib" + libname + ".so";
+
+        System.out.println("Looking for library " + libname + ".so in java.library.path:" + path);
+
+        try {
+            if (path == null) {
+                throw new UnsatisfiedLinkError("Cannot load library " + libname + "."
+                        + " Property java.library.path not set!");
+            }
+            for (var dir : path.split(":")) {
+                var file = new File(dir + "/" + filename);
+                if (file.exists()) {
+                    System.load(file.getAbsolutePath());
+                    return;
+                }
+            }
+            throw new UnsatisfiedLinkError("Library " + libname + " not found in "
+                    + "java.library.path: " + path);
+        } catch (Throwable e) {
+            dumpFiles(System.out);
+            throw e;
+        }
+    }
+
+    private static void dumpFiles(PrintStream out) {
+        try {
+            var path = System.getProperty("java.library.path");
+            out.println("# java.library.path=" + path);
+
+            for (var dir : path.split(":")) {
+                listFiles(out, new File(dir), "");
+
+                var gparent = new File((new File(dir)).getAbsolutePath() + "../../..")
+                        .getCanonicalFile();
+                if (gparent.getName().contains("testcases")) {
+                    // Special case: if we found this directory, dump its contents too.
+                    listFiles(out, gparent, "");
+                }
+            }
+
+            var gparent = new File("../..").getCanonicalFile();
+            out.println("# ../..=" + gparent);
+            listFiles(out, gparent, "");
+        } catch (Throwable th) {
+            out.println("Error: " + th.toString());
+            th.printStackTrace(out);
+        }
+    }
+
+    private static void listFiles(PrintStream out, File dir, String prefix) {
+        if (!dir.isDirectory()) {
+            out.println(prefix + dir.getAbsolutePath() + " is not a directory!");
+            return;
+        }
+        out.println(prefix + ":" + dir.getAbsolutePath() + "/");
+        // First, list the files.
+        for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
+            out.println(prefix + "  " + file.getName() + "" + (file.isDirectory() ? "/" : ""));
+        }
+
+        // Then recurse.
+        if (dir.getAbsolutePath().startsWith("/usr") || dir.getAbsolutePath().startsWith("/lib")) {
+            // There would be too many files, so don't recurse.
+            return;
+        }
+        for (var file : Arrays.stream(dir.listFiles()).sorted().toList()) {
+            if (file.isDirectory()) {
+                listFiles(out, file, prefix + "  ");
+            }
+        }
+    }
+
+    /**
+     * @return the full directory path that contains the "ravenwood-runtime" files.
+     *
+     * This method throws if called on the device side.
+     */
+    public static String getRavenwoodRuntimePath() {
+        ensureOnRavenwood();
+        return RAVEWOOD_RUNTIME_PATH;
+    }
+
+    private static String getRavenwoodRuntimePathInternal() {
+        if (!isOnRavenwood()) {
+            return null;
+        }
+        var path = System.getProperty("java.library.path");
+
+        System.out.println("Looking for " + RAVENWOOD_RUNTIME_DIR_NAME + " directory"
+                + " in java.library.path:" + path);
+
+        try {
+            if (path == null) {
+                throw new IllegalStateException("java.library.path shouldn't be null");
+            }
+            for (var dir : path.split(":")) {
+
+                // For each path, see if the path contains RAVENWOOD_RUNTIME_DIR_NAME.
+                var d = new File(dir);
+                for (;;) {
+                    if (d.getParent() == null) {
+                        break; // Root dir, stop.
+                    }
+                    if (RAVENWOOD_RUNTIME_DIR_NAME.equals(d.getName())) {
+                        var ret = d.getAbsolutePath() + "/";
+                        System.out.println("Found: " + ret);
+                        return ret;
+                    }
+                    d = d.getParentFile();
+                }
+            }
+            throw new IllegalStateException(RAVENWOOD_RUNTIME_DIR_NAME + " not found");
+        } catch (Throwable e) {
+            dumpFiles(System.out);
+            throw e;
+        }
+    }
+
+    /** Close an {@link AutoCloseable}. */
+    public static void closeQuietly(AutoCloseable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+    }
+
+    /** Close a {@link FileDescriptor}. */
+    public static void closeQuietly(FileDescriptor fd) {
+        var is = new FileInputStream(fd);
+        RavenwoodCommonUtils.closeQuietly(is);
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java
new file mode 100644
index 0000000..7b0cebc
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeException.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+public class RavenwoodRuntimeException extends RuntimeException {
+    public RavenwoodRuntimeException(String message) {
+        super(message);
+    }
+
+    public RavenwoodRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java
new file mode 100644
index 0000000..6540221
--- /dev/null
+++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodRuntimeNative.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.common;
+
+import java.io.FileDescriptor;
+
+/**
+ * Class to host all the JNI methods used in ravenwood runtime.
+ */
+public class RavenwoodRuntimeNative {
+    private RavenwoodRuntimeNative() {
+    }
+
+    static {
+        RavenwoodCommonUtils.ensureOnRavenwood();
+        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
+    }
+
+    public static native void applyFreeFunction(long freeFunction, long nativePtr);
+
+    public static native long nLseek(int fd, long offset, int whence);
+
+    public static native int[] nPipe2(int flags);
+
+    public static native int nDup(int oldfd);
+
+    public static native int nFcntlInt(int fd, int cmd, int arg);
+
+    public static long lseek(FileDescriptor fd, long offset, int whence) {
+        return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
+    }
+
+    public static FileDescriptor[] pipe2(int flags) {
+        var fds = nPipe2(flags);
+        var ret = new FileDescriptor[] {
+                new FileDescriptor(),
+                new FileDescriptor(),
+        };
+        JvmWorkaround.getInstance().setFdInt(ret[0], fds[0]);
+        JvmWorkaround.getInstance().setFdInt(ret[1], fds[1]);
+
+        return ret;
+    }
+
+    public static FileDescriptor dup(FileDescriptor fd) {
+        var fdInt = nDup(JvmWorkaround.getInstance().getFdInt(fd));
+
+        var retFd = new java.io.FileDescriptor();
+        JvmWorkaround.getInstance().setFdInt(retFd, fdInt);
+        return retFd;
+    }
+
+    public static int fcntlInt(FileDescriptor fd, int cmd, int arg) {
+        var fdInt = JvmWorkaround.getInstance().getFdInt(fd);
+
+        return nFcntlInt(fdInt, cmd, arg);
+    }
+}
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
index 2d79914..8fe6853 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/ParcelFileDescriptor_host.java
@@ -26,6 +26,7 @@
 import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.ravenwood.common.JvmWorkaround;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -46,27 +47,11 @@
     private static final Map<FileDescriptor, RandomAccessFile> sActive = new HashMap<>();
 
     public static void native_setFdInt$ravenwood(FileDescriptor fd, int fdInt) {
-        try {
-            final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
-                    "getJavaIOFileDescriptorAccess").invoke(null);
-            Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
-                    "set", FileDescriptor.class, int.class).invoke(obj, fd, fdInt);
-        } catch (ReflectiveOperationException e) {
-            throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
-                    + " perhaps JRE has changed?", e);
-        }
+        JvmWorkaround.getInstance().setFdInt(fd, fdInt);
     }
 
     public static int native_getFdInt$ravenwood(FileDescriptor fd) {
-        try {
-            final Object obj = Class.forName("jdk.internal.access.SharedSecrets").getMethod(
-                    "getJavaIOFileDescriptorAccess").invoke(null);
-            return (int) Class.forName("jdk.internal.access.JavaIOFileDescriptorAccess").getMethod(
-                    "get", FileDescriptor.class).invoke(obj, fd);
-        } catch (ReflectiveOperationException e) {
-            throw new RuntimeException("Failed to interact with raw FileDescriptor internals;"
-                    + " perhaps JRE has changed?", e);
-        }
+        return JvmWorkaround.getInstance().getFdInt(fd);
     }
 
     public static FileDescriptor native_open$ravenwood(File file, int pfdMode) throws IOException {
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
index 68bf922..b00cee0 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/nativesubstitution/RavenwoodEnvironment_host.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 
 import com.android.internal.ravenwood.RavenwoodEnvironment;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 public class RavenwoodEnvironment_host {
     private static final String TAG = RavenwoodEnvironment.TAG;
@@ -39,7 +40,7 @@
             if (sInitialized) {
                 return;
             }
-            Log.w(TAG, "Initializing Ravenwood environment");
+            Log.i(TAG, "Initializing Ravenwood environment");
 
             // Set the default values.
             var sysProps = RavenwoodSystemProperties.DEFAULT_VALUES;
@@ -54,4 +55,4 @@
             sInitialized = true;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 69ff262..e198646 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -15,7 +15,7 @@
  */
 package com.android.platform.test.ravenwood.runtimehelper;
 
-import android.platform.test.ravenwood.RavenwoodUtils;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
 
 import java.io.File;
 import java.lang.reflect.Modifier;
@@ -141,7 +141,7 @@
 
         log("Loading " + LIBANDROID_RUNTIME_NAME + " for '" + libanrdoidClasses + "' and '"
                 + libhwuiClasses + "'");
-        RavenwoodUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
+        RavenwoodCommonUtils.loadJniLibrary(LIBANDROID_RUNTIME_NAME);
     }
 
     /**
diff --git a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp b/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
deleted file mode 100644
index 8e3a21d..0000000
--- a/ravenwood/runtime-helper-src/jni/ravenwood_runtime.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2024 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.
- */
-
-#include <nativehelper/JNIHelp.h>
-#include "jni.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
-
-
-typedef void (*FreeFunction)(void*);
-
-static void NativeAllocationRegistry_applyFreeFunction(JNIEnv*,
-                                                       jclass,
-                                                       jlong freeFunction,
-                                                       jlong ptr) {
-    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
-    FreeFunction nativeFreeFunction
-        = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
-    nativeFreeFunction(nativePtr);
-}
-
-static const JNINativeMethod sMethods_NAR[] =
-{
-    { "applyFreeFunction", "(JJ)V", (void*)NativeAllocationRegistry_applyFreeFunction },
-};
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
-{
-    JNIEnv* env = NULL;
-    jint result = -1;
-
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        ALOGE("GetEnv failed!");
-        return result;
-    }
-    ALOG_ASSERT(env, "Could not retrieve the env!");
-
-    ALOGI("%s: JNI_OnLoad", __FILE__);
-
-    // Initialize the Ravenwood version of NativeAllocationRegistry.
-    // We don't use this JNI on the device side, but if we ever have to do, skip this part.
-#ifndef __ANDROID__
-    int res = jniRegisterNativeMethods(env, "libcore/util/NativeAllocationRegistry",
-            sMethods_NAR, NELEM(sMethods_NAR));
-    if (res < 0) {
-        return res;
-    }
-#endif
-
-    return JNI_VERSION_1_4;
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
index 388156a..843455d 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/ErrnoException.java
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+// [ravenwood] Copied from libcore.
+
 package android.system;
 
 import java.io.IOException;
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
new file mode 100644
index 0000000..e031eb2
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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 android.system;
+
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
+
+import java.io.FileDescriptor;
+
+/**
+ * OS class replacement used on Ravenwood. For now, we just implement APIs as we need them...
+ * TODO(b/340887115): Need a better integration with libcore.
+ */
+public final class Os {
+    private Os() {}
+
+    public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
+        return RavenwoodRuntimeNative.lseek(fd, offset, whence);
+    }
+
+
+    public static FileDescriptor[] pipe2(int flags) throws ErrnoException {
+        return RavenwoodRuntimeNative.pipe2(flags);
+    }
+
+    public static FileDescriptor dup(FileDescriptor fd) throws ErrnoException {
+        return RavenwoodRuntimeNative.dup(fd);
+    }
+
+    public static int fcntlInt(FileDescriptor fd, int cmd, int arg) throws ErrnoException {
+        return RavenwoodRuntimeNative.fcntlInt(fd, cmd, arg);
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
new file mode 100644
index 0000000..c56ec8a
--- /dev/null
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java
@@ -0,0 +1,1259 @@
+/*
+ * Copyright (C) 2011 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 android.system;
+
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+/**
+ * Copied from libcore's version, with the local changes:
+ * - All the imports are removed. (they're only used in javadoc)
+ * - All the annotations are removed.
+ * - The initConstants() method is moved to a nested class.
+ *
+ * TODO(b/340887115): Need a better integration with libcore.
+ */
+
+public class OsConstants {
+//    @UnsupportedAppUsage
+    private OsConstants() {
+    }
+
+    /**
+     * Returns the index of the element in the {@link StructCapUserData} (cap_user_data)
+     * array that this capability is stored in.
+     *
+     * @param x capability
+     * @return index of the element in the {@link StructCapUserData} array storing this capability
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static int CAP_TO_INDEX(int x) { return x >>> 5; }
+
+    /**
+     * Returns the mask for the given capability. This is relative to the capability's
+     * {@link StructCapUserData} (cap_user_data) element, the index of which can be
+     * retrieved with {@link CAP_TO_INDEX}.
+     *
+     * @param x capability
+     * @return mask for given capability
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static int CAP_TO_MASK(int x) { return 1 << (x & 31); }
+
+    /**
+     * Tests whether the given mode is a block device.
+     */
+    public static boolean S_ISBLK(int mode) { return (mode & S_IFMT) == S_IFBLK; }
+
+    /**
+     * Tests whether the given mode is a character device.
+     */
+    public static boolean S_ISCHR(int mode) { return (mode & S_IFMT) == S_IFCHR; }
+
+    /**
+     * Tests whether the given mode is a directory.
+     */
+    public static boolean S_ISDIR(int mode) { return (mode & S_IFMT) == S_IFDIR; }
+
+    /**
+     * Tests whether the given mode is a FIFO.
+     */
+    public static boolean S_ISFIFO(int mode) { return (mode & S_IFMT) == S_IFIFO; }
+
+    /**
+     * Tests whether the given mode is a regular file.
+     */
+    public static boolean S_ISREG(int mode) { return (mode & S_IFMT) == S_IFREG; }
+
+    /**
+     * Tests whether the given mode is a symbolic link.
+     */
+    public static boolean S_ISLNK(int mode) { return (mode & S_IFMT) == S_IFLNK; }
+
+    /**
+     * Tests whether the given mode is a socket.
+     */
+    public static boolean S_ISSOCK(int mode) { return (mode & S_IFMT) == S_IFSOCK; }
+
+    /**
+     * Extracts the exit status of a child. Only valid if WIFEXITED returns true.
+     */
+    public static int WEXITSTATUS(int status) { return (status & 0xff00) >> 8; }
+
+    /**
+     * Tests whether the child dumped core. Only valid if WIFSIGNALED returns true.
+     */
+    public static boolean WCOREDUMP(int status) { return (status & 0x80) != 0; }
+
+    /**
+     * Returns the signal that caused the child to exit. Only valid if WIFSIGNALED returns true.
+     */
+    public static int WTERMSIG(int status) { return status & 0x7f; }
+
+    /**
+     * Returns the signal that cause the child to stop. Only valid if WIFSTOPPED returns true.
+     */
+    public static int WSTOPSIG(int status) { return WEXITSTATUS(status); }
+
+    /**
+     * Tests whether the child exited normally.
+     */
+    public static boolean WIFEXITED(int status) { return (WTERMSIG(status) == 0); }
+
+    /**
+     * Tests whether the child was stopped (not terminated) by a signal.
+     */
+    public static boolean WIFSTOPPED(int status) { return (WTERMSIG(status) == 0x7f); }
+
+    /**
+     * Tests whether the child was terminated by a signal.
+     */
+    public static boolean WIFSIGNALED(int status) { return (WTERMSIG(status + 1) >= 2); }
+
+    public static final int AF_INET = placeholder();
+    public static final int AF_INET6 = placeholder();
+    public static final int AF_NETLINK = placeholder();
+    public static final int AF_PACKET = placeholder();
+    public static final int AF_UNIX = placeholder();
+
+    /**
+     * The virt-vsock address family, linux specific.
+     * It is used with {@code struct sockaddr_vm} from uapi/linux/vm_sockets.h.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int AF_VSOCK = placeholder();
+    public static final int AF_UNSPEC = placeholder();
+    public static final int AI_ADDRCONFIG = placeholder();
+    public static final int AI_ALL = placeholder();
+    public static final int AI_CANONNAME = placeholder();
+    public static final int AI_NUMERICHOST = placeholder();
+    public static final int AI_NUMERICSERV = placeholder();
+    public static final int AI_PASSIVE = placeholder();
+    public static final int AI_V4MAPPED = placeholder();
+    public static final int ARPHRD_ETHER = placeholder();
+
+    /**
+     * The virtio-vsock {@code svmPort} value to bind for any available port.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int VMADDR_PORT_ANY = placeholder();
+
+    /**
+     * The virtio-vsock {@code svmCid} value to listens for all CIDs.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int VMADDR_CID_ANY = placeholder();
+
+    /**
+     * The virtio-vsock {@code svmCid} value for host communication.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int VMADDR_CID_LOCAL = placeholder();
+
+    /**
+     * The virtio-vsock {@code svmCid} value for loopback communication.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/vsock.7.html">vsock(7)</a>
+     * @see VmSocketAddress
+     */
+    public static final int VMADDR_CID_HOST = placeholder();
+
+    /**
+     * ARP protocol loopback device identifier.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int ARPHRD_LOOPBACK = placeholder();
+    public static final int CAP_AUDIT_CONTROL = placeholder();
+    public static final int CAP_AUDIT_WRITE = placeholder();
+    public static final int CAP_BLOCK_SUSPEND = placeholder();
+    public static final int CAP_CHOWN = placeholder();
+    public static final int CAP_DAC_OVERRIDE = placeholder();
+    public static final int CAP_DAC_READ_SEARCH = placeholder();
+    public static final int CAP_FOWNER = placeholder();
+    public static final int CAP_FSETID = placeholder();
+    public static final int CAP_IPC_LOCK = placeholder();
+    public static final int CAP_IPC_OWNER = placeholder();
+    public static final int CAP_KILL = placeholder();
+    public static final int CAP_LAST_CAP = placeholder();
+    public static final int CAP_LEASE = placeholder();
+    public static final int CAP_LINUX_IMMUTABLE = placeholder();
+    public static final int CAP_MAC_ADMIN = placeholder();
+    public static final int CAP_MAC_OVERRIDE = placeholder();
+    public static final int CAP_MKNOD = placeholder();
+    public static final int CAP_NET_ADMIN = placeholder();
+    public static final int CAP_NET_BIND_SERVICE = placeholder();
+    public static final int CAP_NET_BROADCAST = placeholder();
+    public static final int CAP_NET_RAW = placeholder();
+    public static final int CAP_SETFCAP = placeholder();
+    public static final int CAP_SETGID = placeholder();
+    public static final int CAP_SETPCAP = placeholder();
+    public static final int CAP_SETUID = placeholder();
+    public static final int CAP_SYS_ADMIN = placeholder();
+    public static final int CAP_SYS_BOOT = placeholder();
+    public static final int CAP_SYS_CHROOT = placeholder();
+    public static final int CAP_SYSLOG = placeholder();
+    public static final int CAP_SYS_MODULE = placeholder();
+    public static final int CAP_SYS_NICE = placeholder();
+    public static final int CAP_SYS_PACCT = placeholder();
+    public static final int CAP_SYS_PTRACE = placeholder();
+    public static final int CAP_SYS_RAWIO = placeholder();
+    public static final int CAP_SYS_RESOURCE = placeholder();
+    public static final int CAP_SYS_TIME = placeholder();
+    public static final int CAP_SYS_TTY_CONFIG = placeholder();
+    public static final int CAP_WAKE_ALARM = placeholder();
+    public static final int E2BIG = placeholder();
+    public static final int EACCES = placeholder();
+    public static final int EADDRINUSE = placeholder();
+    public static final int EADDRNOTAVAIL = placeholder();
+    public static final int EAFNOSUPPORT = placeholder();
+    public static final int EAGAIN = placeholder();
+    public static final int EAI_AGAIN = placeholder();
+    public static final int EAI_BADFLAGS = placeholder();
+    public static final int EAI_FAIL = placeholder();
+    public static final int EAI_FAMILY = placeholder();
+    public static final int EAI_MEMORY = placeholder();
+    public static final int EAI_NODATA = placeholder();
+    public static final int EAI_NONAME = placeholder();
+    public static final int EAI_OVERFLOW = placeholder();
+    public static final int EAI_SERVICE = placeholder();
+    public static final int EAI_SOCKTYPE = placeholder();
+    public static final int EAI_SYSTEM = placeholder();
+    public static final int EALREADY = placeholder();
+    public static final int EBADF = placeholder();
+    public static final int EBADMSG = placeholder();
+    public static final int EBUSY = placeholder();
+    public static final int ECANCELED = placeholder();
+    public static final int ECHILD = placeholder();
+    public static final int ECONNABORTED = placeholder();
+    public static final int ECONNREFUSED = placeholder();
+    public static final int ECONNRESET = placeholder();
+    public static final int EDEADLK = placeholder();
+    public static final int EDESTADDRREQ = placeholder();
+    public static final int EDOM = placeholder();
+    public static final int EDQUOT = placeholder();
+    public static final int EEXIST = placeholder();
+    public static final int EFAULT = placeholder();
+    public static final int EFBIG = placeholder();
+    public static final int EHOSTUNREACH = placeholder();
+    public static final int EIDRM = placeholder();
+    public static final int EILSEQ = placeholder();
+    public static final int EINPROGRESS = placeholder();
+    public static final int EINTR = placeholder();
+    public static final int EINVAL = placeholder();
+    public static final int EIO = placeholder();
+    public static final int EISCONN = placeholder();
+    public static final int EISDIR = placeholder();
+    public static final int ELOOP = placeholder();
+    public static final int EMFILE = placeholder();
+    public static final int EMLINK = placeholder();
+    public static final int EMSGSIZE = placeholder();
+    public static final int EMULTIHOP = placeholder();
+    public static final int ENAMETOOLONG = placeholder();
+    public static final int ENETDOWN = placeholder();
+    public static final int ENETRESET = placeholder();
+    public static final int ENETUNREACH = placeholder();
+    public static final int ENFILE = placeholder();
+    public static final int ENOBUFS = placeholder();
+    public static final int ENODATA = placeholder();
+    public static final int ENODEV = placeholder();
+    public static final int ENOENT = placeholder();
+    public static final int ENOEXEC = placeholder();
+    public static final int ENOLCK = placeholder();
+    public static final int ENOLINK = placeholder();
+    public static final int ENOMEM = placeholder();
+    public static final int ENOMSG = placeholder();
+    public static final int ENONET = placeholder();
+    public static final int ENOPROTOOPT = placeholder();
+    public static final int ENOSPC = placeholder();
+    public static final int ENOSR = placeholder();
+    public static final int ENOSTR = placeholder();
+    public static final int ENOSYS = placeholder();
+    public static final int ENOTCONN = placeholder();
+    public static final int ENOTDIR = placeholder();
+    public static final int ENOTEMPTY = placeholder();
+    public static final int ENOTSOCK = placeholder();
+    public static final int ENOTSUP = placeholder();
+    public static final int ENOTTY = placeholder();
+    public static final int ENXIO = placeholder();
+    public static final int EOPNOTSUPP = placeholder();
+    public static final int EOVERFLOW = placeholder();
+    public static final int EPERM = placeholder();
+    public static final int EPIPE = placeholder();
+    public static final int EPROTO = placeholder();
+    public static final int EPROTONOSUPPORT = placeholder();
+    public static final int EPROTOTYPE = placeholder();
+    public static final int ERANGE = placeholder();
+    public static final int EROFS = placeholder();
+    public static final int ESPIPE = placeholder();
+    public static final int ESRCH = placeholder();
+    public static final int ESTALE = placeholder();
+    public static final int ETH_P_ALL = placeholder();
+    public static final int ETH_P_ARP = placeholder();
+    public static final int ETH_P_IP = placeholder();
+    public static final int ETH_P_IPV6 = placeholder();
+    public static final int ETIME = placeholder();
+    public static final int ETIMEDOUT = placeholder();
+    public static final int ETXTBSY = placeholder();
+    /**
+     * "Too many users" error.
+     * See <a href="https://man7.org/linux/man-pages/man3/errno.3.html">errno(3)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int EUSERS = placeholder();
+    // On Linux, EWOULDBLOCK == EAGAIN. Use EAGAIN instead, to reduce confusion.
+    public static final int EXDEV = placeholder();
+    public static final int EXIT_FAILURE = placeholder();
+    public static final int EXIT_SUCCESS = placeholder();
+    public static final int FD_CLOEXEC = placeholder();
+    public static final int FIONREAD = placeholder();
+    public static final int F_DUPFD = placeholder();
+    public static final int F_DUPFD_CLOEXEC = placeholder();
+    public static final int F_GETFD = placeholder();
+    public static final int F_GETFL = placeholder();
+    public static final int F_GETLK = placeholder();
+    public static final int F_GETLK64 = placeholder();
+    public static final int F_GETOWN = placeholder();
+    public static final int F_OK = placeholder();
+    public static final int F_RDLCK = placeholder();
+    public static final int F_SETFD = placeholder();
+    public static final int F_SETFL = placeholder();
+    public static final int F_SETLK = placeholder();
+    public static final int F_SETLK64 = placeholder();
+    public static final int F_SETLKW = placeholder();
+    public static final int F_SETLKW64 = placeholder();
+    public static final int F_SETOWN = placeholder();
+    public static final int F_UNLCK = placeholder();
+    public static final int F_WRLCK = placeholder();
+    public static final int ICMP_ECHO = placeholder();
+    public static final int ICMP_ECHOREPLY = placeholder();
+    public static final int ICMP6_ECHO_REQUEST = placeholder();
+    public static final int ICMP6_ECHO_REPLY = placeholder();
+    public static final int IFA_F_DADFAILED = placeholder();
+    public static final int IFA_F_DEPRECATED = placeholder();
+    public static final int IFA_F_HOMEADDRESS = placeholder();
+    public static final int IFA_F_MANAGETEMPADDR = placeholder();
+    public static final int IFA_F_NODAD = placeholder();
+    public static final int IFA_F_NOPREFIXROUTE = placeholder();
+    public static final int IFA_F_OPTIMISTIC = placeholder();
+    public static final int IFA_F_PERMANENT = placeholder();
+    public static final int IFA_F_SECONDARY = placeholder();
+    public static final int IFA_F_TEMPORARY = placeholder();
+    public static final int IFA_F_TENTATIVE = placeholder();
+    public static final int IFF_ALLMULTI = placeholder();
+    public static final int IFF_AUTOMEDIA = placeholder();
+    public static final int IFF_BROADCAST = placeholder();
+    public static final int IFF_DEBUG = placeholder();
+    public static final int IFF_DYNAMIC = placeholder();
+    public static final int IFF_LOOPBACK = placeholder();
+    public static final int IFF_MASTER = placeholder();
+    public static final int IFF_MULTICAST = placeholder();
+    public static final int IFF_NOARP = placeholder();
+    public static final int IFF_NOTRAILERS = placeholder();
+    public static final int IFF_POINTOPOINT = placeholder();
+    public static final int IFF_PORTSEL = placeholder();
+    public static final int IFF_PROMISC = placeholder();
+    public static final int IFF_RUNNING = placeholder();
+    public static final int IFF_SLAVE = placeholder();
+    public static final int IFF_UP = placeholder();
+    public static final int IPPROTO_ICMP = placeholder();
+    public static final int IPPROTO_ICMPV6 = placeholder();
+    public static final int IPPROTO_IP = placeholder();
+    public static final int IPPROTO_IPV6 = placeholder();
+    public static final int IPPROTO_RAW = placeholder();
+    public static final int IPPROTO_TCP = placeholder();
+    public static final int IPPROTO_UDP = placeholder();
+
+    /**
+     * Encapsulation Security Payload protocol
+     *
+     * <p>Defined in /uapi/linux/in.h
+     */
+    public static final int IPPROTO_ESP = placeholder();
+
+    public static final int IPV6_CHECKSUM = placeholder();
+    public static final int IPV6_MULTICAST_HOPS = placeholder();
+    public static final int IPV6_MULTICAST_IF = placeholder();
+    public static final int IPV6_MULTICAST_LOOP = placeholder();
+    public static final int IPV6_PKTINFO = placeholder();
+    public static final int IPV6_RECVDSTOPTS = placeholder();
+    public static final int IPV6_RECVHOPLIMIT = placeholder();
+    public static final int IPV6_RECVHOPOPTS = placeholder();
+    public static final int IPV6_RECVPKTINFO = placeholder();
+    public static final int IPV6_RECVRTHDR = placeholder();
+    public static final int IPV6_RECVTCLASS = placeholder();
+    public static final int IPV6_TCLASS = placeholder();
+    public static final int IPV6_UNICAST_HOPS = placeholder();
+    public static final int IPV6_V6ONLY = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int IP_MULTICAST_ALL = placeholder();
+    public static final int IP_MULTICAST_IF = placeholder();
+    public static final int IP_MULTICAST_LOOP = placeholder();
+    public static final int IP_MULTICAST_TTL = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int IP_RECVTOS = placeholder();
+    public static final int IP_TOS = placeholder();
+    public static final int IP_TTL = placeholder();
+    /**
+     * Version constant to be used in {@link StructCapUserHeader} with
+     * {@link Os#capset(StructCapUserHeader, StructCapUserData[])} and
+     * {@link Os#capget(StructCapUserHeader)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/capget.2.html">capget(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int _LINUX_CAPABILITY_VERSION_3 = placeholder();
+    public static final int MAP_FIXED = placeholder();
+    public static final int MAP_ANONYMOUS = placeholder();
+    /**
+     * Flag argument for {@code mmap(long, long, int, int, FileDescriptor, long)}.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/mmap.2.html">mmap(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int MAP_POPULATE = placeholder();
+    public static final int MAP_PRIVATE = placeholder();
+    public static final int MAP_SHARED = placeholder();
+    public static final int MCAST_JOIN_GROUP = placeholder();
+    public static final int MCAST_LEAVE_GROUP = placeholder();
+    public static final int MCAST_JOIN_SOURCE_GROUP = placeholder();
+    public static final int MCAST_LEAVE_SOURCE_GROUP = placeholder();
+    public static final int MCAST_BLOCK_SOURCE = placeholder();
+    public static final int MCAST_UNBLOCK_SOURCE = placeholder();
+    public static final int MCL_CURRENT = placeholder();
+    public static final int MCL_FUTURE = placeholder();
+    public static final int MFD_CLOEXEC = placeholder();
+    public static final int MSG_CTRUNC = placeholder();
+    public static final int MSG_DONTROUTE = placeholder();
+    public static final int MSG_EOR = placeholder();
+    public static final int MSG_OOB = placeholder();
+    public static final int MSG_PEEK = placeholder();
+    public static final int MSG_TRUNC = placeholder();
+    public static final int MSG_WAITALL = placeholder();
+    public static final int MS_ASYNC = placeholder();
+    public static final int MS_INVALIDATE = placeholder();
+    public static final int MS_SYNC = placeholder();
+    public static final int NETLINK_NETFILTER = placeholder();
+    public static final int NETLINK_ROUTE = placeholder();
+    /**
+     * SELinux enforces that only system_server and netd may use this netlink socket type.
+     */
+    public static final int NETLINK_INET_DIAG = placeholder();
+
+    /**
+     * SELinux enforces that only system_server and netd may use this netlink socket type.
+     *
+     * @see <a href="https://man7.org/linux/man-pages/man7/netlink.7.html">netlink(7)</a>
+     */
+    public static final int NETLINK_XFRM = placeholder();
+
+    public static final int NI_DGRAM = placeholder();
+    public static final int NI_NAMEREQD = placeholder();
+    public static final int NI_NOFQDN = placeholder();
+    public static final int NI_NUMERICHOST = placeholder();
+    public static final int NI_NUMERICSERV = placeholder();
+    public static final int O_ACCMODE = placeholder();
+    public static final int O_APPEND = placeholder();
+    public static final int O_CLOEXEC = placeholder();
+    public static final int O_CREAT = placeholder();
+    /**
+     * Flag for {@code Os#open(String, int, int)}.
+     *
+     * When enabled, tries to minimize cache effects of the I/O to and from this
+     * file. In general this will degrade performance, but it is
+     * useful in special situations, such as when applications do
+     * their own caching. File I/O is done directly to/from
+     * user-space buffers. The {@link O_DIRECT} flag on its own makes an
+     * effort to transfer data synchronously, but does not give
+     * the guarantees of the {@link O_SYNC} flag that data and necessary
+     * metadata are transferred. To guarantee synchronous I/O,
+     * {@link O_SYNC} must be used in addition to {@link O_DIRECT}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int O_DIRECT = placeholder();
+    public static final int O_EXCL = placeholder();
+    public static final int O_NOCTTY = placeholder();
+    public static final int O_NOFOLLOW = placeholder();
+    public static final int O_NONBLOCK = placeholder();
+    public static final int O_RDONLY = placeholder();
+    public static final int O_RDWR = placeholder();
+    public static final int O_SYNC = placeholder();
+    public static final int O_DSYNC = placeholder();
+    public static final int O_TRUNC = placeholder();
+    public static final int O_WRONLY = placeholder();
+    public static final int POLLERR = placeholder();
+    public static final int POLLHUP = placeholder();
+    public static final int POLLIN = placeholder();
+    public static final int POLLNVAL = placeholder();
+    public static final int POLLOUT = placeholder();
+    public static final int POLLPRI = placeholder();
+    public static final int POLLRDBAND = placeholder();
+    public static final int POLLRDNORM = placeholder();
+    public static final int POLLWRBAND = placeholder();
+    public static final int POLLWRNORM = placeholder();
+    /**
+     * Reads or changes the ambient capability set of the calling thread.
+     * Has to be used as a first argument for {@link Os#prctl(int, long, long, long, long)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int PR_CAP_AMBIENT = placeholder();
+    /**
+     * The capability specified in {@code arg3} of {@link Os#prctl(int, long, long, long, long)}
+     * is added to the ambient set. The specified capability must already
+     * be present in both the permitted and the inheritable sets of the process.
+     * Has to be used as a second argument for {@link Os#prctl(int, long, long, long, long)}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/prctl.2.html">prctl(2)</a>.
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int PR_CAP_AMBIENT_RAISE = placeholder();
+    public static final int PR_GET_DUMPABLE = placeholder();
+    public static final int PR_SET_DUMPABLE = placeholder();
+    public static final int PR_SET_NO_NEW_PRIVS = placeholder();
+    public static final int PROT_EXEC = placeholder();
+    public static final int PROT_NONE = placeholder();
+    public static final int PROT_READ = placeholder();
+    public static final int PROT_WRITE = placeholder();
+    public static final int R_OK = placeholder();
+    /**
+     * Specifies a value one greater than the maximum file
+     * descriptor number that can be opened by this process.
+     *
+     * <p>Attempts ({@link Os#open(String, int, int)}, {@link Os#pipe()},
+     * {@link Os#dup(java.io.FileDescriptor)}, etc.) to exceed this
+     * limit yield the error {@link EMFILE}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man3/vlimit.3.html">getrlimit(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int RLIMIT_NOFILE = placeholder();
+    public static final int RT_SCOPE_HOST = placeholder();
+    public static final int RT_SCOPE_LINK = placeholder();
+    public static final int RT_SCOPE_NOWHERE = placeholder();
+    public static final int RT_SCOPE_SITE = placeholder();
+    public static final int RT_SCOPE_UNIVERSE = placeholder();
+    /**
+     * Bitmask for IPv4 addresses add/delete events multicast groups mask.
+     * Used in {@link NetlinkSocketAddress}.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man7/netlink.7.html">netlink(7)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int RTMGRP_IPV4_IFADDR = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV4_MROUTE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV4_ROUTE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV4_RULE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV6_IFADDR = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV6_IFINFO = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV6_MROUTE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV6_PREFIX = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_IPV6_ROUTE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_LINK = placeholder();
+    public static final int RTMGRP_NEIGH = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_NOTIFY = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int RTMGRP_TC = placeholder();
+    public static final int SEEK_CUR = placeholder();
+    public static final int SEEK_END = placeholder();
+    public static final int SEEK_SET = placeholder();
+    public static final int SHUT_RD = placeholder();
+    public static final int SHUT_RDWR = placeholder();
+    public static final int SHUT_WR = placeholder();
+    public static final int SIGABRT = placeholder();
+    public static final int SIGALRM = placeholder();
+    public static final int SIGBUS = placeholder();
+    public static final int SIGCHLD = placeholder();
+    public static final int SIGCONT = placeholder();
+    public static final int SIGFPE = placeholder();
+    public static final int SIGHUP = placeholder();
+    public static final int SIGILL = placeholder();
+    public static final int SIGINT = placeholder();
+    public static final int SIGIO = placeholder();
+    public static final int SIGKILL = placeholder();
+    public static final int SIGPIPE = placeholder();
+    public static final int SIGPROF = placeholder();
+    public static final int SIGPWR = placeholder();
+    public static final int SIGQUIT = placeholder();
+    public static final int SIGRTMAX = placeholder();
+    public static final int SIGRTMIN = placeholder();
+    public static final int SIGSEGV = placeholder();
+    public static final int SIGSTKFLT = placeholder();
+    public static final int SIGSTOP = placeholder();
+    public static final int SIGSYS = placeholder();
+    public static final int SIGTERM = placeholder();
+    public static final int SIGTRAP = placeholder();
+    public static final int SIGTSTP = placeholder();
+    public static final int SIGTTIN = placeholder();
+    public static final int SIGTTOU = placeholder();
+    public static final int SIGURG = placeholder();
+    public static final int SIGUSR1 = placeholder();
+    public static final int SIGUSR2 = placeholder();
+    public static final int SIGVTALRM = placeholder();
+    public static final int SIGWINCH = placeholder();
+    public static final int SIGXCPU = placeholder();
+    public static final int SIGXFSZ = placeholder();
+    public static final int SIOCGIFADDR = placeholder();
+    public static final int SIOCGIFBRDADDR = placeholder();
+    public static final int SIOCGIFDSTADDR = placeholder();
+    public static final int SIOCGIFNETMASK = placeholder();
+
+    /**
+     * Set the close-on-exec ({@code FD_CLOEXEC}) flag on the new file
+     * descriptor created by {@link Os#socket(int,int,int)} or
+     * {@link Os#socketpair(int,int,int,java.io.FileDescriptor,java.io.FileDescriptor)}.
+     * See the description of the O_CLOEXEC flag in
+     * <a href="http://man7.org/linux/man-pages/man2/open.2.html">open(2)</a>
+     * for reasons why this may be useful.
+     *
+     * <p>Applications wishing to make use of this flag on older API versions
+     * may use {@link #O_CLOEXEC} instead. On Android, {@code O_CLOEXEC} and
+     * {@code SOCK_CLOEXEC} are the same value.
+     */
+    public static final int SOCK_CLOEXEC = placeholder();
+    public static final int SOCK_DGRAM = placeholder();
+
+    /**
+     * Set the O_NONBLOCK file status flag on the file descriptor
+     * created by {@link Os#socket(int,int,int)} or
+     * {@link Os#socketpair(int,int,int,java.io.FileDescriptor,java.io.FileDescriptor)}.
+     *
+     * <p>Applications wishing to make use of this flag on older API versions
+     * may use {@link #O_NONBLOCK} instead. On Android, {@code O_NONBLOCK}
+     * and {@code SOCK_NONBLOCK} are the same value.
+     */
+    public static final int SOCK_NONBLOCK = placeholder();
+    public static final int SOCK_RAW = placeholder();
+    public static final int SOCK_SEQPACKET = placeholder();
+    public static final int SOCK_STREAM = placeholder();
+    public static final int SOL_SOCKET = placeholder();
+    public static final int SOL_UDP = placeholder();
+    public static final int SOL_PACKET = placeholder();
+    public static final int SO_BINDTODEVICE = placeholder();
+    public static final int SO_BROADCAST = placeholder();
+    public static final int SO_DEBUG = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int SO_DOMAIN = placeholder();
+    public static final int SO_DONTROUTE = placeholder();
+    public static final int SO_ERROR = placeholder();
+    public static final int SO_KEEPALIVE = placeholder();
+    public static final int SO_LINGER = placeholder();
+    public static final int SO_OOBINLINE = placeholder();
+    public static final int SO_PASSCRED = placeholder();
+    public static final int SO_PEERCRED = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int SO_PROTOCOL = placeholder();
+    public static final int SO_RCVBUF = placeholder();
+    public static final int SO_RCVLOWAT = placeholder();
+    public static final int SO_RCVTIMEO = placeholder();
+    public static final int SO_REUSEADDR = placeholder();
+    public static final int SO_SNDBUF = placeholder();
+    public static final int SO_SNDLOWAT = placeholder();
+    public static final int SO_SNDTIMEO = placeholder();
+    public static final int SO_TYPE = placeholder();
+    public static final int PACKET_IGNORE_OUTGOING = placeholder();
+    /**
+     * Bitmask for flags argument of
+     * {@link splice(java.io.FileDescriptor, Int64Ref , FileDescriptor, Int64Ref, long, int)}.
+     *
+     * Attempt to move pages instead of copying.  This is only a
+     * hint to the kernel: pages may still be copied if the
+     * kernel cannot move the pages from the pipe, or if the pipe
+     * buffers don't refer to full pages.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int SPLICE_F_MOVE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int SPLICE_F_NONBLOCK = placeholder();
+    /**
+     * Bitmask for flags argument of
+     * {@link splice(java.io.FileDescriptor, Int64Ref, FileDescriptor, Int64Ref, long, int)}.
+     *
+     * <p>Indicates that more data will be coming in a subsequent splice. This is
+     * a helpful hint when the {@code fdOut} refers to a socket.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/splice.2.html">splice(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int SPLICE_F_MORE = placeholder();
+    public static final int STDERR_FILENO = placeholder();
+    public static final int STDIN_FILENO = placeholder();
+    public static final int STDOUT_FILENO = placeholder();
+    public static final int ST_MANDLOCK = placeholder();
+    public static final int ST_NOATIME = placeholder();
+    public static final int ST_NODEV = placeholder();
+    public static final int ST_NODIRATIME = placeholder();
+    public static final int ST_NOEXEC = placeholder();
+    public static final int ST_NOSUID = placeholder();
+    public static final int ST_RDONLY = placeholder();
+    public static final int ST_RELATIME = placeholder();
+    public static final int ST_SYNCHRONOUS = placeholder();
+    public static final int S_IFBLK = placeholder();
+    public static final int S_IFCHR = placeholder();
+    public static final int S_IFDIR = placeholder();
+    public static final int S_IFIFO = placeholder();
+    public static final int S_IFLNK = placeholder();
+    public static final int S_IFMT = placeholder();
+    public static final int S_IFREG = placeholder();
+    public static final int S_IFSOCK = placeholder();
+    public static final int S_IRGRP = placeholder();
+    public static final int S_IROTH = placeholder();
+    public static final int S_IRUSR = placeholder();
+    public static final int S_IRWXG = placeholder();
+    public static final int S_IRWXO = placeholder();
+    public static final int S_IRWXU = placeholder();
+    public static final int S_ISGID = placeholder();
+    public static final int S_ISUID = placeholder();
+    public static final int S_ISVTX = placeholder();
+    public static final int S_IWGRP = placeholder();
+    public static final int S_IWOTH = placeholder();
+    public static final int S_IWUSR = placeholder();
+    public static final int S_IXGRP = placeholder();
+    public static final int S_IXOTH = placeholder();
+    public static final int S_IXUSR = placeholder();
+    public static final int TCP_NODELAY = placeholder();
+    public static final int TCP_USER_TIMEOUT = placeholder();
+    public static final int UDP_GRO = placeholder();
+    public static final int UDP_SEGMENT = placeholder();
+    /**
+     * Get the number of bytes in the output buffer.
+     *
+     * See <a href="https://man7.org/linux/man-pages/man2/ioctl.2.html">ioctl(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int TIOCOUTQ = placeholder();
+    /**
+     * Sockopt option to encapsulate ESP packets in UDP.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int UDP_ENCAP = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int UDP_ENCAP_ESPINUDP_NON_IKE = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int UDP_ENCAP_ESPINUDP = placeholder();
+    /** @hide */
+//    @UnsupportedAppUsage
+    public static final int UNIX_PATH_MAX = placeholder();
+    public static final int WCONTINUED = placeholder();
+    public static final int WEXITED = placeholder();
+    public static final int WNOHANG = placeholder();
+    public static final int WNOWAIT = placeholder();
+    public static final int WSTOPPED = placeholder();
+    public static final int WUNTRACED = placeholder();
+    public static final int W_OK = placeholder();
+    /**
+     * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+     *
+     * <p>Performs a pure create, which fails if the named attribute exists already.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int XATTR_CREATE = placeholder();
+    /**
+     * {@code flags} option for {@link Os#setxattr(String, String, byte[], int)}.
+     *
+     * <p>Perform a pure replace operation, which fails if the named attribute
+     * does not already exist.
+     *
+     * See <a href="http://man7.org/linux/man-pages/man2/setxattr.2.html">setxattr(2)</a>.
+     *
+     * @hide
+     */
+//    @UnsupportedAppUsage
+//    @SystemApi(client = MODULE_LIBRARIES)
+    public static final int XATTR_REPLACE = placeholder();
+    public static final int X_OK = placeholder();
+    public static final int _SC_2_CHAR_TERM = placeholder();
+    public static final int _SC_2_C_BIND = placeholder();
+    public static final int _SC_2_C_DEV = placeholder();
+    public static final int _SC_2_C_VERSION = placeholder();
+    public static final int _SC_2_FORT_DEV = placeholder();
+    public static final int _SC_2_FORT_RUN = placeholder();
+    public static final int _SC_2_LOCALEDEF = placeholder();
+    public static final int _SC_2_SW_DEV = placeholder();
+    public static final int _SC_2_UPE = placeholder();
+    public static final int _SC_2_VERSION = placeholder();
+    public static final int _SC_AIO_LISTIO_MAX = placeholder();
+    public static final int _SC_AIO_MAX = placeholder();
+    public static final int _SC_AIO_PRIO_DELTA_MAX = placeholder();
+    public static final int _SC_ARG_MAX = placeholder();
+    public static final int _SC_ASYNCHRONOUS_IO = placeholder();
+    public static final int _SC_ATEXIT_MAX = placeholder();
+    public static final int _SC_AVPHYS_PAGES = placeholder();
+    public static final int _SC_BC_BASE_MAX = placeholder();
+    public static final int _SC_BC_DIM_MAX = placeholder();
+    public static final int _SC_BC_SCALE_MAX = placeholder();
+    public static final int _SC_BC_STRING_MAX = placeholder();
+    public static final int _SC_CHILD_MAX = placeholder();
+    public static final int _SC_CLK_TCK = placeholder();
+    public static final int _SC_COLL_WEIGHTS_MAX = placeholder();
+    public static final int _SC_DELAYTIMER_MAX = placeholder();
+    public static final int _SC_EXPR_NEST_MAX = placeholder();
+    public static final int _SC_FSYNC = placeholder();
+    public static final int _SC_GETGR_R_SIZE_MAX = placeholder();
+    public static final int _SC_GETPW_R_SIZE_MAX = placeholder();
+    public static final int _SC_IOV_MAX = placeholder();
+    public static final int _SC_JOB_CONTROL = placeholder();
+    public static final int _SC_LINE_MAX = placeholder();
+    public static final int _SC_LOGIN_NAME_MAX = placeholder();
+    public static final int _SC_MAPPED_FILES = placeholder();
+    public static final int _SC_MEMLOCK = placeholder();
+    public static final int _SC_MEMLOCK_RANGE = placeholder();
+    public static final int _SC_MEMORY_PROTECTION = placeholder();
+    public static final int _SC_MESSAGE_PASSING = placeholder();
+    public static final int _SC_MQ_OPEN_MAX = placeholder();
+    public static final int _SC_MQ_PRIO_MAX = placeholder();
+    public static final int _SC_NGROUPS_MAX = placeholder();
+    public static final int _SC_NPROCESSORS_CONF = placeholder();
+    public static final int _SC_NPROCESSORS_ONLN = placeholder();
+    public static final int _SC_OPEN_MAX = placeholder();
+    public static final int _SC_PAGESIZE = placeholder();
+    public static final int _SC_PAGE_SIZE = placeholder();
+    public static final int _SC_PASS_MAX = placeholder();
+    public static final int _SC_PHYS_PAGES = placeholder();
+    public static final int _SC_PRIORITIZED_IO = placeholder();
+    public static final int _SC_PRIORITY_SCHEDULING = placeholder();
+    public static final int _SC_REALTIME_SIGNALS = placeholder();
+    public static final int _SC_RE_DUP_MAX = placeholder();
+    public static final int _SC_RTSIG_MAX = placeholder();
+    public static final int _SC_SAVED_IDS = placeholder();
+    public static final int _SC_SEMAPHORES = placeholder();
+    public static final int _SC_SEM_NSEMS_MAX = placeholder();
+    public static final int _SC_SEM_VALUE_MAX = placeholder();
+    public static final int _SC_SHARED_MEMORY_OBJECTS = placeholder();
+    public static final int _SC_SIGQUEUE_MAX = placeholder();
+    public static final int _SC_STREAM_MAX = placeholder();
+    public static final int _SC_SYNCHRONIZED_IO = placeholder();
+    public static final int _SC_THREADS = placeholder();
+    public static final int _SC_THREAD_ATTR_STACKADDR = placeholder();
+    public static final int _SC_THREAD_ATTR_STACKSIZE = placeholder();
+    public static final int _SC_THREAD_DESTRUCTOR_ITERATIONS = placeholder();
+    public static final int _SC_THREAD_KEYS_MAX = placeholder();
+    public static final int _SC_THREAD_PRIORITY_SCHEDULING = placeholder();
+    public static final int _SC_THREAD_PRIO_INHERIT = placeholder();
+    public static final int _SC_THREAD_PRIO_PROTECT = placeholder();
+    public static final int _SC_THREAD_SAFE_FUNCTIONS = placeholder();
+    public static final int _SC_THREAD_STACK_MIN = placeholder();
+    public static final int _SC_THREAD_THREADS_MAX = placeholder();
+    public static final int _SC_TIMERS = placeholder();
+    public static final int _SC_TIMER_MAX = placeholder();
+    public static final int _SC_TTY_NAME_MAX = placeholder();
+    public static final int _SC_TZNAME_MAX = placeholder();
+    public static final int _SC_VERSION = placeholder();
+    public static final int _SC_XBS5_ILP32_OFF32 = placeholder();
+    public static final int _SC_XBS5_ILP32_OFFBIG = placeholder();
+    public static final int _SC_XBS5_LP64_OFF64 = placeholder();
+    public static final int _SC_XBS5_LPBIG_OFFBIG = placeholder();
+    public static final int _SC_XOPEN_CRYPT = placeholder();
+    public static final int _SC_XOPEN_ENH_I18N = placeholder();
+    public static final int _SC_XOPEN_LEGACY = placeholder();
+    public static final int _SC_XOPEN_REALTIME = placeholder();
+    public static final int _SC_XOPEN_REALTIME_THREADS = placeholder();
+    public static final int _SC_XOPEN_SHM = placeholder();
+    public static final int _SC_XOPEN_UNIX = placeholder();
+    public static final int _SC_XOPEN_VERSION = placeholder();
+    public static final int _SC_XOPEN_XCU_VERSION = placeholder();
+
+    /**
+     * Returns the string name of a getaddrinfo(3) error value.
+     * For example, "EAI_AGAIN".
+     */
+    public static String gaiName(int error) {
+        if (error == EAI_AGAIN) {
+            return "EAI_AGAIN";
+        }
+        if (error == EAI_BADFLAGS) {
+            return "EAI_BADFLAGS";
+        }
+        if (error == EAI_FAIL) {
+            return "EAI_FAIL";
+        }
+        if (error == EAI_FAMILY) {
+            return "EAI_FAMILY";
+        }
+        if (error == EAI_MEMORY) {
+            return "EAI_MEMORY";
+        }
+        if (error == EAI_NODATA) {
+            return "EAI_NODATA";
+        }
+        if (error == EAI_NONAME) {
+            return "EAI_NONAME";
+        }
+        if (error == EAI_OVERFLOW) {
+            return "EAI_OVERFLOW";
+        }
+        if (error == EAI_SERVICE) {
+            return "EAI_SERVICE";
+        }
+        if (error == EAI_SOCKTYPE) {
+            return "EAI_SOCKTYPE";
+        }
+        if (error == EAI_SYSTEM) {
+            return "EAI_SYSTEM";
+        }
+        return null;
+    }
+
+    /**
+     * Returns the string name of an errno value.
+     * For example, "EACCES". See {@link Os#strerror} for human-readable errno descriptions.
+     */
+    public static String errnoName(int errno) {
+        if (errno == E2BIG) {
+            return "E2BIG";
+        }
+        if (errno == EACCES) {
+            return "EACCES";
+        }
+        if (errno == EADDRINUSE) {
+            return "EADDRINUSE";
+        }
+        if (errno == EADDRNOTAVAIL) {
+            return "EADDRNOTAVAIL";
+        }
+        if (errno == EAFNOSUPPORT) {
+            return "EAFNOSUPPORT";
+        }
+        if (errno == EAGAIN) {
+            return "EAGAIN";
+        }
+        if (errno == EALREADY) {
+            return "EALREADY";
+        }
+        if (errno == EBADF) {
+            return "EBADF";
+        }
+        if (errno == EBADMSG) {
+            return "EBADMSG";
+        }
+        if (errno == EBUSY) {
+            return "EBUSY";
+        }
+        if (errno == ECANCELED) {
+            return "ECANCELED";
+        }
+        if (errno == ECHILD) {
+            return "ECHILD";
+        }
+        if (errno == ECONNABORTED) {
+            return "ECONNABORTED";
+        }
+        if (errno == ECONNREFUSED) {
+            return "ECONNREFUSED";
+        }
+        if (errno == ECONNRESET) {
+            return "ECONNRESET";
+        }
+        if (errno == EDEADLK) {
+            return "EDEADLK";
+        }
+        if (errno == EDESTADDRREQ) {
+            return "EDESTADDRREQ";
+        }
+        if (errno == EDOM) {
+            return "EDOM";
+        }
+        if (errno == EDQUOT) {
+            return "EDQUOT";
+        }
+        if (errno == EEXIST) {
+            return "EEXIST";
+        }
+        if (errno == EFAULT) {
+            return "EFAULT";
+        }
+        if (errno == EFBIG) {
+            return "EFBIG";
+        }
+        if (errno == EHOSTUNREACH) {
+            return "EHOSTUNREACH";
+        }
+        if (errno == EIDRM) {
+            return "EIDRM";
+        }
+        if (errno == EILSEQ) {
+            return "EILSEQ";
+        }
+        if (errno == EINPROGRESS) {
+            return "EINPROGRESS";
+        }
+        if (errno == EINTR) {
+            return "EINTR";
+        }
+        if (errno == EINVAL) {
+            return "EINVAL";
+        }
+        if (errno == EIO) {
+            return "EIO";
+        }
+        if (errno == EISCONN) {
+            return "EISCONN";
+        }
+        if (errno == EISDIR) {
+            return "EISDIR";
+        }
+        if (errno == ELOOP) {
+            return "ELOOP";
+        }
+        if (errno == EMFILE) {
+            return "EMFILE";
+        }
+        if (errno == EMLINK) {
+            return "EMLINK";
+        }
+        if (errno == EMSGSIZE) {
+            return "EMSGSIZE";
+        }
+        if (errno == EMULTIHOP) {
+            return "EMULTIHOP";
+        }
+        if (errno == ENAMETOOLONG) {
+            return "ENAMETOOLONG";
+        }
+        if (errno == ENETDOWN) {
+            return "ENETDOWN";
+        }
+        if (errno == ENETRESET) {
+            return "ENETRESET";
+        }
+        if (errno == ENETUNREACH) {
+            return "ENETUNREACH";
+        }
+        if (errno == ENFILE) {
+            return "ENFILE";
+        }
+        if (errno == ENOBUFS) {
+            return "ENOBUFS";
+        }
+        if (errno == ENODATA) {
+            return "ENODATA";
+        }
+        if (errno == ENODEV) {
+            return "ENODEV";
+        }
+        if (errno == ENOENT) {
+            return "ENOENT";
+        }
+        if (errno == ENOEXEC) {
+            return "ENOEXEC";
+        }
+        if (errno == ENOLCK) {
+            return "ENOLCK";
+        }
+        if (errno == ENOLINK) {
+            return "ENOLINK";
+        }
+        if (errno == ENOMEM) {
+            return "ENOMEM";
+        }
+        if (errno == ENOMSG) {
+            return "ENOMSG";
+        }
+        if (errno == ENONET) {
+            return "ENONET";
+        }
+        if (errno == ENOPROTOOPT) {
+            return "ENOPROTOOPT";
+        }
+        if (errno == ENOSPC) {
+            return "ENOSPC";
+        }
+        if (errno == ENOSR) {
+            return "ENOSR";
+        }
+        if (errno == ENOSTR) {
+            return "ENOSTR";
+        }
+        if (errno == ENOSYS) {
+            return "ENOSYS";
+        }
+        if (errno == ENOTCONN) {
+            return "ENOTCONN";
+        }
+        if (errno == ENOTDIR) {
+            return "ENOTDIR";
+        }
+        if (errno == ENOTEMPTY) {
+            return "ENOTEMPTY";
+        }
+        if (errno == ENOTSOCK) {
+            return "ENOTSOCK";
+        }
+        if (errno == ENOTSUP) {
+            return "ENOTSUP";
+        }
+        if (errno == ENOTTY) {
+            return "ENOTTY";
+        }
+        if (errno == ENXIO) {
+            return "ENXIO";
+        }
+        if (errno == EOPNOTSUPP) {
+            return "EOPNOTSUPP";
+        }
+        if (errno == EOVERFLOW) {
+            return "EOVERFLOW";
+        }
+        if (errno == EPERM) {
+            return "EPERM";
+        }
+        if (errno == EPIPE) {
+            return "EPIPE";
+        }
+        if (errno == EPROTO) {
+            return "EPROTO";
+        }
+        if (errno == EPROTONOSUPPORT) {
+            return "EPROTONOSUPPORT";
+        }
+        if (errno == EPROTOTYPE) {
+            return "EPROTOTYPE";
+        }
+        if (errno == ERANGE) {
+            return "ERANGE";
+        }
+        if (errno == EROFS) {
+            return "EROFS";
+        }
+        if (errno == ESPIPE) {
+            return "ESPIPE";
+        }
+        if (errno == ESRCH) {
+            return "ESRCH";
+        }
+        if (errno == ESTALE) {
+            return "ESTALE";
+        }
+        if (errno == ETIME) {
+            return "ETIME";
+        }
+        if (errno == ETIMEDOUT) {
+            return "ETIMEDOUT";
+        }
+        if (errno == ETXTBSY) {
+            return "ETXTBSY";
+        }
+        if (errno == EXDEV) {
+            return "EXDEV";
+        }
+        return null;
+    }
+
+    // [ravenwood-change] Moved to a nested class.
+    //    @UnsupportedAppUsage
+    static class Native {
+        private static native void initConstants();
+    }
+
+    // A hack to avoid these constants being inlined by javac...
+//    @UnsupportedAppUsage
+    private static int placeholder() { return 0; }
+    // ...because we want to initialize them at runtime.
+    static {
+        // [ravenwood-change] Load the JNI lib.
+        RavenwoodCommonUtils.loadRavenwoodNativeRuntime();
+        Native.initConstants();
+    }
+}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
deleted file mode 100644
index 839b62a..0000000
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/ravenwood/LibcoreRavenwoodUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2024 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 libcore.ravenwood;
-
-public class LibcoreRavenwoodUtils {
-    private LibcoreRavenwoodUtils() {
-    }
-
-    public static void loadRavenwoodNativeRuntime() {
-        // TODO Stop using reflections.
-        // We need to call RavenwoodUtils.loadRavenwoodNativeRuntime(), but due to the build
-        // structure complexity, we can't refer to to this method directly from here,
-        // so let's use reflections for now...
-        try {
-            final var clazz = Class.forName("android.platform.test.ravenwood.RavenwoodUtils");
-            final var method = clazz.getMethod("loadRavenwoodNativeRuntime");
-            method.invoke(null);
-        } catch (Throwable th) {
-            throw new IllegalStateException("Failed to load Ravenwood native runtime", th);
-        }
-    }
-}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
index 93861e8..14b5a4f 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/libcore/util/NativeAllocationRegistry.java
@@ -15,7 +15,7 @@
  */
 package libcore.util;
 
-import libcore.ravenwood.LibcoreRavenwoodUtils;
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
 
 import java.lang.ref.Cleaner;
 import java.lang.ref.Reference;
@@ -27,11 +27,6 @@
  *   (Should ART switch to java.lang.ref.Cleaner?)
  */
 public class NativeAllocationRegistry {
-    static {
-        // Initialize the JNI method.
-        LibcoreRavenwoodUtils.loadRavenwoodNativeRuntime();
-    }
-
     private final long mFreeFunction;
     private static final Cleaner sCleaner = Cleaner.create();
 
@@ -71,7 +66,7 @@
         }
 
         final Runnable releaser = () -> {
-            applyFreeFunction(mFreeFunction, nativePtr);
+            RavenwoodRuntimeNative.applyFreeFunction(mFreeFunction, nativePtr);
         };
         sCleaner.register(referent, releaser);
 
@@ -79,10 +74,4 @@
         Reference.reachabilityFence(referent);
         return releaser;
     }
-
-    /**
-     * Calls {@code freeFunction}({@code nativePtr}).
-     */
-    public static native void applyFreeFunction(long freeFunction, long nativePtr);
 }
-
diff --git a/ravenwood/runtime-jni/ravenwood_os_constants.cpp b/ravenwood/runtime-jni/ravenwood_os_constants.cpp
new file mode 100644
index 0000000..ea6c9d4
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_os_constants.cpp
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+// Copied from libcore/luni/src/main/native/android_system_OsConstants.cpp,
+// changes annotated with [ravenwood-change].
+
+#define LOG_TAG "OsConstants"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <netinet/icmp6.h>
+#include <netinet/in.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <net/if_arp.h>
+#include <linux/if_ether.h>
+
+// After the others because these are not necessarily self-contained in glibc.
+#include <linux/if_addr.h>
+#include <linux/rtnetlink.h>
+
+// Include linux socket constants for setting sockopts
+#include <linux/udp.h>
+
+#include <net/if.h> // After <sys/socket.h> to work around a Mac header file bug.
+
+// [ravenwood-change] always include it
+// #if defined(__BIONIC__)
+#include <linux/capability.h>
+// #endif
+
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/jni_macros.h>
+
+// [ravenwood-change] -- can't access "Portability.h", so just inline it here.
+// #include "Portability.h"
+#include <byteswap.h>
+#include <sys/sendfile.h>
+#include <sys/statvfs.h>
+#include <netdb.h>
+#include <linux/vm_sockets.h>
+
+// For LOG_ALWAYS_FATAL_IF
+#include "utils/Log.h"
+
+
+static void initConstant(JNIEnv* env, jclass c, const char* fieldName, int value) {
+    jfieldID field = env->GetStaticFieldID(c, fieldName, "I");
+    env->SetStaticIntField(c, field, value);
+}
+
+static void OsConstants_initConstants(JNIEnv* env, jclass) {
+    // [ravenwood-change] -- the constants are in the outer class, but this JNI method is in the
+    // nested class, so we need to get the outer class here.
+    jclass c = env->FindClass("android/system/OsConstants");
+    LOG_ALWAYS_FATAL_IF(c == NULL, "Unable to find class android/system/OsConstants");
+
+    initConstant(env, c, "AF_INET", AF_INET);
+    initConstant(env, c, "AF_INET6", AF_INET6);
+    initConstant(env, c, "AF_PACKET", AF_PACKET);
+    initConstant(env, c, "AF_NETLINK", AF_NETLINK);
+    initConstant(env, c, "AF_UNIX", AF_UNIX);
+    initConstant(env, c, "AF_VSOCK", AF_VSOCK);
+    initConstant(env, c, "AF_UNSPEC", AF_UNSPEC);
+    initConstant(env, c, "AI_ADDRCONFIG", AI_ADDRCONFIG);
+    initConstant(env, c, "AI_ALL", AI_ALL);
+    initConstant(env, c, "AI_CANONNAME", AI_CANONNAME);
+    initConstant(env, c, "AI_NUMERICHOST", AI_NUMERICHOST);
+#if defined(AI_NUMERICSERV)
+    initConstant(env, c, "AI_NUMERICSERV", AI_NUMERICSERV);
+#endif
+    initConstant(env, c, "AI_PASSIVE", AI_PASSIVE);
+    initConstant(env, c, "AI_V4MAPPED", AI_V4MAPPED);
+    initConstant(env, c, "ARPHRD_ETHER", ARPHRD_ETHER);
+    initConstant(env, c, "VMADDR_PORT_ANY", VMADDR_PORT_ANY);
+    initConstant(env, c, "VMADDR_CID_ANY", VMADDR_CID_ANY);
+    initConstant(env, c, "VMADDR_CID_LOCAL", VMADDR_CID_LOCAL);
+    initConstant(env, c, "VMADDR_CID_HOST", VMADDR_CID_HOST);
+    initConstant(env, c, "ARPHRD_LOOPBACK", ARPHRD_LOOPBACK);
+#if defined(CAP_LAST_CAP)
+    initConstant(env, c, "CAP_AUDIT_CONTROL", CAP_AUDIT_CONTROL);
+    initConstant(env, c, "CAP_AUDIT_WRITE", CAP_AUDIT_WRITE);
+    initConstant(env, c, "CAP_BLOCK_SUSPEND", CAP_BLOCK_SUSPEND);
+    initConstant(env, c, "CAP_CHOWN", CAP_CHOWN);
+    initConstant(env, c, "CAP_DAC_OVERRIDE", CAP_DAC_OVERRIDE);
+    initConstant(env, c, "CAP_DAC_READ_SEARCH", CAP_DAC_READ_SEARCH);
+    initConstant(env, c, "CAP_FOWNER", CAP_FOWNER);
+    initConstant(env, c, "CAP_FSETID", CAP_FSETID);
+    initConstant(env, c, "CAP_IPC_LOCK", CAP_IPC_LOCK);
+    initConstant(env, c, "CAP_IPC_OWNER", CAP_IPC_OWNER);
+    initConstant(env, c, "CAP_KILL", CAP_KILL);
+    initConstant(env, c, "CAP_LAST_CAP", CAP_LAST_CAP);
+    initConstant(env, c, "CAP_LEASE", CAP_LEASE);
+    initConstant(env, c, "CAP_LINUX_IMMUTABLE", CAP_LINUX_IMMUTABLE);
+    initConstant(env, c, "CAP_MAC_ADMIN", CAP_MAC_ADMIN);
+    initConstant(env, c, "CAP_MAC_OVERRIDE", CAP_MAC_OVERRIDE);
+    initConstant(env, c, "CAP_MKNOD", CAP_MKNOD);
+    initConstant(env, c, "CAP_NET_ADMIN", CAP_NET_ADMIN);
+    initConstant(env, c, "CAP_NET_BIND_SERVICE", CAP_NET_BIND_SERVICE);
+    initConstant(env, c, "CAP_NET_BROADCAST", CAP_NET_BROADCAST);
+    initConstant(env, c, "CAP_NET_RAW", CAP_NET_RAW);
+    initConstant(env, c, "CAP_SETFCAP", CAP_SETFCAP);
+    initConstant(env, c, "CAP_SETGID", CAP_SETGID);
+    initConstant(env, c, "CAP_SETPCAP", CAP_SETPCAP);
+    initConstant(env, c, "CAP_SETUID", CAP_SETUID);
+    initConstant(env, c, "CAP_SYS_ADMIN", CAP_SYS_ADMIN);
+    initConstant(env, c, "CAP_SYS_BOOT", CAP_SYS_BOOT);
+    initConstant(env, c, "CAP_SYS_CHROOT", CAP_SYS_CHROOT);
+    initConstant(env, c, "CAP_SYSLOG", CAP_SYSLOG);
+    initConstant(env, c, "CAP_SYS_MODULE", CAP_SYS_MODULE);
+    initConstant(env, c, "CAP_SYS_NICE", CAP_SYS_NICE);
+    initConstant(env, c, "CAP_SYS_PACCT", CAP_SYS_PACCT);
+    initConstant(env, c, "CAP_SYS_PTRACE", CAP_SYS_PTRACE);
+    initConstant(env, c, "CAP_SYS_RAWIO", CAP_SYS_RAWIO);
+    initConstant(env, c, "CAP_SYS_RESOURCE", CAP_SYS_RESOURCE);
+    initConstant(env, c, "CAP_SYS_TIME", CAP_SYS_TIME);
+    initConstant(env, c, "CAP_SYS_TTY_CONFIG", CAP_SYS_TTY_CONFIG);
+    initConstant(env, c, "CAP_WAKE_ALARM", CAP_WAKE_ALARM);
+#endif
+    initConstant(env, c, "E2BIG", E2BIG);
+    initConstant(env, c, "EACCES", EACCES);
+    initConstant(env, c, "EADDRINUSE", EADDRINUSE);
+    initConstant(env, c, "EADDRNOTAVAIL", EADDRNOTAVAIL);
+    initConstant(env, c, "EAFNOSUPPORT", EAFNOSUPPORT);
+    initConstant(env, c, "EAGAIN", EAGAIN);
+    initConstant(env, c, "EAI_AGAIN", EAI_AGAIN);
+    initConstant(env, c, "EAI_BADFLAGS", EAI_BADFLAGS);
+    initConstant(env, c, "EAI_FAIL", EAI_FAIL);
+    initConstant(env, c, "EAI_FAMILY", EAI_FAMILY);
+    initConstant(env, c, "EAI_MEMORY", EAI_MEMORY);
+    initConstant(env, c, "EAI_NODATA", EAI_NODATA);
+    initConstant(env, c, "EAI_NONAME", EAI_NONAME);
+#if defined(EAI_OVERFLOW)
+    initConstant(env, c, "EAI_OVERFLOW", EAI_OVERFLOW);
+#endif
+    initConstant(env, c, "EAI_SERVICE", EAI_SERVICE);
+    initConstant(env, c, "EAI_SOCKTYPE", EAI_SOCKTYPE);
+    initConstant(env, c, "EAI_SYSTEM", EAI_SYSTEM);
+    initConstant(env, c, "EALREADY", EALREADY);
+    initConstant(env, c, "EBADF", EBADF);
+    initConstant(env, c, "EBADMSG", EBADMSG);
+    initConstant(env, c, "EBUSY", EBUSY);
+    initConstant(env, c, "ECANCELED", ECANCELED);
+    initConstant(env, c, "ECHILD", ECHILD);
+    initConstant(env, c, "ECONNABORTED", ECONNABORTED);
+    initConstant(env, c, "ECONNREFUSED", ECONNREFUSED);
+    initConstant(env, c, "ECONNRESET", ECONNRESET);
+    initConstant(env, c, "EDEADLK", EDEADLK);
+    initConstant(env, c, "EDESTADDRREQ", EDESTADDRREQ);
+    initConstant(env, c, "EDOM", EDOM);
+    initConstant(env, c, "EDQUOT", EDQUOT);
+    initConstant(env, c, "EEXIST", EEXIST);
+    initConstant(env, c, "EFAULT", EFAULT);
+    initConstant(env, c, "EFBIG", EFBIG);
+    initConstant(env, c, "EHOSTUNREACH", EHOSTUNREACH);
+    initConstant(env, c, "EIDRM", EIDRM);
+    initConstant(env, c, "EILSEQ", EILSEQ);
+    initConstant(env, c, "EINPROGRESS", EINPROGRESS);
+    initConstant(env, c, "EINTR", EINTR);
+    initConstant(env, c, "EINVAL", EINVAL);
+    initConstant(env, c, "EIO", EIO);
+    initConstant(env, c, "EISCONN", EISCONN);
+    initConstant(env, c, "EISDIR", EISDIR);
+    initConstant(env, c, "ELOOP", ELOOP);
+    initConstant(env, c, "EMFILE", EMFILE);
+    initConstant(env, c, "EMLINK", EMLINK);
+    initConstant(env, c, "EMSGSIZE", EMSGSIZE);
+    initConstant(env, c, "EMULTIHOP", EMULTIHOP);
+    initConstant(env, c, "ENAMETOOLONG", ENAMETOOLONG);
+    initConstant(env, c, "ENETDOWN", ENETDOWN);
+    initConstant(env, c, "ENETRESET", ENETRESET);
+    initConstant(env, c, "ENETUNREACH", ENETUNREACH);
+    initConstant(env, c, "ENFILE", ENFILE);
+    initConstant(env, c, "ENOBUFS", ENOBUFS);
+    initConstant(env, c, "ENODATA", ENODATA);
+    initConstant(env, c, "ENODEV", ENODEV);
+    initConstant(env, c, "ENOENT", ENOENT);
+    initConstant(env, c, "ENOEXEC", ENOEXEC);
+    initConstant(env, c, "ENOLCK", ENOLCK);
+    initConstant(env, c, "ENOLINK", ENOLINK);
+    initConstant(env, c, "ENOMEM", ENOMEM);
+    initConstant(env, c, "ENOMSG", ENOMSG);
+    initConstant(env, c, "ENONET", ENONET);
+    initConstant(env, c, "ENOPROTOOPT", ENOPROTOOPT);
+    initConstant(env, c, "ENOSPC", ENOSPC);
+    initConstant(env, c, "ENOSR", ENOSR);
+    initConstant(env, c, "ENOSTR", ENOSTR);
+    initConstant(env, c, "ENOSYS", ENOSYS);
+    initConstant(env, c, "ENOTCONN", ENOTCONN);
+    initConstant(env, c, "ENOTDIR", ENOTDIR);
+    initConstant(env, c, "ENOTEMPTY", ENOTEMPTY);
+    initConstant(env, c, "ENOTSOCK", ENOTSOCK);
+    initConstant(env, c, "ENOTSUP", ENOTSUP);
+    initConstant(env, c, "ENOTTY", ENOTTY);
+    initConstant(env, c, "ENXIO", ENXIO);
+    initConstant(env, c, "EOPNOTSUPP", EOPNOTSUPP);
+    initConstant(env, c, "EOVERFLOW", EOVERFLOW);
+    initConstant(env, c, "EPERM", EPERM);
+    initConstant(env, c, "EPIPE", EPIPE);
+    initConstant(env, c, "EPROTO", EPROTO);
+    initConstant(env, c, "EPROTONOSUPPORT", EPROTONOSUPPORT);
+    initConstant(env, c, "EPROTOTYPE", EPROTOTYPE);
+    initConstant(env, c, "ERANGE", ERANGE);
+    initConstant(env, c, "EROFS", EROFS);
+    initConstant(env, c, "ESPIPE", ESPIPE);
+    initConstant(env, c, "ESRCH", ESRCH);
+    initConstant(env, c, "ESTALE", ESTALE);
+    initConstant(env, c, "ETH_P_ALL", ETH_P_ALL);
+    initConstant(env, c, "ETH_P_ARP", ETH_P_ARP);
+    initConstant(env, c, "ETH_P_IP", ETH_P_IP);
+    initConstant(env, c, "ETH_P_IPV6", ETH_P_IPV6);
+    initConstant(env, c, "ETIME", ETIME);
+    initConstant(env, c, "ETIMEDOUT", ETIMEDOUT);
+    initConstant(env, c, "ETXTBSY", ETXTBSY);
+    initConstant(env, c, "EUSERS", EUSERS);
+#if EWOULDBLOCK != EAGAIN
+#error EWOULDBLOCK != EAGAIN
+#endif
+    initConstant(env, c, "EXDEV", EXDEV);
+    initConstant(env, c, "EXIT_FAILURE", EXIT_FAILURE);
+    initConstant(env, c, "EXIT_SUCCESS", EXIT_SUCCESS);
+    initConstant(env, c, "FD_CLOEXEC", FD_CLOEXEC);
+    initConstant(env, c, "FIONREAD", FIONREAD);
+    initConstant(env, c, "F_DUPFD", F_DUPFD);
+    initConstant(env, c, "F_DUPFD_CLOEXEC", F_DUPFD_CLOEXEC);
+    initConstant(env, c, "F_GETFD", F_GETFD);
+    initConstant(env, c, "F_GETFL", F_GETFL);
+    initConstant(env, c, "F_GETLK", F_GETLK);
+#if defined(F_GETLK64)
+    initConstant(env, c, "F_GETLK64", F_GETLK64);
+#endif
+    initConstant(env, c, "F_GETOWN", F_GETOWN);
+    initConstant(env, c, "F_OK", F_OK);
+    initConstant(env, c, "F_RDLCK", F_RDLCK);
+    initConstant(env, c, "F_SETFD", F_SETFD);
+    initConstant(env, c, "F_SETFL", F_SETFL);
+    initConstant(env, c, "F_SETLK", F_SETLK);
+#if defined(F_SETLK64)
+    initConstant(env, c, "F_SETLK64", F_SETLK64);
+#endif
+    initConstant(env, c, "F_SETLKW", F_SETLKW);
+#if defined(F_SETLKW64)
+    initConstant(env, c, "F_SETLKW64", F_SETLKW64);
+#endif
+    initConstant(env, c, "F_SETOWN", F_SETOWN);
+    initConstant(env, c, "F_UNLCK", F_UNLCK);
+    initConstant(env, c, "F_WRLCK", F_WRLCK);
+    initConstant(env, c, "ICMP_ECHO", ICMP_ECHO);
+    initConstant(env, c, "ICMP_ECHOREPLY", ICMP_ECHOREPLY);
+    initConstant(env, c, "ICMP6_ECHO_REQUEST", ICMP6_ECHO_REQUEST);
+    initConstant(env, c, "ICMP6_ECHO_REPLY", ICMP6_ECHO_REPLY);
+#if defined(IFA_F_DADFAILED)
+    initConstant(env, c, "IFA_F_DADFAILED", IFA_F_DADFAILED);
+#endif
+#if defined(IFA_F_DEPRECATED)
+    initConstant(env, c, "IFA_F_DEPRECATED", IFA_F_DEPRECATED);
+#endif
+#if defined(IFA_F_HOMEADDRESS)
+    initConstant(env, c, "IFA_F_HOMEADDRESS", IFA_F_HOMEADDRESS);
+#endif
+#if defined(IFA_F_MANAGETEMPADDR)
+    initConstant(env, c, "IFA_F_MANAGETEMPADDR", IFA_F_MANAGETEMPADDR);
+#endif
+#if defined(IFA_F_NODAD)
+    initConstant(env, c, "IFA_F_NODAD", IFA_F_NODAD);
+#endif
+#if defined(IFA_F_NOPREFIXROUTE)
+    initConstant(env, c, "IFA_F_NOPREFIXROUTE", IFA_F_NOPREFIXROUTE);
+#endif
+#if defined(IFA_F_OPTIMISTIC)
+    initConstant(env, c, "IFA_F_OPTIMISTIC", IFA_F_OPTIMISTIC);
+#endif
+#if defined(IFA_F_PERMANENT)
+    initConstant(env, c, "IFA_F_PERMANENT", IFA_F_PERMANENT);
+#endif
+#if defined(IFA_F_SECONDARY)
+    initConstant(env, c, "IFA_F_SECONDARY", IFA_F_SECONDARY);
+#endif
+#if defined(IFA_F_TEMPORARY)
+    initConstant(env, c, "IFA_F_TEMPORARY", IFA_F_TEMPORARY);
+#endif
+#if defined(IFA_F_TENTATIVE)
+    initConstant(env, c, "IFA_F_TENTATIVE", IFA_F_TENTATIVE);
+#endif
+    initConstant(env, c, "IFF_ALLMULTI", IFF_ALLMULTI);
+#if defined(IFF_AUTOMEDIA)
+    initConstant(env, c, "IFF_AUTOMEDIA", IFF_AUTOMEDIA);
+#endif
+    initConstant(env, c, "IFF_BROADCAST", IFF_BROADCAST);
+    initConstant(env, c, "IFF_DEBUG", IFF_DEBUG);
+#if defined(IFF_DYNAMIC)
+    initConstant(env, c, "IFF_DYNAMIC", IFF_DYNAMIC);
+#endif
+    initConstant(env, c, "IFF_LOOPBACK", IFF_LOOPBACK);
+#if defined(IFF_MASTER)
+    initConstant(env, c, "IFF_MASTER", IFF_MASTER);
+#endif
+    initConstant(env, c, "IFF_MULTICAST", IFF_MULTICAST);
+    initConstant(env, c, "IFF_NOARP", IFF_NOARP);
+    initConstant(env, c, "IFF_NOTRAILERS", IFF_NOTRAILERS);
+    initConstant(env, c, "IFF_POINTOPOINT", IFF_POINTOPOINT);
+#if defined(IFF_PORTSEL)
+    initConstant(env, c, "IFF_PORTSEL", IFF_PORTSEL);
+#endif
+    initConstant(env, c, "IFF_PROMISC", IFF_PROMISC);
+    initConstant(env, c, "IFF_RUNNING", IFF_RUNNING);
+#if defined(IFF_SLAVE)
+    initConstant(env, c, "IFF_SLAVE", IFF_SLAVE);
+#endif
+    initConstant(env, c, "IFF_UP", IFF_UP);
+    initConstant(env, c, "IPPROTO_ICMP", IPPROTO_ICMP);
+    initConstant(env, c, "IPPROTO_ICMPV6", IPPROTO_ICMPV6);
+    initConstant(env, c, "IPPROTO_IP", IPPROTO_IP);
+    initConstant(env, c, "IPPROTO_IPV6", IPPROTO_IPV6);
+    initConstant(env, c, "IPPROTO_RAW", IPPROTO_RAW);
+    initConstant(env, c, "IPPROTO_TCP", IPPROTO_TCP);
+    initConstant(env, c, "IPPROTO_UDP", IPPROTO_UDP);
+    initConstant(env, c, "IPPROTO_ESP", IPPROTO_ESP);
+    initConstant(env, c, "IPV6_CHECKSUM", IPV6_CHECKSUM);
+    initConstant(env, c, "IPV6_MULTICAST_HOPS", IPV6_MULTICAST_HOPS);
+    initConstant(env, c, "IPV6_MULTICAST_IF", IPV6_MULTICAST_IF);
+    initConstant(env, c, "IPV6_MULTICAST_LOOP", IPV6_MULTICAST_LOOP);
+#if defined(IPV6_PKTINFO)
+    initConstant(env, c, "IPV6_PKTINFO", IPV6_PKTINFO);
+#endif
+#if defined(IPV6_RECVDSTOPTS)
+    initConstant(env, c, "IPV6_RECVDSTOPTS", IPV6_RECVDSTOPTS);
+#endif
+#if defined(IPV6_RECVHOPLIMIT)
+    initConstant(env, c, "IPV6_RECVHOPLIMIT", IPV6_RECVHOPLIMIT);
+#endif
+#if defined(IPV6_RECVHOPOPTS)
+    initConstant(env, c, "IPV6_RECVHOPOPTS", IPV6_RECVHOPOPTS);
+#endif
+#if defined(IPV6_RECVPKTINFO)
+    initConstant(env, c, "IPV6_RECVPKTINFO", IPV6_RECVPKTINFO);
+#endif
+#if defined(IPV6_RECVRTHDR)
+    initConstant(env, c, "IPV6_RECVRTHDR", IPV6_RECVRTHDR);
+#endif
+#if defined(IPV6_RECVTCLASS)
+    initConstant(env, c, "IPV6_RECVTCLASS", IPV6_RECVTCLASS);
+#endif
+#if defined(IPV6_TCLASS)
+    initConstant(env, c, "IPV6_TCLASS", IPV6_TCLASS);
+#endif
+    initConstant(env, c, "IPV6_UNICAST_HOPS", IPV6_UNICAST_HOPS);
+    initConstant(env, c, "IPV6_V6ONLY", IPV6_V6ONLY);
+    initConstant(env, c, "IP_MULTICAST_ALL", IP_MULTICAST_ALL);
+    initConstant(env, c, "IP_MULTICAST_IF", IP_MULTICAST_IF);
+    initConstant(env, c, "IP_MULTICAST_LOOP", IP_MULTICAST_LOOP);
+    initConstant(env, c, "IP_MULTICAST_TTL", IP_MULTICAST_TTL);
+    initConstant(env, c, "IP_RECVTOS", IP_RECVTOS);
+    initConstant(env, c, "IP_TOS", IP_TOS);
+    initConstant(env, c, "IP_TTL", IP_TTL);
+#if defined(_LINUX_CAPABILITY_VERSION_3)
+    initConstant(env, c, "_LINUX_CAPABILITY_VERSION_3", _LINUX_CAPABILITY_VERSION_3);
+#endif
+    initConstant(env, c, "MAP_FIXED", MAP_FIXED);
+    initConstant(env, c, "MAP_ANONYMOUS", MAP_ANONYMOUS);
+    initConstant(env, c, "MAP_POPULATE", MAP_POPULATE);
+    initConstant(env, c, "MAP_PRIVATE", MAP_PRIVATE);
+    initConstant(env, c, "MAP_SHARED", MAP_SHARED);
+#if defined(MCAST_JOIN_GROUP)
+    initConstant(env, c, "MCAST_JOIN_GROUP", MCAST_JOIN_GROUP);
+#endif
+#if defined(MCAST_LEAVE_GROUP)
+    initConstant(env, c, "MCAST_LEAVE_GROUP", MCAST_LEAVE_GROUP);
+#endif
+#if defined(MCAST_JOIN_SOURCE_GROUP)
+    initConstant(env, c, "MCAST_JOIN_SOURCE_GROUP", MCAST_JOIN_SOURCE_GROUP);
+#endif
+#if defined(MCAST_LEAVE_SOURCE_GROUP)
+    initConstant(env, c, "MCAST_LEAVE_SOURCE_GROUP", MCAST_LEAVE_SOURCE_GROUP);
+#endif
+#if defined(MCAST_BLOCK_SOURCE)
+    initConstant(env, c, "MCAST_BLOCK_SOURCE", MCAST_BLOCK_SOURCE);
+#endif
+#if defined(MCAST_UNBLOCK_SOURCE)
+    initConstant(env, c, "MCAST_UNBLOCK_SOURCE", MCAST_UNBLOCK_SOURCE);
+#endif
+    initConstant(env, c, "MCL_CURRENT", MCL_CURRENT);
+    initConstant(env, c, "MCL_FUTURE", MCL_FUTURE);
+#if defined(MFD_CLOEXEC)
+    initConstant(env, c, "MFD_CLOEXEC", MFD_CLOEXEC);
+#endif
+    initConstant(env, c, "MSG_CTRUNC", MSG_CTRUNC);
+    initConstant(env, c, "MSG_DONTROUTE", MSG_DONTROUTE);
+    initConstant(env, c, "MSG_EOR", MSG_EOR);
+    initConstant(env, c, "MSG_OOB", MSG_OOB);
+    initConstant(env, c, "MSG_PEEK", MSG_PEEK);
+    initConstant(env, c, "MSG_TRUNC", MSG_TRUNC);
+    initConstant(env, c, "MSG_WAITALL", MSG_WAITALL);
+    initConstant(env, c, "MS_ASYNC", MS_ASYNC);
+    initConstant(env, c, "MS_INVALIDATE", MS_INVALIDATE);
+    initConstant(env, c, "MS_SYNC", MS_SYNC);
+    initConstant(env, c, "NETLINK_NETFILTER", NETLINK_NETFILTER);
+    initConstant(env, c, "NETLINK_ROUTE", NETLINK_ROUTE);
+    initConstant(env, c, "NETLINK_INET_DIAG", NETLINK_INET_DIAG);
+    initConstant(env, c, "NETLINK_XFRM", NETLINK_XFRM);
+    initConstant(env, c, "NI_DGRAM", NI_DGRAM);
+    initConstant(env, c, "NI_NAMEREQD", NI_NAMEREQD);
+    initConstant(env, c, "NI_NOFQDN", NI_NOFQDN);
+    initConstant(env, c, "NI_NUMERICHOST", NI_NUMERICHOST);
+    initConstant(env, c, "NI_NUMERICSERV", NI_NUMERICSERV);
+    initConstant(env, c, "O_ACCMODE", O_ACCMODE);
+    initConstant(env, c, "O_APPEND", O_APPEND);
+    initConstant(env, c, "O_CLOEXEC", O_CLOEXEC);
+    initConstant(env, c, "O_CREAT", O_CREAT);
+    initConstant(env, c, "O_DIRECT", O_DIRECT);
+    initConstant(env, c, "O_EXCL", O_EXCL);
+    initConstant(env, c, "O_NOCTTY", O_NOCTTY);
+    initConstant(env, c, "O_NOFOLLOW", O_NOFOLLOW);
+    initConstant(env, c, "O_NONBLOCK", O_NONBLOCK);
+    initConstant(env, c, "O_RDONLY", O_RDONLY);
+    initConstant(env, c, "O_RDWR", O_RDWR);
+    initConstant(env, c, "O_SYNC", O_SYNC);
+    initConstant(env, c, "O_DSYNC", O_DSYNC);
+    initConstant(env, c, "O_TRUNC", O_TRUNC);
+    initConstant(env, c, "O_WRONLY", O_WRONLY);
+    initConstant(env, c, "POLLERR", POLLERR);
+    initConstant(env, c, "POLLHUP", POLLHUP);
+    initConstant(env, c, "POLLIN", POLLIN);
+    initConstant(env, c, "POLLNVAL", POLLNVAL);
+    initConstant(env, c, "POLLOUT", POLLOUT);
+    initConstant(env, c, "POLLPRI", POLLPRI);
+    initConstant(env, c, "POLLRDBAND", POLLRDBAND);
+    initConstant(env, c, "POLLRDNORM", POLLRDNORM);
+    initConstant(env, c, "POLLWRBAND", POLLWRBAND);
+    initConstant(env, c, "POLLWRNORM", POLLWRNORM);
+#if defined(PR_CAP_AMBIENT)
+    initConstant(env, c, "PR_CAP_AMBIENT", PR_CAP_AMBIENT);
+#endif
+#if defined(PR_CAP_AMBIENT_RAISE)
+    initConstant(env, c, "PR_CAP_AMBIENT_RAISE", PR_CAP_AMBIENT_RAISE);
+#endif
+#if defined(PR_GET_DUMPABLE)
+    initConstant(env, c, "PR_GET_DUMPABLE", PR_GET_DUMPABLE);
+#endif
+#if defined(PR_SET_DUMPABLE)
+    initConstant(env, c, "PR_SET_DUMPABLE", PR_SET_DUMPABLE);
+#endif
+#if defined(PR_SET_NO_NEW_PRIVS)
+    initConstant(env, c, "PR_SET_NO_NEW_PRIVS", PR_SET_NO_NEW_PRIVS);
+#endif
+    initConstant(env, c, "PROT_EXEC", PROT_EXEC);
+    initConstant(env, c, "PROT_NONE", PROT_NONE);
+    initConstant(env, c, "PROT_READ", PROT_READ);
+    initConstant(env, c, "PROT_WRITE", PROT_WRITE);
+    initConstant(env, c, "R_OK", R_OK);
+    initConstant(env, c, "RLIMIT_NOFILE", RLIMIT_NOFILE);
+// NOTE: The RT_* constants are not preprocessor defines, they're enum
+// members. The best we can do (barring UAPI / kernel version checks) is
+// to hope they exist on all host linuxes we're building on. These
+// constants have been around since 2.6.35 at least, so we should be ok.
+    initConstant(env, c, "RT_SCOPE_HOST", RT_SCOPE_HOST);
+    initConstant(env, c, "RT_SCOPE_LINK", RT_SCOPE_LINK);
+    initConstant(env, c, "RT_SCOPE_NOWHERE", RT_SCOPE_NOWHERE);
+    initConstant(env, c, "RT_SCOPE_SITE", RT_SCOPE_SITE);
+    initConstant(env, c, "RT_SCOPE_UNIVERSE", RT_SCOPE_UNIVERSE);
+    initConstant(env, c, "RTMGRP_IPV4_IFADDR", RTMGRP_IPV4_IFADDR);
+    initConstant(env, c, "RTMGRP_IPV4_MROUTE", RTMGRP_IPV4_MROUTE);
+    initConstant(env, c, "RTMGRP_IPV4_ROUTE", RTMGRP_IPV4_ROUTE);
+    initConstant(env, c, "RTMGRP_IPV4_RULE", RTMGRP_IPV4_RULE);
+    initConstant(env, c, "RTMGRP_IPV6_IFADDR", RTMGRP_IPV6_IFADDR);
+    initConstant(env, c, "RTMGRP_IPV6_IFINFO", RTMGRP_IPV6_IFINFO);
+    initConstant(env, c, "RTMGRP_IPV6_MROUTE", RTMGRP_IPV6_MROUTE);
+    initConstant(env, c, "RTMGRP_IPV6_PREFIX", RTMGRP_IPV6_PREFIX);
+    initConstant(env, c, "RTMGRP_IPV6_ROUTE", RTMGRP_IPV6_ROUTE);
+    initConstant(env, c, "RTMGRP_LINK", RTMGRP_LINK);
+    initConstant(env, c, "RTMGRP_NEIGH", RTMGRP_NEIGH);
+    initConstant(env, c, "RTMGRP_NOTIFY", RTMGRP_NOTIFY);
+    initConstant(env, c, "RTMGRP_TC", RTMGRP_TC);
+    initConstant(env, c, "SEEK_CUR", SEEK_CUR);
+    initConstant(env, c, "SEEK_END", SEEK_END);
+    initConstant(env, c, "SEEK_SET", SEEK_SET);
+    initConstant(env, c, "SHUT_RD", SHUT_RD);
+    initConstant(env, c, "SHUT_RDWR", SHUT_RDWR);
+    initConstant(env, c, "SHUT_WR", SHUT_WR);
+    initConstant(env, c, "SIGABRT", SIGABRT);
+    initConstant(env, c, "SIGALRM", SIGALRM);
+    initConstant(env, c, "SIGBUS", SIGBUS);
+    initConstant(env, c, "SIGCHLD", SIGCHLD);
+    initConstant(env, c, "SIGCONT", SIGCONT);
+    initConstant(env, c, "SIGFPE", SIGFPE);
+    initConstant(env, c, "SIGHUP", SIGHUP);
+    initConstant(env, c, "SIGILL", SIGILL);
+    initConstant(env, c, "SIGINT", SIGINT);
+    initConstant(env, c, "SIGIO", SIGIO);
+    initConstant(env, c, "SIGKILL", SIGKILL);
+    initConstant(env, c, "SIGPIPE", SIGPIPE);
+    initConstant(env, c, "SIGPROF", SIGPROF);
+#if defined(SIGPWR)
+    initConstant(env, c, "SIGPWR", SIGPWR);
+#endif
+    initConstant(env, c, "SIGQUIT", SIGQUIT);
+#if defined(SIGRTMAX)
+    initConstant(env, c, "SIGRTMAX", SIGRTMAX);
+#endif
+#if defined(SIGRTMIN)
+    initConstant(env, c, "SIGRTMIN", SIGRTMIN);
+#endif
+    initConstant(env, c, "SIGSEGV", SIGSEGV);
+#if defined(SIGSTKFLT)
+    initConstant(env, c, "SIGSTKFLT", SIGSTKFLT);
+#endif
+    initConstant(env, c, "SIGSTOP", SIGSTOP);
+    initConstant(env, c, "SIGSYS", SIGSYS);
+    initConstant(env, c, "SIGTERM", SIGTERM);
+    initConstant(env, c, "SIGTRAP", SIGTRAP);
+    initConstant(env, c, "SIGTSTP", SIGTSTP);
+    initConstant(env, c, "SIGTTIN", SIGTTIN);
+    initConstant(env, c, "SIGTTOU", SIGTTOU);
+    initConstant(env, c, "SIGURG", SIGURG);
+    initConstant(env, c, "SIGUSR1", SIGUSR1);
+    initConstant(env, c, "SIGUSR2", SIGUSR2);
+    initConstant(env, c, "SIGVTALRM", SIGVTALRM);
+    initConstant(env, c, "SIGWINCH", SIGWINCH);
+    initConstant(env, c, "SIGXCPU", SIGXCPU);
+    initConstant(env, c, "SIGXFSZ", SIGXFSZ);
+    initConstant(env, c, "SIOCGIFADDR", SIOCGIFADDR);
+    initConstant(env, c, "SIOCGIFBRDADDR", SIOCGIFBRDADDR);
+    initConstant(env, c, "SIOCGIFDSTADDR", SIOCGIFDSTADDR);
+    initConstant(env, c, "SIOCGIFNETMASK", SIOCGIFNETMASK);
+    initConstant(env, c, "SOCK_CLOEXEC", SOCK_CLOEXEC);
+    initConstant(env, c, "SOCK_DGRAM", SOCK_DGRAM);
+    initConstant(env, c, "SOCK_NONBLOCK", SOCK_NONBLOCK);
+    initConstant(env, c, "SOCK_RAW", SOCK_RAW);
+    initConstant(env, c, "SOCK_SEQPACKET", SOCK_SEQPACKET);
+    initConstant(env, c, "SOCK_STREAM", SOCK_STREAM);
+    initConstant(env, c, "SOL_SOCKET", SOL_SOCKET);
+#if defined(SOL_UDP)
+    initConstant(env, c, "SOL_UDP", SOL_UDP);
+#endif
+    initConstant(env, c, "SOL_PACKET", SOL_PACKET);
+#if defined(SO_BINDTODEVICE)
+    initConstant(env, c, "SO_BINDTODEVICE", SO_BINDTODEVICE);
+#endif
+    initConstant(env, c, "SO_BROADCAST", SO_BROADCAST);
+    initConstant(env, c, "SO_DEBUG", SO_DEBUG);
+#if defined(SO_DOMAIN)
+    initConstant(env, c, "SO_DOMAIN", SO_DOMAIN);
+#endif
+    initConstant(env, c, "SO_DONTROUTE", SO_DONTROUTE);
+    initConstant(env, c, "SO_ERROR", SO_ERROR);
+    initConstant(env, c, "SO_KEEPALIVE", SO_KEEPALIVE);
+    initConstant(env, c, "SO_LINGER", SO_LINGER);
+    initConstant(env, c, "SO_OOBINLINE", SO_OOBINLINE);
+#if defined(SO_PASSCRED)
+    initConstant(env, c, "SO_PASSCRED", SO_PASSCRED);
+#endif
+#if defined(SO_PEERCRED)
+    initConstant(env, c, "SO_PEERCRED", SO_PEERCRED);
+#endif
+#if defined(SO_PROTOCOL)
+    initConstant(env, c, "SO_PROTOCOL", SO_PROTOCOL);
+#endif
+    initConstant(env, c, "SO_RCVBUF", SO_RCVBUF);
+    initConstant(env, c, "SO_RCVLOWAT", SO_RCVLOWAT);
+    initConstant(env, c, "SO_RCVTIMEO", SO_RCVTIMEO);
+    initConstant(env, c, "SO_REUSEADDR", SO_REUSEADDR);
+    initConstant(env, c, "SO_SNDBUF", SO_SNDBUF);
+    initConstant(env, c, "SO_SNDLOWAT", SO_SNDLOWAT);
+    initConstant(env, c, "SO_SNDTIMEO", SO_SNDTIMEO);
+    initConstant(env, c, "SO_TYPE", SO_TYPE);
+#if defined(PACKET_IGNORE_OUTGOING)
+    initConstant(env, c, "PACKET_IGNORE_OUTGOING", PACKET_IGNORE_OUTGOING);
+#endif
+    initConstant(env, c, "SPLICE_F_MOVE", SPLICE_F_MOVE);
+    initConstant(env, c, "SPLICE_F_NONBLOCK", SPLICE_F_NONBLOCK);
+    initConstant(env, c, "SPLICE_F_MORE", SPLICE_F_MORE);
+    initConstant(env, c, "STDERR_FILENO", STDERR_FILENO);
+    initConstant(env, c, "STDIN_FILENO", STDIN_FILENO);
+    initConstant(env, c, "STDOUT_FILENO", STDOUT_FILENO);
+    initConstant(env, c, "ST_MANDLOCK", ST_MANDLOCK);
+    initConstant(env, c, "ST_NOATIME", ST_NOATIME);
+    initConstant(env, c, "ST_NODEV", ST_NODEV);
+    initConstant(env, c, "ST_NODIRATIME", ST_NODIRATIME);
+    initConstant(env, c, "ST_NOEXEC", ST_NOEXEC);
+    initConstant(env, c, "ST_NOSUID", ST_NOSUID);
+    initConstant(env, c, "ST_RDONLY", ST_RDONLY);
+    initConstant(env, c, "ST_RELATIME", ST_RELATIME);
+    initConstant(env, c, "ST_SYNCHRONOUS", ST_SYNCHRONOUS);
+    initConstant(env, c, "S_IFBLK", S_IFBLK);
+    initConstant(env, c, "S_IFCHR", S_IFCHR);
+    initConstant(env, c, "S_IFDIR", S_IFDIR);
+    initConstant(env, c, "S_IFIFO", S_IFIFO);
+    initConstant(env, c, "S_IFLNK", S_IFLNK);
+    initConstant(env, c, "S_IFMT", S_IFMT);
+    initConstant(env, c, "S_IFREG", S_IFREG);
+    initConstant(env, c, "S_IFSOCK", S_IFSOCK);
+    initConstant(env, c, "S_IRGRP", S_IRGRP);
+    initConstant(env, c, "S_IROTH", S_IROTH);
+    initConstant(env, c, "S_IRUSR", S_IRUSR);
+    initConstant(env, c, "S_IRWXG", S_IRWXG);
+    initConstant(env, c, "S_IRWXO", S_IRWXO);
+    initConstant(env, c, "S_IRWXU", S_IRWXU);
+    initConstant(env, c, "S_ISGID", S_ISGID);
+    initConstant(env, c, "S_ISUID", S_ISUID);
+    initConstant(env, c, "S_ISVTX", S_ISVTX);
+    initConstant(env, c, "S_IWGRP", S_IWGRP);
+    initConstant(env, c, "S_IWOTH", S_IWOTH);
+    initConstant(env, c, "S_IWUSR", S_IWUSR);
+    initConstant(env, c, "S_IXGRP", S_IXGRP);
+    initConstant(env, c, "S_IXOTH", S_IXOTH);
+    initConstant(env, c, "S_IXUSR", S_IXUSR);
+    initConstant(env, c, "TCP_NODELAY", TCP_NODELAY);
+#if defined(TCP_USER_TIMEOUT)
+    initConstant(env, c, "TCP_USER_TIMEOUT", TCP_USER_TIMEOUT);
+#endif
+    initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
+    initConstant(env, c, "UDP_ENCAP", UDP_ENCAP);
+    initConstant(env, c, "UDP_ENCAP_ESPINUDP_NON_IKE", UDP_ENCAP_ESPINUDP_NON_IKE);
+    initConstant(env, c, "UDP_ENCAP_ESPINUDP", UDP_ENCAP_ESPINUDP);
+#if defined(UDP_GRO)
+    initConstant(env, c, "UDP_GRO", UDP_GRO);
+#endif
+#if defined(UDP_SEGMENT)
+    initConstant(env, c, "UDP_SEGMENT", UDP_SEGMENT);
+#endif
+    // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
+    initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
+    initConstant(env, c, "WCONTINUED", WCONTINUED);
+    initConstant(env, c, "WEXITED", WEXITED);
+    initConstant(env, c, "WNOHANG", WNOHANG);
+    initConstant(env, c, "WNOWAIT", WNOWAIT);
+    initConstant(env, c, "WSTOPPED", WSTOPPED);
+    initConstant(env, c, "WUNTRACED", WUNTRACED);
+    initConstant(env, c, "W_OK", W_OK);
+    initConstant(env, c, "XATTR_CREATE", XATTR_CREATE);
+    initConstant(env, c, "XATTR_REPLACE", XATTR_REPLACE);
+    initConstant(env, c, "X_OK", X_OK);
+    initConstant(env, c, "_SC_2_CHAR_TERM", _SC_2_CHAR_TERM);
+    initConstant(env, c, "_SC_2_C_BIND", _SC_2_C_BIND);
+    initConstant(env, c, "_SC_2_C_DEV", _SC_2_C_DEV);
+#if defined(_SC_2_C_VERSION)
+    initConstant(env, c, "_SC_2_C_VERSION", _SC_2_C_VERSION);
+#endif
+    initConstant(env, c, "_SC_2_FORT_DEV", _SC_2_FORT_DEV);
+    initConstant(env, c, "_SC_2_FORT_RUN", _SC_2_FORT_RUN);
+    initConstant(env, c, "_SC_2_LOCALEDEF", _SC_2_LOCALEDEF);
+    initConstant(env, c, "_SC_2_SW_DEV", _SC_2_SW_DEV);
+    initConstant(env, c, "_SC_2_UPE", _SC_2_UPE);
+    initConstant(env, c, "_SC_2_VERSION", _SC_2_VERSION);
+    initConstant(env, c, "_SC_AIO_LISTIO_MAX", _SC_AIO_LISTIO_MAX);
+    initConstant(env, c, "_SC_AIO_MAX", _SC_AIO_MAX);
+    initConstant(env, c, "_SC_AIO_PRIO_DELTA_MAX", _SC_AIO_PRIO_DELTA_MAX);
+    initConstant(env, c, "_SC_ARG_MAX", _SC_ARG_MAX);
+    initConstant(env, c, "_SC_ASYNCHRONOUS_IO", _SC_ASYNCHRONOUS_IO);
+    initConstant(env, c, "_SC_ATEXIT_MAX", _SC_ATEXIT_MAX);
+#if defined(_SC_AVPHYS_PAGES)
+    initConstant(env, c, "_SC_AVPHYS_PAGES", _SC_AVPHYS_PAGES);
+#endif
+    initConstant(env, c, "_SC_BC_BASE_MAX", _SC_BC_BASE_MAX);
+    initConstant(env, c, "_SC_BC_DIM_MAX", _SC_BC_DIM_MAX);
+    initConstant(env, c, "_SC_BC_SCALE_MAX", _SC_BC_SCALE_MAX);
+    initConstant(env, c, "_SC_BC_STRING_MAX", _SC_BC_STRING_MAX);
+    initConstant(env, c, "_SC_CHILD_MAX", _SC_CHILD_MAX);
+    initConstant(env, c, "_SC_CLK_TCK", _SC_CLK_TCK);
+    initConstant(env, c, "_SC_COLL_WEIGHTS_MAX", _SC_COLL_WEIGHTS_MAX);
+    initConstant(env, c, "_SC_DELAYTIMER_MAX", _SC_DELAYTIMER_MAX);
+    initConstant(env, c, "_SC_EXPR_NEST_MAX", _SC_EXPR_NEST_MAX);
+    initConstant(env, c, "_SC_FSYNC", _SC_FSYNC);
+    initConstant(env, c, "_SC_GETGR_R_SIZE_MAX", _SC_GETGR_R_SIZE_MAX);
+    initConstant(env, c, "_SC_GETPW_R_SIZE_MAX", _SC_GETPW_R_SIZE_MAX);
+    initConstant(env, c, "_SC_IOV_MAX", _SC_IOV_MAX);
+    initConstant(env, c, "_SC_JOB_CONTROL", _SC_JOB_CONTROL);
+    initConstant(env, c, "_SC_LINE_MAX", _SC_LINE_MAX);
+    initConstant(env, c, "_SC_LOGIN_NAME_MAX", _SC_LOGIN_NAME_MAX);
+    initConstant(env, c, "_SC_MAPPED_FILES", _SC_MAPPED_FILES);
+    initConstant(env, c, "_SC_MEMLOCK", _SC_MEMLOCK);
+    initConstant(env, c, "_SC_MEMLOCK_RANGE", _SC_MEMLOCK_RANGE);
+    initConstant(env, c, "_SC_MEMORY_PROTECTION", _SC_MEMORY_PROTECTION);
+    initConstant(env, c, "_SC_MESSAGE_PASSING", _SC_MESSAGE_PASSING);
+    initConstant(env, c, "_SC_MQ_OPEN_MAX", _SC_MQ_OPEN_MAX);
+    initConstant(env, c, "_SC_MQ_PRIO_MAX", _SC_MQ_PRIO_MAX);
+    initConstant(env, c, "_SC_NGROUPS_MAX", _SC_NGROUPS_MAX);
+    initConstant(env, c, "_SC_NPROCESSORS_CONF", _SC_NPROCESSORS_CONF);
+    initConstant(env, c, "_SC_NPROCESSORS_ONLN", _SC_NPROCESSORS_ONLN);
+    initConstant(env, c, "_SC_OPEN_MAX", _SC_OPEN_MAX);
+    initConstant(env, c, "_SC_PAGESIZE", _SC_PAGESIZE);
+    initConstant(env, c, "_SC_PAGE_SIZE", _SC_PAGE_SIZE);
+    initConstant(env, c, "_SC_PASS_MAX", _SC_PASS_MAX);
+#if defined(_SC_PHYS_PAGES)
+    initConstant(env, c, "_SC_PHYS_PAGES", _SC_PHYS_PAGES);
+#endif
+    initConstant(env, c, "_SC_PRIORITIZED_IO", _SC_PRIORITIZED_IO);
+    initConstant(env, c, "_SC_PRIORITY_SCHEDULING", _SC_PRIORITY_SCHEDULING);
+    initConstant(env, c, "_SC_REALTIME_SIGNALS", _SC_REALTIME_SIGNALS);
+    initConstant(env, c, "_SC_RE_DUP_MAX", _SC_RE_DUP_MAX);
+    initConstant(env, c, "_SC_RTSIG_MAX", _SC_RTSIG_MAX);
+    initConstant(env, c, "_SC_SAVED_IDS", _SC_SAVED_IDS);
+    initConstant(env, c, "_SC_SEMAPHORES", _SC_SEMAPHORES);
+    initConstant(env, c, "_SC_SEM_NSEMS_MAX", _SC_SEM_NSEMS_MAX);
+    initConstant(env, c, "_SC_SEM_VALUE_MAX", _SC_SEM_VALUE_MAX);
+    initConstant(env, c, "_SC_SHARED_MEMORY_OBJECTS", _SC_SHARED_MEMORY_OBJECTS);
+    initConstant(env, c, "_SC_SIGQUEUE_MAX", _SC_SIGQUEUE_MAX);
+    initConstant(env, c, "_SC_STREAM_MAX", _SC_STREAM_MAX);
+    initConstant(env, c, "_SC_SYNCHRONIZED_IO", _SC_SYNCHRONIZED_IO);
+    initConstant(env, c, "_SC_THREADS", _SC_THREADS);
+    initConstant(env, c, "_SC_THREAD_ATTR_STACKADDR", _SC_THREAD_ATTR_STACKADDR);
+    initConstant(env, c, "_SC_THREAD_ATTR_STACKSIZE", _SC_THREAD_ATTR_STACKSIZE);
+    initConstant(env, c, "_SC_THREAD_DESTRUCTOR_ITERATIONS", _SC_THREAD_DESTRUCTOR_ITERATIONS);
+    initConstant(env, c, "_SC_THREAD_KEYS_MAX", _SC_THREAD_KEYS_MAX);
+    initConstant(env, c, "_SC_THREAD_PRIORITY_SCHEDULING", _SC_THREAD_PRIORITY_SCHEDULING);
+    initConstant(env, c, "_SC_THREAD_PRIO_INHERIT", _SC_THREAD_PRIO_INHERIT);
+    initConstant(env, c, "_SC_THREAD_PRIO_PROTECT", _SC_THREAD_PRIO_PROTECT);
+    initConstant(env, c, "_SC_THREAD_SAFE_FUNCTIONS", _SC_THREAD_SAFE_FUNCTIONS);
+    initConstant(env, c, "_SC_THREAD_STACK_MIN", _SC_THREAD_STACK_MIN);
+    initConstant(env, c, "_SC_THREAD_THREADS_MAX", _SC_THREAD_THREADS_MAX);
+    initConstant(env, c, "_SC_TIMERS", _SC_TIMERS);
+    initConstant(env, c, "_SC_TIMER_MAX", _SC_TIMER_MAX);
+    initConstant(env, c, "_SC_TTY_NAME_MAX", _SC_TTY_NAME_MAX);
+    initConstant(env, c, "_SC_TZNAME_MAX", _SC_TZNAME_MAX);
+    initConstant(env, c, "_SC_VERSION", _SC_VERSION);
+    initConstant(env, c, "_SC_XBS5_ILP32_OFF32", _SC_XBS5_ILP32_OFF32);
+    initConstant(env, c, "_SC_XBS5_ILP32_OFFBIG", _SC_XBS5_ILP32_OFFBIG);
+    initConstant(env, c, "_SC_XBS5_LP64_OFF64", _SC_XBS5_LP64_OFF64);
+    initConstant(env, c, "_SC_XBS5_LPBIG_OFFBIG", _SC_XBS5_LPBIG_OFFBIG);
+    initConstant(env, c, "_SC_XOPEN_CRYPT", _SC_XOPEN_CRYPT);
+    initConstant(env, c, "_SC_XOPEN_ENH_I18N", _SC_XOPEN_ENH_I18N);
+    initConstant(env, c, "_SC_XOPEN_LEGACY", _SC_XOPEN_LEGACY);
+    initConstant(env, c, "_SC_XOPEN_REALTIME", _SC_XOPEN_REALTIME);
+    initConstant(env, c, "_SC_XOPEN_REALTIME_THREADS", _SC_XOPEN_REALTIME_THREADS);
+    initConstant(env, c, "_SC_XOPEN_SHM", _SC_XOPEN_SHM);
+    initConstant(env, c, "_SC_XOPEN_UNIX", _SC_XOPEN_UNIX);
+    initConstant(env, c, "_SC_XOPEN_VERSION", _SC_XOPEN_VERSION);
+    initConstant(env, c, "_SC_XOPEN_XCU_VERSION", _SC_XOPEN_XCU_VERSION);
+}
+
+static JNINativeMethod gMethods[] = {
+    NATIVE_METHOD(OsConstants, initConstants, "()V"),
+};
+
+void register_android_system_OsConstants(JNIEnv* env) {
+    // [ravenwood-change] -- method moved to the nested class
+    jniRegisterNativeMethods(env, "android/system/OsConstants$Native", gMethods, NELEM(gMethods));
+}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
new file mode 100644
index 0000000..34cf9f9
--- /dev/null
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+// Defined in ravenwood_os_constants.cpp
+void register_android_system_OsConstants(JNIEnv* env);
+
+// ---- Exception related ----
+
+static void throwErrnoException(JNIEnv* env, const char* functionName) {
+    int error = errno;
+    jniThrowErrnoException(env, functionName, error);
+}
+
+template <typename rc_t>
+static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) {
+    if (rc == rc_t(-1)) {
+        throwErrnoException(env, name);
+    }
+    return rc;
+}
+
+// ---- JNI methods ----
+
+typedef void (*FreeFunction)(void*);
+
+static void nApplyFreeFunction(JNIEnv*, jclass, jlong freeFunction, jlong ptr) {
+    void* nativePtr = reinterpret_cast<void*>(static_cast<uintptr_t>(ptr));
+    FreeFunction nativeFreeFunction
+        = reinterpret_cast<FreeFunction>(static_cast<uintptr_t>(freeFunction));
+    nativeFreeFunction(nativePtr);
+}
+
+static jint nFcntlInt(JNIEnv* env, jclass, jint fd, jint cmd, jint arg) {
+    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, cmd, arg)));
+}
+
+static jlong nLseek(JNIEnv* env, jclass, jint fd, jlong offset, jint whence) {
+    return throwIfMinusOne(env, "lseek", TEMP_FAILURE_RETRY(lseek(fd, offset, whence)));
+}
+
+static jintArray nPipe2(JNIEnv* env, jclass, jint flags) {
+    int fds[2];
+    throwIfMinusOne(env, "pipe2", TEMP_FAILURE_RETRY(pipe2(fds, flags)));
+
+    jintArray result;
+    result = env->NewIntArray(2);
+    if (result == NULL) {
+        return NULL; /* out of memory error thrown */
+    }
+    env->SetIntArrayRegion(result, 0, 2, fds);
+    return result;
+}
+
+static jlong nDup(JNIEnv* env, jclass, jint fd) {
+    return throwIfMinusOne(env, "fcntl", TEMP_FAILURE_RETRY(fcntl(fd, F_DUPFD_CLOEXEC, 0)));
+}
+
+// ---- Registration ----
+
+static const JNINativeMethod sMethods[] =
+{
+    { "applyFreeFunction", "(JJ)V", (void*)nApplyFreeFunction },
+    { "nFcntlInt", "(III)I", (void*)nFcntlInt },
+    { "nLseek", "(IJI)J", (void*)nLseek },
+    { "nPipe2", "(I)[I", (void*)nPipe2 },
+    { "nDup", "(I)I", (void*)nDup },
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
+{
+    JNIEnv* env = NULL;
+    jint result = -1;
+
+    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
+        ALOGE("GetEnv failed!");
+        return result;
+    }
+    ALOG_ASSERT(env, "Could not retrieve the env!");
+
+    ALOGI("%s: JNI_OnLoad", __FILE__);
+
+    jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/common/RavenwoodRuntimeNative",
+            sMethods, NELEM(sMethods));
+    if (res < 0) {
+        return res;
+    }
+
+    register_android_system_OsConstants(env);
+
+    return JNI_VERSION_1_4;
+}
diff --git a/ravenwood/runtime-test/Android.bp b/ravenwood/runtime-test/Android.bp
new file mode 100644
index 0000000..4102920
--- /dev/null
+++ b/ravenwood/runtime-test/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_ravenwood_test {
+    name: "RavenwoodRuntimeTest",
+
+    static_libs: [
+        "androidx.annotation_annotation",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+    ],
+    srcs: [
+        "test/**/*.java",
+    ],
+    // sdk_version: "module_current",
+    auto_gen_config: true,
+}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
new file mode 100644
index 0000000..3332e24
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsConstantsTest.java
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.runtimetest;
+
+// Copied from libcore/luni/src/test/java/libcore/android/system/OsConstantsTest.java
+
+import static android.system.OsConstants.CAP_TO_INDEX;
+import static android.system.OsConstants.CAP_TO_MASK;
+import static android.system.OsConstants.S_ISBLK;
+import static android.system.OsConstants.S_ISCHR;
+import static android.system.OsConstants.S_ISDIR;
+import static android.system.OsConstants.S_ISFIFO;
+import static android.system.OsConstants.S_ISLNK;
+import static android.system.OsConstants.S_ISREG;
+import static android.system.OsConstants.S_ISSOCK;
+import static android.system.OsConstants.WCOREDUMP;
+import static android.system.OsConstants.WEXITSTATUS;
+import static android.system.OsConstants.WIFEXITED;
+import static android.system.OsConstants.WIFSIGNALED;
+import static android.system.OsConstants.WIFSTOPPED;
+import static android.system.OsConstants.WSTOPSIG;
+import static android.system.OsConstants.WTERMSIG;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.system.OsConstants;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class OsConstantsTest {
+
+    // http://b/15602893
+    @Test
+    public void testBug15602893() {
+        assertTrue(OsConstants.RT_SCOPE_HOST > 0);
+        assertTrue(OsConstants.RT_SCOPE_LINK > 0);
+        assertTrue(OsConstants.RT_SCOPE_SITE > 0);
+
+        assertTrue(OsConstants.IFA_F_TENTATIVE > 0);
+    }
+
+    // introduced for http://b/30402085
+    @Test
+    public void testTcpUserTimeoutIsDefined() {
+        assertTrue(OsConstants.TCP_USER_TIMEOUT > 0);
+    }
+
+    /**
+     * Verifies equality assertions given in the documentation for
+     * {@link OsConstants#SOCK_CLOEXEC} and {@link OsConstants#SOCK_NONBLOCK}.
+     */
+    @Test
+    public void testConstantsEqual() {
+        assertEquals(OsConstants.O_CLOEXEC,  OsConstants.SOCK_CLOEXEC);
+        assertEquals(OsConstants.O_NONBLOCK, OsConstants.SOCK_NONBLOCK);
+    }
+
+    @Test
+    public void test_CAP_constants() {
+        assertEquals(0,  OsConstants.CAP_CHOWN);
+        assertEquals(1,  OsConstants.CAP_DAC_OVERRIDE);
+        assertEquals(2,  OsConstants.CAP_DAC_READ_SEARCH);
+        assertEquals(3,  OsConstants.CAP_FOWNER);
+        assertEquals(4,  OsConstants.CAP_FSETID);
+        assertEquals(5,  OsConstants.CAP_KILL);
+        assertEquals(6,  OsConstants.CAP_SETGID);
+        assertEquals(7,  OsConstants.CAP_SETUID);
+        assertEquals(8,  OsConstants.CAP_SETPCAP);
+        assertEquals(9,  OsConstants.CAP_LINUX_IMMUTABLE);
+        assertEquals(10, OsConstants.CAP_NET_BIND_SERVICE);
+        assertEquals(11, OsConstants.CAP_NET_BROADCAST);
+        assertEquals(12, OsConstants.CAP_NET_ADMIN);
+        assertEquals(13, OsConstants.CAP_NET_RAW);
+        assertEquals(14, OsConstants.CAP_IPC_LOCK);
+        assertEquals(15, OsConstants.CAP_IPC_OWNER);
+        assertEquals(16, OsConstants.CAP_SYS_MODULE);
+        assertEquals(17, OsConstants.CAP_SYS_RAWIO);
+        assertEquals(18, OsConstants.CAP_SYS_CHROOT);
+        assertEquals(19, OsConstants.CAP_SYS_PTRACE);
+        assertEquals(20, OsConstants.CAP_SYS_PACCT);
+        assertEquals(21, OsConstants.CAP_SYS_ADMIN);
+        assertEquals(22, OsConstants.CAP_SYS_BOOT);
+        assertEquals(23, OsConstants.CAP_SYS_NICE);
+        assertEquals(24, OsConstants.CAP_SYS_RESOURCE);
+        assertEquals(25, OsConstants.CAP_SYS_TIME);
+        assertEquals(26, OsConstants.CAP_SYS_TTY_CONFIG);
+        assertEquals(27, OsConstants.CAP_MKNOD);
+        assertEquals(28, OsConstants.CAP_LEASE);
+        assertEquals(29, OsConstants.CAP_AUDIT_WRITE);
+        assertEquals(30, OsConstants.CAP_AUDIT_CONTROL);
+        assertEquals(31, OsConstants.CAP_SETFCAP);
+        assertEquals(32, OsConstants.CAP_MAC_OVERRIDE);
+        assertEquals(33, OsConstants.CAP_MAC_ADMIN);
+        assertEquals(34, OsConstants.CAP_SYSLOG);
+        assertEquals(35, OsConstants.CAP_WAKE_ALARM);
+        assertEquals(36, OsConstants.CAP_BLOCK_SUSPEND);
+        // last constant
+        assertEquals(40, OsConstants.CAP_LAST_CAP);
+    }
+
+    @Test
+    public void test_CAP_TO_INDEX() {
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_CHOWN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_OVERRIDE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_DAC_READ_SEARCH));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FOWNER));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_FSETID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_KILL));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETGID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETUID));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETPCAP));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LINUX_IMMUTABLE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BIND_SERVICE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_BROADCAST));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_ADMIN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_NET_RAW));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_LOCK));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_IPC_OWNER));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_MODULE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RAWIO));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_CHROOT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PTRACE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_PACCT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_ADMIN));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_BOOT));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_NICE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_RESOURCE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TIME));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SYS_TTY_CONFIG));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_MKNOD));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_LEASE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_WRITE));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_AUDIT_CONTROL));
+        assertEquals(0, CAP_TO_INDEX(OsConstants.CAP_SETFCAP));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_OVERRIDE));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_MAC_ADMIN));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_SYSLOG));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_WAKE_ALARM));
+        assertEquals(1, CAP_TO_INDEX(OsConstants.CAP_BLOCK_SUSPEND));
+    }
+
+    @Test
+    public void test_CAP_TO_MASK() {
+        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_CHOWN));
+        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_DAC_OVERRIDE));
+        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_DAC_READ_SEARCH));
+        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_FOWNER));
+        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_FSETID));
+        assertEquals(1 << 5,  CAP_TO_MASK(OsConstants.CAP_KILL));
+        assertEquals(1 << 6,  CAP_TO_MASK(OsConstants.CAP_SETGID));
+        assertEquals(1 << 7,  CAP_TO_MASK(OsConstants.CAP_SETUID));
+        assertEquals(1 << 8,  CAP_TO_MASK(OsConstants.CAP_SETPCAP));
+        assertEquals(1 << 9,  CAP_TO_MASK(OsConstants.CAP_LINUX_IMMUTABLE));
+        assertEquals(1 << 10, CAP_TO_MASK(OsConstants.CAP_NET_BIND_SERVICE));
+        assertEquals(1 << 11, CAP_TO_MASK(OsConstants.CAP_NET_BROADCAST));
+        assertEquals(1 << 12, CAP_TO_MASK(OsConstants.CAP_NET_ADMIN));
+        assertEquals(1 << 13, CAP_TO_MASK(OsConstants.CAP_NET_RAW));
+        assertEquals(1 << 14, CAP_TO_MASK(OsConstants.CAP_IPC_LOCK));
+        assertEquals(1 << 15, CAP_TO_MASK(OsConstants.CAP_IPC_OWNER));
+        assertEquals(1 << 16, CAP_TO_MASK(OsConstants.CAP_SYS_MODULE));
+        assertEquals(1 << 17, CAP_TO_MASK(OsConstants.CAP_SYS_RAWIO));
+        assertEquals(1 << 18, CAP_TO_MASK(OsConstants.CAP_SYS_CHROOT));
+        assertEquals(1 << 19, CAP_TO_MASK(OsConstants.CAP_SYS_PTRACE));
+        assertEquals(1 << 20, CAP_TO_MASK(OsConstants.CAP_SYS_PACCT));
+        assertEquals(1 << 21, CAP_TO_MASK(OsConstants.CAP_SYS_ADMIN));
+        assertEquals(1 << 22, CAP_TO_MASK(OsConstants.CAP_SYS_BOOT));
+        assertEquals(1 << 23, CAP_TO_MASK(OsConstants.CAP_SYS_NICE));
+        assertEquals(1 << 24, CAP_TO_MASK(OsConstants.CAP_SYS_RESOURCE));
+        assertEquals(1 << 25, CAP_TO_MASK(OsConstants.CAP_SYS_TIME));
+        assertEquals(1 << 26, CAP_TO_MASK(OsConstants.CAP_SYS_TTY_CONFIG));
+        assertEquals(1 << 27, CAP_TO_MASK(OsConstants.CAP_MKNOD));
+        assertEquals(1 << 28, CAP_TO_MASK(OsConstants.CAP_LEASE));
+        assertEquals(1 << 29, CAP_TO_MASK(OsConstants.CAP_AUDIT_WRITE));
+        assertEquals(1 << 30, CAP_TO_MASK(OsConstants.CAP_AUDIT_CONTROL));
+        assertEquals(1 << 31, CAP_TO_MASK(OsConstants.CAP_SETFCAP));
+        assertEquals(1 << 0,  CAP_TO_MASK(OsConstants.CAP_MAC_OVERRIDE));
+        assertEquals(1 << 1,  CAP_TO_MASK(OsConstants.CAP_MAC_ADMIN));
+        assertEquals(1 << 2,  CAP_TO_MASK(OsConstants.CAP_SYSLOG));
+        assertEquals(1 << 3,  CAP_TO_MASK(OsConstants.CAP_WAKE_ALARM));
+        assertEquals(1 << 4,  CAP_TO_MASK(OsConstants.CAP_BLOCK_SUSPEND));
+    }
+
+    @Test
+    public void test_S_ISLNK() {
+        assertTrue(S_ISLNK(OsConstants.S_IFLNK));
+
+        assertFalse(S_ISLNK(OsConstants.S_IFBLK));
+        assertFalse(S_ISLNK(OsConstants.S_IFCHR));
+        assertFalse(S_ISLNK(OsConstants.S_IFDIR));
+        assertFalse(S_ISLNK(OsConstants.S_IFIFO));
+        assertFalse(S_ISLNK(OsConstants.S_IFMT));
+        assertFalse(S_ISLNK(OsConstants.S_IFREG));
+        assertFalse(S_ISLNK(OsConstants.S_IFSOCK));
+        assertFalse(S_ISLNK(OsConstants.S_IRGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IROTH));
+        assertFalse(S_ISLNK(OsConstants.S_IRUSR));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXG));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXO));
+        assertFalse(S_ISLNK(OsConstants.S_IRWXU));
+        assertFalse(S_ISLNK(OsConstants.S_ISGID));
+        assertFalse(S_ISLNK(OsConstants.S_ISUID));
+        assertFalse(S_ISLNK(OsConstants.S_ISVTX));
+        assertFalse(S_ISLNK(OsConstants.S_IWGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IWOTH));
+        assertFalse(S_ISLNK(OsConstants.S_IWUSR));
+        assertFalse(S_ISLNK(OsConstants.S_IXGRP));
+        assertFalse(S_ISLNK(OsConstants.S_IXOTH));
+        assertFalse(S_ISLNK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISREG() {
+        assertTrue(S_ISREG(OsConstants.S_IFREG));
+
+        assertFalse(S_ISREG(OsConstants.S_IFBLK));
+        assertFalse(S_ISREG(OsConstants.S_IFCHR));
+        assertFalse(S_ISREG(OsConstants.S_IFDIR));
+        assertFalse(S_ISREG(OsConstants.S_IFIFO));
+        assertFalse(S_ISREG(OsConstants.S_IFLNK));
+        assertFalse(S_ISREG(OsConstants.S_IFMT));
+        assertFalse(S_ISREG(OsConstants.S_IFSOCK));
+        assertFalse(S_ISREG(OsConstants.S_IRGRP));
+        assertFalse(S_ISREG(OsConstants.S_IROTH));
+        assertFalse(S_ISREG(OsConstants.S_IRUSR));
+        assertFalse(S_ISREG(OsConstants.S_IRWXG));
+        assertFalse(S_ISREG(OsConstants.S_IRWXO));
+        assertFalse(S_ISREG(OsConstants.S_IRWXU));
+        assertFalse(S_ISREG(OsConstants.S_ISGID));
+        assertFalse(S_ISREG(OsConstants.S_ISUID));
+        assertFalse(S_ISREG(OsConstants.S_ISVTX));
+        assertFalse(S_ISREG(OsConstants.S_IWGRP));
+        assertFalse(S_ISREG(OsConstants.S_IWOTH));
+        assertFalse(S_ISREG(OsConstants.S_IWUSR));
+        assertFalse(S_ISREG(OsConstants.S_IXGRP));
+        assertFalse(S_ISREG(OsConstants.S_IXOTH));
+        assertFalse(S_ISREG(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISDIR() {
+        assertTrue(S_ISDIR(OsConstants.S_IFDIR));
+
+        assertFalse(S_ISDIR(OsConstants.S_IFBLK));
+        assertFalse(S_ISDIR(OsConstants.S_IFCHR));
+        assertFalse(S_ISDIR(OsConstants.S_IFIFO));
+        assertFalse(S_ISDIR(OsConstants.S_IFLNK));
+        assertFalse(S_ISDIR(OsConstants.S_IFMT));
+        assertFalse(S_ISDIR(OsConstants.S_IFREG));
+        assertFalse(S_ISDIR(OsConstants.S_IFSOCK));
+        assertFalse(S_ISDIR(OsConstants.S_IRGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IROTH));
+        assertFalse(S_ISDIR(OsConstants.S_IRUSR));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXG));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXO));
+        assertFalse(S_ISDIR(OsConstants.S_IRWXU));
+        assertFalse(S_ISDIR(OsConstants.S_ISGID));
+        assertFalse(S_ISDIR(OsConstants.S_ISUID));
+        assertFalse(S_ISDIR(OsConstants.S_ISVTX));
+        assertFalse(S_ISDIR(OsConstants.S_IWGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IWOTH));
+        assertFalse(S_ISDIR(OsConstants.S_IWUSR));
+        assertFalse(S_ISDIR(OsConstants.S_IXGRP));
+        assertFalse(S_ISDIR(OsConstants.S_IXOTH));
+        assertFalse(S_ISDIR(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISCHR() {
+        assertTrue(S_ISCHR(OsConstants.S_IFCHR));
+
+        assertFalse(S_ISCHR(OsConstants.S_IFBLK));
+        assertFalse(S_ISCHR(OsConstants.S_IFDIR));
+        assertFalse(S_ISCHR(OsConstants.S_IFIFO));
+        assertFalse(S_ISCHR(OsConstants.S_IFLNK));
+        assertFalse(S_ISCHR(OsConstants.S_IFMT));
+        assertFalse(S_ISCHR(OsConstants.S_IFREG));
+        assertFalse(S_ISCHR(OsConstants.S_IFSOCK));
+        assertFalse(S_ISCHR(OsConstants.S_IRGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IROTH));
+        assertFalse(S_ISCHR(OsConstants.S_IRUSR));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXG));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXO));
+        assertFalse(S_ISCHR(OsConstants.S_IRWXU));
+        assertFalse(S_ISCHR(OsConstants.S_ISGID));
+        assertFalse(S_ISCHR(OsConstants.S_ISUID));
+        assertFalse(S_ISCHR(OsConstants.S_ISVTX));
+        assertFalse(S_ISCHR(OsConstants.S_IWGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IWOTH));
+        assertFalse(S_ISCHR(OsConstants.S_IWUSR));
+        assertFalse(S_ISCHR(OsConstants.S_IXGRP));
+        assertFalse(S_ISCHR(OsConstants.S_IXOTH));
+        assertFalse(S_ISCHR(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISBLK() {
+        assertTrue (S_ISBLK(OsConstants.S_IFBLK));
+
+        assertFalse(S_ISBLK(OsConstants.S_IFCHR));
+        assertFalse(S_ISBLK(OsConstants.S_IFDIR));
+        assertFalse(S_ISBLK(OsConstants.S_IFIFO));
+        assertFalse(S_ISBLK(OsConstants.S_IFLNK));
+        assertFalse(S_ISBLK(OsConstants.S_IFMT));
+        assertFalse(S_ISBLK(OsConstants.S_IFREG));
+        assertFalse(S_ISBLK(OsConstants.S_IFSOCK));
+        assertFalse(S_ISBLK(OsConstants.S_IRGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IROTH));
+        assertFalse(S_ISBLK(OsConstants.S_IRUSR));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXG));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXO));
+        assertFalse(S_ISBLK(OsConstants.S_IRWXU));
+        assertFalse(S_ISBLK(OsConstants.S_ISGID));
+        assertFalse(S_ISBLK(OsConstants.S_ISUID));
+        assertFalse(S_ISBLK(OsConstants.S_ISVTX));
+        assertFalse(S_ISBLK(OsConstants.S_IWGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IWOTH));
+        assertFalse(S_ISBLK(OsConstants.S_IWUSR));
+        assertFalse(S_ISBLK(OsConstants.S_IXGRP));
+        assertFalse(S_ISBLK(OsConstants.S_IXOTH));
+        assertFalse(S_ISBLK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISFIFO() {
+        assertTrue(S_ISFIFO(OsConstants.S_IFIFO));
+
+        assertFalse(S_ISFIFO(OsConstants.S_IFBLK));
+        assertFalse(S_ISFIFO(OsConstants.S_IFCHR));
+        assertFalse(S_ISFIFO(OsConstants.S_IFDIR));
+        assertFalse(S_ISFIFO(OsConstants.S_IFLNK));
+        assertFalse(S_ISFIFO(OsConstants.S_IFMT));
+        assertFalse(S_ISFIFO(OsConstants.S_IFREG));
+        assertFalse(S_ISFIFO(OsConstants.S_IFSOCK));
+        assertFalse(S_ISFIFO(OsConstants.S_IRGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IROTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IRUSR));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXG));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXO));
+        assertFalse(S_ISFIFO(OsConstants.S_IRWXU));
+        assertFalse(S_ISFIFO(OsConstants.S_ISGID));
+        assertFalse(S_ISFIFO(OsConstants.S_ISUID));
+        assertFalse(S_ISFIFO(OsConstants.S_ISVTX));
+        assertFalse(S_ISFIFO(OsConstants.S_IWGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IWOTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IWUSR));
+        assertFalse(S_ISFIFO(OsConstants.S_IXGRP));
+        assertFalse(S_ISFIFO(OsConstants.S_IXOTH));
+        assertFalse(S_ISFIFO(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_S_ISSOCK() {
+        assertTrue(S_ISSOCK(OsConstants.S_IFSOCK));
+
+        assertFalse(S_ISSOCK(OsConstants.S_IFBLK));
+        assertFalse(S_ISSOCK(OsConstants.S_IFCHR));
+        assertFalse(S_ISSOCK(OsConstants.S_IFDIR));
+        assertFalse(S_ISSOCK(OsConstants.S_IFIFO));
+        assertFalse(S_ISSOCK(OsConstants.S_IFLNK));
+        assertFalse(S_ISSOCK(OsConstants.S_IFMT));
+        assertFalse(S_ISSOCK(OsConstants.S_IFREG));
+        assertFalse(S_ISSOCK(OsConstants.S_IRGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IROTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IRUSR));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXG));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXO));
+        assertFalse(S_ISSOCK(OsConstants.S_IRWXU));
+        assertFalse(S_ISSOCK(OsConstants.S_ISGID));
+        assertFalse(S_ISSOCK(OsConstants.S_ISUID));
+        assertFalse(S_ISSOCK(OsConstants.S_ISVTX));
+        assertFalse(S_ISSOCK(OsConstants.S_IWGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IWOTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IWUSR));
+        assertFalse(S_ISSOCK(OsConstants.S_IXGRP));
+        assertFalse(S_ISSOCK(OsConstants.S_IXOTH));
+        assertFalse(S_ISSOCK(OsConstants.S_IXUSR));
+    }
+
+    @Test
+    public void test_WEXITSTATUS() {
+        assertEquals(0, WEXITSTATUS(0x0000));
+        assertEquals(0, WEXITSTATUS(0x00DE));
+        assertEquals(0xF0, WEXITSTATUS(0xF000));
+        assertEquals(0xAB, WEXITSTATUS(0xAB12));
+    }
+
+    @Test
+    public void test_WCOREDUMP() {
+        assertFalse(WCOREDUMP(0));
+        assertTrue(WCOREDUMP(0x80));
+    }
+
+    @Test
+    public void test_WTERMSIG() {
+        assertEquals(0, WTERMSIG(0));
+        assertEquals(0x7f, WTERMSIG(0x7f));
+    }
+
+    @Test
+    public void test_WSTOPSIG() {
+        assertEquals(0, WSTOPSIG(0x0000));
+        assertEquals(0, WSTOPSIG(0x00DE));
+        assertEquals(0xF0, WSTOPSIG(0xF000));
+        assertEquals(0xAB, WSTOPSIG(0xAB12));
+    }
+
+
+    @Test
+    public void test_WIFEXITED() {
+        assertTrue(WIFEXITED(0));
+        assertFalse(WIFEXITED(0x7f));
+    }
+
+    @Test
+    public void test_WIFSTOPPED() {
+        assertFalse(WIFSTOPPED(0));
+        assertTrue(WIFSTOPPED(0x7f));
+    }
+
+    @Test
+    public void test_WIFSIGNALED() {
+        assertFalse(WIFSIGNALED(0));
+        assertTrue(WIFSIGNALED(1));
+    }
+}
diff --git a/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
new file mode 100644
index 0000000..b5038e6
--- /dev/null
+++ b/ravenwood/runtime-test/test/com/android/ravenwood/runtimetest/OsTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2024 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.ravenwood.runtimetest;
+
+import static org.junit.Assert.assertEquals;
+
+import android.system.Os;
+import android.system.OsConstants;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.ravenwood.common.JvmWorkaround;
+import com.android.ravenwood.common.RavenwoodRuntimeNative;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+@RunWith(AndroidJUnit4.class)
+public class OsTest {
+    public interface ConsumerWithThrow<T> {
+        void accept(T var1) throws Exception;
+    }
+
+    private void withTestFile(ConsumerWithThrow<FileDescriptor> consumer) throws Exception {
+        File file = File.createTempFile("osTest", "bin");
+        try (var raf = new RandomAccessFile(file, "rw")) {
+            var fd = raf.getFD();
+
+            try (var os = new FileOutputStream(fd)) {
+                os.write(1);
+                os.write(2);
+                os.write(3);
+                os.write(4);
+
+                consumer.accept(fd);
+            }
+        }
+    }
+
+    @Test
+    public void testLseek() throws Exception {
+        withTestFile((fd) -> {
+            assertEquals(4, Os.lseek(fd, 4, OsConstants.SEEK_SET));
+            assertEquals(4, Os.lseek(fd, 0, OsConstants.SEEK_CUR));
+            assertEquals(6, Os.lseek(fd, 2, OsConstants.SEEK_CUR));
+        });
+    }
+
+    @Test
+    public void testDup() throws Exception {
+        withTestFile((fd) -> {
+            var dup = Os.dup(fd);
+
+            checkAreDup(fd, dup);
+        });
+    }
+
+    @Test
+    public void testPipe2() throws Exception {
+        var fds = Os.pipe2(0);
+
+        write(fds[1], 123);
+        assertEquals(123, read(fds[0]));
+    }
+
+    @Test
+    public void testFcntlInt() throws Exception {
+        withTestFile((fd) -> {
+            var dupInt = Os.fcntlInt(fd, 0, 0);
+
+            var dup = new FileDescriptor();
+            JvmWorkaround.getInstance().setFdInt(dup, dupInt);
+
+            checkAreDup(fd, dup);
+        });
+    }
+
+    private static void write(FileDescriptor fd, int oneByte)  throws IOException {
+        // Create a dup to avoid closing the FD.
+        try (var dup = new FileOutputStream(RavenwoodRuntimeNative.dup(fd))) {
+            dup.write(oneByte);
+        }
+    }
+
+    private static int read(FileDescriptor fd) throws IOException {
+        // Create a dup to avoid closing the FD.
+        try (var dup = new FileInputStream(RavenwoodRuntimeNative.dup(fd))) {
+            return dup.read();
+        }
+    }
+
+    private static void checkAreDup(FileDescriptor fd1, FileDescriptor fd2) throws Exception {
+        assertEquals(4, Os.lseek(fd1, 4, OsConstants.SEEK_SET));
+        assertEquals(4, Os.lseek(fd1, 0, OsConstants.SEEK_CUR));
+
+        // Dup'ed FD shares the same position.
+        assertEquals(4, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+
+        assertEquals(6, Os.lseek(fd1, 2, OsConstants.SEEK_CUR));
+        assertEquals(6, Os.lseek(fd2, 0, OsConstants.SEEK_CUR));
+    }
+}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 110100a..8d14c1d 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -35,6 +35,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
+import android.os.Build;
 import android.os.ParcelFileDescriptor;
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
@@ -189,6 +190,9 @@
 
     @Override
     protected int getMaximumTemporaryServiceDurationMs() {
+        if (Build.isDebuggable()) {
+            return Integer.MAX_VALUE;
+        }
         return MAX_TEMPORARY_SERVICE_DURATION_MS;
     }