diff options
14 files changed, 426 insertions, 432 deletions
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index e53873b5622e..89b727cdd655 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -21,13 +21,10 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.ravenwood.annotation.RavenwoodKeepWholeClass; -import android.ravenwood.annotation.RavenwoodRedirect; -import android.ravenwood.annotation.RavenwoodRedirectionClass; import android.util.Log; import android.util.MutableInt; import com.android.internal.annotations.GuardedBy; -import com.android.internal.ravenwood.RavenwoodEnvironment; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -40,8 +37,6 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.Map; -import java.util.function.Predicate; /** * Gives access to the system properties store. The system properties @@ -58,7 +53,6 @@ import java.util.function.Predicate; */ @SystemApi @RavenwoodKeepWholeClass -@RavenwoodRedirectionClass("SystemProperties_host") public class SystemProperties { private static final String TAG = "SystemProperties"; private static final boolean TRACK_KEY_ACCESS = false; @@ -76,7 +70,7 @@ public class SystemProperties { @UnsupportedAppUsage @GuardedBy("sChangeCallbacks") - static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); + private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); @GuardedBy("sRoReads") private static final HashMap<String, MutableInt> sRoReads = @@ -102,19 +96,6 @@ public class SystemProperties { } } - /** @hide */ - @RavenwoodRedirect - public static void init$ravenwood(Map<String, String> values, - Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) { - throw RavenwoodEnvironment.notSupportedOnDevice(); - } - - /** @hide */ - @RavenwoodRedirect - public static void reset$ravenwood() { - throw RavenwoodEnvironment.notSupportedOnDevice(); - } - // The one-argument version of native_get used to be a regular native function. Nowadays, // we use the two-argument form of native_get all the time, but we can't just delete the // one-argument overload: apps use it via reflection, as the UnsupportedAppUsage annotation @@ -126,46 +107,34 @@ public class SystemProperties { @FastNative @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - @RavenwoodRedirect private static native String native_get(String key, String def); @FastNative @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - @RavenwoodRedirect private static native int native_get_int(String key, int def); @FastNative @UnsupportedAppUsage - @RavenwoodRedirect private static native long native_get_long(String key, long def); @FastNative @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - @RavenwoodRedirect private static native boolean native_get_boolean(String key, boolean def); @FastNative - @RavenwoodRedirect private static native long native_find(String name); @FastNative - @RavenwoodRedirect private static native String native_get(long handle); @CriticalNative - @RavenwoodRedirect private static native int native_get_int(long handle, int def); @CriticalNative - @RavenwoodRedirect private static native long native_get_long(long handle, long def); @CriticalNative - @RavenwoodRedirect private static native boolean native_get_boolean(long handle, boolean def); // _NOT_ FastNative: native_set performs IPC and can block @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - @RavenwoodRedirect private static native void native_set(String key, String def); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) - @RavenwoodRedirect private static native void native_add_change_callback(); - @RavenwoodRedirect private static native void native_report_sysprop_change(); /** @@ -301,7 +270,7 @@ public class SystemProperties { } @SuppressWarnings("unused") // Called from native code. - static void callChangeCallbacks() { + private static void callChangeCallbacks() { ArrayList<Runnable> callbacks = null; synchronized (sChangeCallbacks) { //Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!"); @@ -327,6 +296,16 @@ public class SystemProperties { } /** + * Clear all callback changes. + * @hide + */ + public static void clearChangeCallbacksForTest() { + synchronized (sChangeCallbacks) { + sChangeCallbacks.clear(); + } + } + + /** * Notifies listeners that a system property has changed * @hide */ diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp index 88e6fa3028ad..e99d89098a40 100644 --- a/core/jni/android_os_SystemProperties.cpp +++ b/core/jni/android_os_SystemProperties.cpp @@ -34,8 +34,6 @@ #if defined(__BIONIC__) # include <sys/system_properties.h> -#else -struct prop_info; #endif namespace android { @@ -46,7 +44,6 @@ using android::base::ParseBoolResult; template<typename Functor> void ReadProperty(const prop_info* prop, Functor&& functor) { -#if defined(__BIONIC__) auto thunk = [](void* cookie, const char* /*name*/, const char* value, @@ -54,9 +51,6 @@ void ReadProperty(const prop_info* prop, Functor&& functor) std::forward<Functor>(*static_cast<Functor*>(cookie))(value); }; __system_property_read_callback(prop, thunk, &functor); -#else - LOG(FATAL) << "fast property access supported only on device"; -#endif } template<typename Functor> @@ -66,16 +60,11 @@ void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor) if (!key.c_str()) { return; } -#if defined(__BIONIC__) const prop_info* prop = __system_property_find(key.c_str()); if (!prop) { return; } ReadProperty(prop, std::forward<Functor>(functor)); -#else - std::forward<Functor>(functor)( - android::base::GetProperty(key.c_str(), "").c_str()); -#endif } jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ, @@ -132,17 +121,12 @@ jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ, jlong SystemProperties_find(JNIEnv* env, jclass, jstring keyJ) { -#if defined(__BIONIC__) ScopedUtfChars key(env, keyJ); if (!key.c_str()) { return 0; } const prop_info* prop = __system_property_find(key.c_str()); return reinterpret_cast<jlong>(prop); -#else - LOG(FATAL) << "fast property access supported only on device"; - __builtin_unreachable(); // Silence warning -#endif } jstring SystemProperties_getH(JNIEnv* env, jclass clazz, jlong propJ) @@ -198,11 +182,7 @@ void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, // request" failures). errno = 0; bool success; -#if defined(__BIONIC__) success = !__system_property_set(key.c_str(), value_c_str); -#else - success = android::base::SetProperty(key.c_str(), value_c_str); -#endif if (!success) { if (errno != 0) { jniThrowExceptionFmt(env, "java/lang/RuntimeException", diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java index d98120f535d8..75aca1b8820c 100644 --- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java +++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java @@ -23,11 +23,10 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.platform.test.ravenwood.RavenwoodRule; +import android.platform.test.ravenwood.RavenwoodConfig; import androidx.test.filters.SmallTest; -import org.junit.Rule; import org.junit.Test; import java.util.Objects; @@ -35,17 +34,19 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; public class SystemPropertiesTest { - @Rule - public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + private static final String KEY = "sys.testkey"; + private static final String UNSET_KEY = "Aiw7woh6ie4toh7W"; + private static final String PERSIST_KEY = "persist.sys.testkey"; + private static final String NONEXIST_KEY = "doesnotexist_2341431"; + + @RavenwoodConfig.Config + public static final RavenwoodConfig mRavenwood = new RavenwoodConfig.Builder() .setSystemPropertyMutable(KEY, null) .setSystemPropertyMutable(UNSET_KEY, null) .setSystemPropertyMutable(PERSIST_KEY, null) + .setSystemPropertyImmutable(NONEXIST_KEY, null) .build(); - private static final String KEY = "sys.testkey"; - private static final String UNSET_KEY = "Aiw7woh6ie4toh7W"; - private static final String PERSIST_KEY = "persist.sys.testkey"; - @Test @SmallTest public void testStressPersistPropertyConsistency() throws Exception { @@ -117,7 +118,7 @@ public class SystemPropertiesTest { @SmallTest public void testHandle() throws Exception { String value; - SystemProperties.Handle handle = SystemProperties.find("doesnotexist_2341431"); + SystemProperties.Handle handle = SystemProperties.find(NONEXIST_KEY); assertNull(handle); SystemProperties.set(KEY, "abc"); handle = SystemProperties.find(KEY); diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index 10e4f3820cd7..6b1197ad4c18 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -267,25 +267,39 @@ filegroup { visibility: ["//frameworks/base"], } -cc_library_shared { - name: "libravenwood_runtime", - host_supported: true, - +cc_defaults { + name: "ravenwood_jni_defaults", cflags: [ "-Wall", "-Werror", "-Wno-unused-parameter", "-Wthread-safety", ], + static_libs: [ + "libnativehelper_jvm", + ], + shared_libs: [ + "liblog", + ], +} + +// We need this as a separate library because we need to overload the +// sysprop symbols before libbase is loaded into the process +cc_library_host_shared { + name: "libravenwood_sysprop", + defaults: ["ravenwood_jni_defaults"], + srcs: ["runtime-jni/ravenwood_sysprop.cpp"], +} +cc_library_host_shared { + name: "libravenwood_runtime", + defaults: ["ravenwood_jni_defaults"], srcs: [ - "runtime-jni/*.cpp", + "runtime-jni/ravenwood_runtime.cpp", + "runtime-jni/ravenwood_os_constants.cpp", ], - shared_libs: [ "libbase", - "liblog", - "libnativehelper", "libutils", "libcutils", ], @@ -377,8 +391,10 @@ android_ravenwood_libgroup { "z00-all-updatable-modules-system-stubs", ], jni_libs: [ - "libandroid_runtime", + // Libraries has to be loaded in the following order + "libravenwood_sysprop", "libravenwood_runtime", + "libandroid_runtime", ], } diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java index e5486117e7f2..d29b93c0c171 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodNativeLoader.java @@ -43,6 +43,7 @@ public final class RavenwoodNativeLoader { android.util.Log.class, android.os.Parcel.class, android.os.Binder.class, + android.os.SystemProperties.class, android.content.res.ApkAssets.class, android.content.res.AssetManager.class, android.content.res.StringBlock.class, diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java index 40b14dbe6bfd..805b0c161cb3 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java @@ -31,6 +31,7 @@ import android.os.Bundle; import android.os.HandlerThread; import android.os.Looper; import android.os.ServiceManager; +import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -38,6 +39,7 @@ import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.os.RuntimeInit; +import com.android.ravenwood.RavenwoodRuntimeNative; import com.android.ravenwood.common.RavenwoodCommonUtils; import com.android.ravenwood.common.RavenwoodRuntimeException; import com.android.ravenwood.common.SneakyThrow; @@ -68,6 +70,8 @@ public class RavenwoodRuntimeEnvironmentController { } private static final String MAIN_THREAD_NAME = "RavenwoodMain"; + private static final String RAVENWOOD_NATIVE_SYSPROP_NAME = "ravenwood_sysprop"; + private static final String RAVENWOOD_NATIVE_RUNTIME_NAME = "ravenwood_runtime"; /** * When enabled, attempt to dump all thread stacks just before we hit the @@ -118,6 +122,7 @@ public class RavenwoodRuntimeEnvironmentController { } private static RavenwoodConfig sConfig; + private static RavenwoodSystemProperties sProps; private static boolean sInitialized = false; /** @@ -132,6 +137,14 @@ public class RavenwoodRuntimeEnvironmentController { // We haven't initialized liblog yet, so directly write to System.out here. RavenwoodCommonUtils.log(TAG, "globalInit()"); + // Load libravenwood_sysprop first + var libProp = RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_SYSPROP_NAME); + System.load(libProp); + RavenwoodRuntimeNative.reloadNativeLibrary(libProp); + + // Make sure libravenwood_runtime is loaded. + System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME)); + // Do the basic set up for the android sysprops. setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES); @@ -359,12 +372,21 @@ public class RavenwoodRuntimeEnvironmentController { /** * Set the current configuration to the actual SystemProperties. */ - public static void setSystemProperties(RavenwoodSystemProperties ravenwoodSystemProperties) { - var clone = new RavenwoodSystemProperties(ravenwoodSystemProperties, true); + private static void setSystemProperties(RavenwoodSystemProperties systemProperties) { + SystemProperties.clearChangeCallbacksForTest(); + RavenwoodRuntimeNative.clearSystemProperties(); + sProps = new RavenwoodSystemProperties(systemProperties, true); + for (var entry : systemProperties.getValues().entrySet()) { + RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue()); + } + } - android.os.SystemProperties.init$ravenwood( - clone.getValues(), - clone.getKeyReadablePredicate(), - clone.getKeyWritablePredicate()); + @SuppressWarnings("unused") // Called from native code (ravenwood_sysprop.cpp) + private static void checkSystemPropertyAccess(String key, boolean write) { + boolean result = write ? sProps.isKeyWritable(key) : sProps.isKeyReadable(key); + if (!result) { + throw new IllegalArgumentException((write ? "Write" : "Read") + + " access to system property '" + key + "' denied via RavenwoodConfig"); + } } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java index ef8f5840f949..f1e1ef672871 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java @@ -22,7 +22,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.function.Predicate; public class RavenwoodSystemProperties { private volatile boolean mIsImmutable; @@ -31,47 +30,9 @@ public class RavenwoodSystemProperties { /** Set of additional keys that should be considered readable */ private final Set<String> mKeyReadable = new HashSet<>(); - private final Predicate<String> mKeyReadablePredicate = (key) -> { - final String root = getKeyRoot(key); - - if (root.startsWith("debug.")) return true; - - // This set is carefully curated to help identify situations where a test may - // accidentally depend on a default value of an obscure property whose owner hasn't - // decided how Ravenwood should behave. - if (root.startsWith("boot.")) return true; - if (root.startsWith("build.")) return true; - if (root.startsWith("product.")) return true; - if (root.startsWith("soc.")) return true; - if (root.startsWith("system.")) return true; - - switch (key) { - case "gsm.version.baseband": - case "no.such.thing": - case "qemu.sf.lcd_density": - case "ro.bootloader": - case "ro.debuggable": - case "ro.hardware": - case "ro.hw_timeout_multiplier": - case "ro.odm.build.media_performance_class": - case "ro.sf.lcd_density": - case "ro.treble.enabled": - case "ro.vndk.version": - return true; - } - - return mKeyReadable.contains(key); - }; /** Set of additional keys that should be considered writable */ private final Set<String> mKeyWritable = new HashSet<>(); - private final Predicate<String> mKeyWritablePredicate = (key) -> { - final String root = getKeyRoot(key); - - if (root.startsWith("debug.")) return true; - - return mKeyWritable.contains(key); - }; public RavenwoodSystemProperties() { // TODO: load these values from build.prop generated files @@ -121,12 +82,45 @@ public class RavenwoodSystemProperties { return new HashMap<>(mValues); } - public Predicate<String> getKeyReadablePredicate() { - return mKeyReadablePredicate; + public boolean isKeyReadable(String key) { + final String root = getKeyRoot(key); + + if (root.startsWith("debug.")) return true; + + // This set is carefully curated to help identify situations where a test may + // accidentally depend on a default value of an obscure property whose owner hasn't + // decided how Ravenwood should behave. + if (root.startsWith("boot.")) return true; + if (root.startsWith("build.")) return true; + if (root.startsWith("product.")) return true; + if (root.startsWith("soc.")) return true; + if (root.startsWith("system.")) return true; + + switch (key) { + case "gsm.version.baseband": + case "no.such.thing": + case "qemu.sf.lcd_density": + case "ro.bootloader": + case "ro.debuggable": + case "ro.hardware": + case "ro.hw_timeout_multiplier": + case "ro.odm.build.media_performance_class": + case "ro.sf.lcd_density": + case "ro.treble.enabled": + case "ro.vndk.version": + case "ro.icu.data.path": + return true; + } + + return mKeyReadable.contains(key); } - public Predicate<String> getKeyWritablePredicate() { - return mKeyWritablePredicate; + public boolean isKeyWritable(String key) { + final String root = getKeyRoot(key); + + if (root.startsWith("debug.")) return true; + + return mKeyWritable.contains(key); } private static final String[] PARTITIONS = { @@ -208,4 +202,4 @@ public class RavenwoodSystemProperties { // Create a default instance, and make an immutable copy of it. public static final RavenwoodSystemProperties DEFAULT_VALUES = new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true); -}
\ No newline at end of file +} diff --git a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java index 989bb6be1782..ef795c63880e 100644 --- a/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java +++ b/ravenwood/runtime-common-src/com/android/ravenwood/common/RavenwoodCommonUtils.java @@ -47,9 +47,6 @@ public class RavenwoodCommonUtils { public static final boolean RAVENWOOD_VERBOSE_LOGGING = "1".equals(System.getenv( "RAVENWOOD_VERBOSE")); - /** 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"; @@ -110,29 +107,21 @@ public class RavenwoodCommonUtils { } /** - * 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); + System.load(getJniLibraryPath(libname)); } else { System.loadLibrary(libname); } } /** - * Function equivalent to ART's System.loadLibrary. See RavenwoodUtils for why we need it. + * Find the shared library path from java.library.path. */ - private static void loadJniLibraryInternal(String libname) { + public static String getJniLibraryPath(String libname) { var path = System.getProperty("java.library.path"); var filename = "lib" + libname + ".so"; @@ -140,22 +129,21 @@ public class RavenwoodCommonUtils { try { if (path == null) { - throw new UnsatisfiedLinkError("Cannot load library " + libname + "." + throw new UnsatisfiedLinkError("Cannot find 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; + return file.getAbsolutePath(); } } - throw new UnsatisfiedLinkError("Library " + libname + " not found in " - + "java.library.path: " + path); } catch (Throwable e) { dumpFiles(System.out); throw e; } + throw new UnsatisfiedLinkError("Library " + libname + " not found in " + + "java.library.path: " + path); } private static void dumpFiles(PrintStream out) { diff --git a/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java b/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java deleted file mode 100644 index b09bf3119cfa..000000000000 --- a/ravenwood/runtime-helper-src/framework/android/os/SystemProperties_host.java +++ /dev/null @@ -1,209 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.os; - -import android.util.SparseArray; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.Preconditions; - -import java.util.Map; -import java.util.Objects; -import java.util.function.Predicate; - -public class SystemProperties_host { - private static final Object sLock = new Object(); - - /** Active system property values */ - @GuardedBy("sLock") - private static Map<String, String> sValues; - /** Predicate tested to determine if a given key can be read. */ - @GuardedBy("sLock") - private static Predicate<String> sKeyReadablePredicate; - /** Predicate tested to determine if a given key can be written. */ - @GuardedBy("sLock") - private static Predicate<String> sKeyWritablePredicate; - - /** - * Reverse mapping that provides a way back to an original key from the - * {@link System#identityHashCode(Object)} of {@link String#intern}. - */ - @GuardedBy("sLock") - private static SparseArray<String> sKeyHandles = new SparseArray<>(); - - /** - * Basically the same as {@link #init$ravenwood}, but it'll only run if no values are - * set yet. - */ - public static void initializeIfNeeded(Map<String, String> values, - Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) { - synchronized (sLock) { - if (sValues != null) { - return; // Already initialized. - } - init$ravenwood(values, keyReadablePredicate, keyWritablePredicate); - } - } - - public static void init$ravenwood(Map<String, String> values, - Predicate<String> keyReadablePredicate, Predicate<String> keyWritablePredicate) { - synchronized (sLock) { - sValues = Objects.requireNonNull(values); - sKeyReadablePredicate = Objects.requireNonNull(keyReadablePredicate); - sKeyWritablePredicate = Objects.requireNonNull(keyWritablePredicate); - sKeyHandles.clear(); - synchronized (SystemProperties.sChangeCallbacks) { - SystemProperties.sChangeCallbacks.clear(); - } - } - } - - public static void reset$ravenwood() { - synchronized (sLock) { - sValues = null; - sKeyReadablePredicate = null; - sKeyWritablePredicate = null; - sKeyHandles.clear(); - synchronized (SystemProperties.sChangeCallbacks) { - SystemProperties.sChangeCallbacks.clear(); - } - } - } - - public static void native_set(String key, String val) { - synchronized (sLock) { - Objects.requireNonNull(key); - Preconditions.requireNonNullViaRavenwoodRule(sValues); - if (!sKeyWritablePredicate.test(key)) { - throw new IllegalArgumentException( - "Write access to system property '" + key + "' denied via RavenwoodRule"); - } - if (key.startsWith("ro.") && sValues.containsKey(key)) { - throw new IllegalArgumentException( - "System property '" + key + "' already defined once; cannot redefine"); - } - if ((val == null) || val.isEmpty()) { - sValues.remove(key); - } else { - sValues.put(key, val); - } - SystemProperties.callChangeCallbacks(); - } - } - - public static String native_get(String key, String def) { - synchronized (sLock) { - Objects.requireNonNull(key); - Preconditions.requireNonNullViaRavenwoodRule(sValues); - if (!sKeyReadablePredicate.test(key)) { - throw new IllegalArgumentException( - "Read access to system property '" + key + "' denied via RavenwoodRule"); - } - return sValues.getOrDefault(key, def); - } - } - - public static int native_get_int(String key, int def) { - try { - return Integer.parseInt(native_get(key, "")); - } catch (NumberFormatException ignored) { - return def; - } - } - - public static long native_get_long(String key, long def) { - try { - return Long.parseLong(native_get(key, "")); - } catch (NumberFormatException ignored) { - return def; - } - } - - public static boolean native_get_boolean(String key, boolean def) { - return parseBoolean(native_get(key, ""), def); - } - - public static long native_find(String name) { - synchronized (sLock) { - Preconditions.requireNonNullViaRavenwoodRule(sValues); - if (sValues.containsKey(name)) { - name = name.intern(); - final int handle = System.identityHashCode(name); - sKeyHandles.put(handle, name); - return handle; - } else { - return 0; - } - } - } - - public static String native_get(long handle) { - synchronized (sLock) { - return native_get(sKeyHandles.get((int) handle), ""); - } - } - - public static int native_get_int(long handle, int def) { - synchronized (sLock) { - return native_get_int(sKeyHandles.get((int) handle), def); - } - } - - public static long native_get_long(long handle, long def) { - synchronized (sLock) { - return native_get_long(sKeyHandles.get((int) handle), def); - } - } - - public static boolean native_get_boolean(long handle, boolean def) { - synchronized (sLock) { - return native_get_boolean(sKeyHandles.get((int) handle), def); - } - } - - public static void native_add_change_callback() { - // Ignored; callback always registered via init above - } - - public static void native_report_sysprop_change() { - // Report through callback always registered via init above - synchronized (sLock) { - Preconditions.requireNonNullViaRavenwoodRule(sValues); - SystemProperties.callChangeCallbacks(); - } - } - - private static boolean parseBoolean(String val, boolean def) { - // Matches system/libbase/include/android-base/parsebool.h - if (val == null) return def; - switch (val) { - case "1": - case "on": - case "true": - case "y": - case "yes": - return true; - case "0": - case "false": - case "n": - case "no": - case "off": - return false; - default: - return def; - } - } -} diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java index c56ec8a60f00..3fedc1a450b9 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java +++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/OsConstants.java @@ -15,8 +15,6 @@ */ 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) @@ -1252,8 +1250,6 @@ public class OsConstants { 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/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java index ad80d92686ab..f13189f6f8be 100644 --- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java +++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java @@ -19,7 +19,6 @@ import android.system.ErrnoException; import android.system.StructStat; import com.android.ravenwood.common.JvmWorkaround; -import com.android.ravenwood.common.RavenwoodCommonUtils; import java.io.FileDescriptor; @@ -30,11 +29,6 @@ public class RavenwoodRuntimeNative { private RavenwoodRuntimeNative() { } - static { - RavenwoodCommonUtils.ensureOnRavenwood(); - RavenwoodCommonUtils.loadRavenwoodNativeRuntime(); - } - public static native void applyFreeFunction(long freeFunction, long nativePtr); private static native long nLseek(int fd, long offset, int whence) throws ErrnoException; @@ -56,6 +50,14 @@ public class RavenwoodRuntimeNative { public static native void setenv(String name, String value, boolean overwrite) throws ErrnoException; + public static native void reloadNativeLibrary(String libFile); + + public static native String getSystemProperty(String key); + + public static native boolean setSystemProperty(String key, String value); + + public static native void clearSystemProperties(); + public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException { return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence); } diff --git a/ravenwood/runtime-jni/jni_helper.h b/ravenwood/runtime-jni/jni_helper.h new file mode 100644 index 000000000000..561fb3beda6b --- /dev/null +++ b/ravenwood/runtime-jni/jni_helper.h @@ -0,0 +1,99 @@ +/* + * 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 <jni.h> +#include <log/log.h> +#include <nativehelper/JNIHelp.h> +#include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/ScopedUtfChars.h> + +#include <string> + +constexpr const char* kCommonUtils = "com/android/ravenwood/common/RavenwoodCommonUtils"; +constexpr const char* kRuntimeEnvController = + "android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController"; +constexpr const char* kRuntimeNative = "com/android/ravenwood/RavenwoodRuntimeNative"; + +// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars +// we only get modified UTF-8, which is not the platform string type used in host JVM. +struct ScopedRealUtf8Chars { + ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) { + if (s == nullptr) { + jniThrowNullPointerException(env); + return; + } + jclass clazz = env->GetObjectClass(s); + jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B"); + + ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8")); + ScopedLocalRef<jbyteArray> jbytes(env, + (jbyteArray)env->CallObjectMethod(s, getBytes, + utf8.get())); + + ScopedByteArrayRO bytes(env, jbytes.get()); + string_.append((const char*)bytes.get(), bytes.size()); + valid_ = true; + } + + const char* c_str() const { + return valid_ ? string_.c_str() : nullptr; + } + + size_t size() const { + return string_.size(); + } + + const char& operator[](size_t n) const { + return string_[n]; + } + +private: + std::string string_; + bool valid_; +}; + +static inline JNIEnv* GetJNIEnvOrDie(JavaVM* vm) { + JNIEnv* env = nullptr; + vm->GetEnv((void**)&env, JNI_VERSION_1_4); + LOG_ALWAYS_FATAL_IF(env == nullptr, "Could not retrieve JNIEnv."); + return env; +} + +static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { + jclass clazz = env->FindClass(class_name); + LOG_ALWAYS_FATAL_IF(clazz == NULL, "Unable to find class %s", class_name); + return clazz; +} + +template <typename T> +static inline T MakeGlobalRefOrDie(JNIEnv* env, T in) { + jobject res = env->NewGlobalRef(in); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to create global reference."); + return static_cast<T>(res); +} + +static inline jclass FindGlobalClassOrDie(JNIEnv* env, const char* class_name) { + return MakeGlobalRefOrDie(env, FindClassOrDie(env, class_name)); +} + +static inline jmethodID GetStaticMethodIDOrDie(JNIEnv* env, jclass clazz, const char* method_name, + const char* method_signature) { + jmethodID res = env->GetStaticMethodID(clazz, method_name, method_signature); + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static method %s with signature %s", + method_name, method_signature); + return res; +} diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp index c255be5f61aa..3ff08483c956 100644 --- a/ravenwood/runtime-jni/ravenwood_runtime.cpp +++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp @@ -15,21 +15,14 @@ */ #include <fcntl.h> -#include <sys/stat.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> -#include <string> -#include <nativehelper/JNIHelp.h> -#include <nativehelper/ScopedLocalRef.h> -#include <nativehelper/ScopedUtfChars.h> -#include <nativehelper/ScopedPrimitiveArray.h> +#include <utils/misc.h> -#include "jni.h" -#include "utils/Log.h" -#include "utils/misc.h" +#include <string> -// Defined in ravenwood_os_constants.cpp -void register_android_system_OsConstants(JNIEnv* env); +#include "jni_helper.h" // ---- Exception related ---- @@ -51,53 +44,6 @@ static rc_t throwIfMinusOne(JNIEnv* env, const char* name, rc_t rc) { static jclass g_StructStat; static jclass g_StructTimespecClass; -// We have to explicitly decode the string to real UTF-8, because when using GetStringUTFChars -// we only get modified UTF-8, which is not the platform string type used in host JVM. -struct ScopedRealUtf8Chars { - ScopedRealUtf8Chars(JNIEnv* env, jstring s) : valid_(false) { - if (s == nullptr) { - jniThrowNullPointerException(env); - return; - } - jclass clazz = env->GetObjectClass(s); - jmethodID getBytes = env->GetMethodID(clazz, "getBytes", "(Ljava/lang/String;)[B"); - - ScopedLocalRef<jstring> utf8(env, env->NewStringUTF("UTF-8")); - ScopedLocalRef<jbyteArray> jbytes(env, - (jbyteArray) env->CallObjectMethod(s, getBytes, utf8.get())); - - ScopedByteArrayRO bytes(env, jbytes.get()); - string_.append((const char *) bytes.get(), bytes.size()); - valid_ = true; - } - - const char* c_str() const { - return valid_ ? string_.c_str() : nullptr; - } - - size_t size() const { - return string_.size(); - } - - const char& operator[](size_t n) const { - return string_[n]; - } - -private: - std::string string_; - bool valid_; -}; - -static jclass findClass(JNIEnv* env, const char* name) { - ScopedLocalRef<jclass> localClass(env, env->FindClass(name)); - jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get())); - if (result == NULL) { - ALOGE("failed to find class '%s'", name); - abort(); - } - return result; -} - static jobject makeStructTimespec(JNIEnv* env, const struct timespec& ts) { static jmethodID ctor = env->GetMethodID(g_StructTimespecClass, "<init>", "(JJ)V"); @@ -229,6 +175,8 @@ static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaVal // ---- Registration ---- +extern void register_android_system_OsConstants(JNIEnv* env); + static const JNINativeMethod sMethods[] = { { "applyFreeFunction", "(JJ)V", (void*)nApplyFreeFunction }, @@ -243,24 +191,14 @@ static const JNINativeMethod sMethods[] = { "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv }, }; -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!"); - +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { ALOGI("%s: JNI_OnLoad", __FILE__); - g_StructStat = findClass(env, "android/system/StructStat"); - g_StructTimespecClass = findClass(env, "android/system/StructTimespec"); + JNIEnv* env = GetJNIEnvOrDie(vm); + g_StructStat = FindGlobalClassOrDie(env, "android/system/StructStat"); + g_StructTimespecClass = FindGlobalClassOrDie(env, "android/system/StructTimespec"); - jint res = jniRegisterNativeMethods(env, "com/android/ravenwood/RavenwoodRuntimeNative", - sMethods, NELEM(sMethods)); + jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods)); if (res < 0) { return res; } diff --git a/ravenwood/runtime-jni/ravenwood_sysprop.cpp b/ravenwood/runtime-jni/ravenwood_sysprop.cpp new file mode 100644 index 000000000000..4fb61b6c590d --- /dev/null +++ b/ravenwood/runtime-jni/ravenwood_sysprop.cpp @@ -0,0 +1,187 @@ +/* + * 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 <dlfcn.h> + +#include <set> + +#include "jni_helper.h" + +// Implement a rudimentary system properties data store + +#define PROP_VALUE_MAX 92 + +namespace { + +struct prop_info { + std::string key; + mutable std::string value; + mutable uint32_t serial; + + prop_info(const char* key, const char* value) : key(key), value(value), serial(0) {} +}; + +struct prop_info_cmp { + using is_transparent = void; + bool operator()(const prop_info& lhs, const prop_info& rhs) { + return lhs.key < rhs.key; + } + bool operator()(std::string_view lhs, const prop_info& rhs) { + return lhs < rhs.key; + } + bool operator()(const prop_info& lhs, std::string_view rhs) { + return lhs.key < rhs; + } +}; + +} // namespace + +static auto& g_properties_lock = *new std::mutex; +static auto& g_properties = *new std::set<prop_info, prop_info_cmp>; + +static bool property_set(const char* key, const char* value) { + if (key == nullptr || *key == '\0') return false; + if (value == nullptr) value = ""; + bool read_only = !strncmp(key, "ro.", 3); + if (!read_only && strlen(value) >= PROP_VALUE_MAX) return -1; + + std::lock_guard lock(g_properties_lock); + auto [it, success] = g_properties.emplace(key, value); + if (read_only) return success; + if (!success) { + it->value = value; + ++it->serial; + } + return true; +} + +template <typename Func> +static void property_get(const char* key, Func callback) { + std::lock_guard lock(g_properties_lock); + auto it = g_properties.find(key); + if (it != g_properties.end()) { + callback(*it); + } +} + +// Redefine the __system_property_XXX functions here so we can perform +// logging and access checks for all sysprops in native code. + +static void check_system_property_access(const char* key, bool write); + +extern "C" { + +int __system_property_set(const char* key, const char* value) { + check_system_property_access(key, true); + return property_set(key, value) ? 0 : -1; +} + +int __system_property_get(const char* key, char* value) { + check_system_property_access(key, false); + *value = '\0'; + property_get(key, [&](const prop_info& info) { + snprintf(value, PROP_VALUE_MAX, "%s", info.value.c_str()); + }); + return strlen(value); +} + +const prop_info* __system_property_find(const char* key) { + check_system_property_access(key, false); + const prop_info* pi = nullptr; + property_get(key, [&](const prop_info& info) { pi = &info; }); + return pi; +} + +void __system_property_read_callback(const prop_info* pi, + void (*callback)(void*, const char*, const char*, uint32_t), + void* cookie) { + std::lock_guard lock(g_properties_lock); + callback(cookie, pi->key.c_str(), pi->value.c_str(), pi->serial); +} + +} // extern "C" + +// ---- JNI ---- + +static JavaVM* gVM = nullptr; +static jclass gEnvController = nullptr; +static jmethodID gCheckSystemPropertyAccess; + +static void reloadNativeLibrary(JNIEnv* env, jclass, jstring javaPath) { + ScopedUtfChars path(env, javaPath); + // Force reload ourselves as global + dlopen(path.c_str(), RTLD_LAZY | RTLD_GLOBAL | RTLD_NOLOAD); +} + +// Call back into Java code to check property access +static void check_system_property_access(const char* key, bool write) { + if (gVM != nullptr && gEnvController != nullptr) { + JNIEnv* env; + if (gVM->GetEnv((void**)&env, JNI_VERSION_1_4) >= 0) { + ALOGI("%s access to system property '%s'", write ? "Write" : "Read", key); + env->CallStaticVoidMethod(gEnvController, gCheckSystemPropertyAccess, + env->NewStringUTF(key), write ? JNI_TRUE : JNI_FALSE); + return; + } + } + // Not on JVM thread, abort + LOG_ALWAYS_FATAL("Access to system property '%s' on non-JVM threads is not allowed.", key); +} + +static jstring getSystemProperty(JNIEnv* env, jclass, jstring javaKey) { + ScopedUtfChars key(env, javaKey); + jstring value = nullptr; + property_get(key.c_str(), + [&](const prop_info& info) { value = env->NewStringUTF(info.value.c_str()); }); + return value; +} + +static jboolean setSystemProperty(JNIEnv* env, jclass, jstring javaKey, jstring javaValue) { + ScopedUtfChars key(env, javaKey); + ScopedUtfChars value(env, javaValue); + return property_set(key.c_str(), value.c_str()) ? JNI_TRUE : JNI_FALSE; +} + +static void clearSystemProperties(JNIEnv*, jclass) { + std::lock_guard lock(g_properties_lock); + g_properties.clear(); +} + +static const JNINativeMethod sMethods[] = { + {"reloadNativeLibrary", "(Ljava/lang/String;)V", (void*)reloadNativeLibrary}, + {"getSystemProperty", "(Ljava/lang/String;)Ljava/lang/String;", (void*)getSystemProperty}, + {"setSystemProperty", "(Ljava/lang/String;Ljava/lang/String;)Z", (void*)setSystemProperty}, + {"clearSystemProperties", "()V", (void*)clearSystemProperties}, +}; + +extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { + ALOGI("%s: JNI_OnLoad", __FILE__); + + JNIEnv* env = GetJNIEnvOrDie(vm); + gVM = vm; + + // Fetch several references for future use + gEnvController = FindGlobalClassOrDie(env, kRuntimeEnvController); + gCheckSystemPropertyAccess = + GetStaticMethodIDOrDie(env, gEnvController, "checkSystemPropertyAccess", + "(Ljava/lang/String;Z)V"); + + // Expose raw property methods as JNI methods + jint res = jniRegisterNativeMethods(env, kRuntimeNative, sMethods, NELEM(sMethods)); + if (res < 0) return -1; + + return JNI_VERSION_1_4; +} |