Merge "[Ravenwood] Use native system property implementation" into main
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index e53873b..89b727c 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -21,13 +21,10 @@
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.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 @@
*/
@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 @@
@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 @@
}
}
- /** @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 @@
@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 @@
}
@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 @@
}
/**
+ * 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 88e6fa3..e99d890 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 @@
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 @@
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 @@
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 @@
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 @@
// 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 d98120f..75aca1b 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.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,16 +34,18 @@
import java.util.concurrent.TimeUnit;
public class SystemPropertiesTest {
- @Rule
- public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder()
- .setSystemPropertyMutable(KEY, null)
- .setSystemPropertyMutable(UNSET_KEY, null)
- .setSystemPropertyMutable(PERSIST_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";
+ 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();
@Test
@SmallTest
@@ -117,7 +118,7 @@
@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 10e4f38..6b1197a 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -267,25 +267,39 @@
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",
],
-
- srcs: [
- "runtime-jni/*.cpp",
+ 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/ravenwood_runtime.cpp",
+ "runtime-jni/ravenwood_os_constants.cpp",
+ ],
shared_libs: [
"libbase",
- "liblog",
- "libnativehelper",
"libutils",
"libcutils",
],
@@ -377,8 +391,10 @@
"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 e548611..d29b93c 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 @@
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 90bb93d..6e73b2c 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.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 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 @@
}
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 @@
}
private static RavenwoodConfig sConfig;
+ private static RavenwoodSystemProperties sProps;
private static boolean sInitialized = false;
/**
@@ -132,6 +137,14 @@
// 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);
@@ -355,12 +368,21 @@
/**
* 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 ef8f584..f1e1ef6 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.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 @@
/** 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 @@
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 @@
// 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 989bb6b..ef795c6 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 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 @@
}
/**
- * 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 @@
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 b09bf31..0000000
--- 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 c56ec8a..3fedc1a 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 @@
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 ad80d92..f13189f 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.StructStat;
import com.android.ravenwood.common.JvmWorkaround;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.FileDescriptor;
@@ -30,11 +29,6 @@
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 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 0000000..561fb3b
--- /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 c255be5..3ff0848 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 <utils/misc.h>
+
#include <string>
-#include <nativehelper/JNIHelp.h>
-#include <nativehelper/ScopedLocalRef.h>
-#include <nativehelper/ScopedUtfChars.h>
-#include <nativehelper/ScopedPrimitiveArray.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);
+#include "jni_helper.h"
// ---- Exception related ----
@@ -51,53 +44,6 @@
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 @@
// ---- Registration ----
+extern void register_android_system_OsConstants(JNIEnv* env);
+
static const JNINativeMethod sMethods[] =
{
{ "applyFreeFunction", "(JJ)V", (void*)nApplyFreeFunction },
@@ -243,24 +191,14 @@
{ "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 0000000..4fb61b6
--- /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;
+}