diff options
author | 2024-05-16 15:44:38 -0700 | |
---|---|---|
committer | 2024-05-23 15:34:29 +0000 | |
commit | df9cd260bbea991ca07f96f8a6ccd2f3682ed5f0 (patch) | |
tree | ff4bf1e383915455113d4bd54c3c252a59bc3171 /ravenwood/runtime-common-src | |
parent | 820d81beb71155f29fab8c1ee4f5c9edb2317954 (diff) |
[Ravenwood] Internal clean up, more PFD APIs, OsConstants, etc
- Support a few more ParcelFileDescriptor APIs.
- Support OsConstants and a couple of Os APIs.
- Also clean up f/b/ravenwood. Now we have "runtime-common" library
that can be used from different components of Ravenwood infra,
with native code support.
Bug: 292141694
Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh
Test: CtsOsTestCases (on tree hugger)
Flag: EXEMPT hostside test change only
Change-Id: I259a04201e05e615e17da0529cb500a102887ec9
Diffstat (limited to 'ravenwood/runtime-common-src')
6 files changed, 471 insertions, 0 deletions
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 000000000000..ee280991216a --- /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 000000000000..9aedaab5b911 --- /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 000000000000..61d54cba8b54 --- /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 000000000000..c8cc8d9fe273 --- /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 000000000000..7b0cebcecc1a --- /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 000000000000..65402219ebee --- /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); + } +} |