[Ravenwood] Load default properties from build.prop

Flag: EXEMPT host test change only
Bug: 292141694
Test: $ANDROID_BUILD_TOP/frameworks/base/ravenwood/scripts/run-ravenwood-tests.sh
Change-Id: Ifdd991c7c85d1a81309721dc44017d3b9cad1894
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index bfa801f..8e884bc 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -372,6 +372,7 @@
 android_ravenwood_libgroup {
     name: "ravenwood-runtime",
     data: [
+        ":system-build.prop",
         ":framework-res",
         ":ravenwood-empty-res",
         ":framework-platform-compat-config",
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 5894476..24950e6 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuntimeEnvironmentController.java
@@ -20,12 +20,14 @@
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_RESOURCE_APK;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERBOSE_LOGGING;
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_VERSION_JAVA_SYSPROP;
+import static com.android.ravenwood.common.RavenwoodCommonUtils.getRavenwoodRuntimePath;
 
 import static org.junit.Assert.assertThrows;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.ResourcesManager;
@@ -81,6 +83,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";
+    private static final String RAVENWOOD_BUILD_PROP =
+            getRavenwoodRuntimePath() + "ravenwood-data/build.prop";
 
     /**
      * When enabled, attempt to dump all thread stacks just before we hit the
@@ -158,7 +162,8 @@
         System.load(RavenwoodCommonUtils.getJniLibraryPath(RAVENWOOD_NATIVE_RUNTIME_NAME));
 
         // Do the basic set up for the android sysprops.
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        RavenwoodSystemProperties.initialize(RAVENWOOD_BUILD_PROP);
+        setSystemProperties(null);
 
         // Make sure libandroid_runtime is loaded.
         RavenwoodNativeLoader.loadFrameworkNativeCode();
@@ -329,7 +334,7 @@
         LocalServices.removeAllServicesForTest();
         ServiceManager.reset$ravenwood();
 
-        setSystemProperties(RavenwoodSystemProperties.DEFAULT_VALUES);
+        setSystemProperties(null);
         if (sOriginalIdentityToken != -1) {
             Binder.restoreCallingIdentity(sOriginalIdentityToken);
         }
@@ -388,9 +393,10 @@
     /**
      * Set the current configuration to the actual SystemProperties.
      */
-    private static void setSystemProperties(RavenwoodSystemProperties systemProperties) {
+    private static void setSystemProperties(@Nullable RavenwoodSystemProperties systemProperties) {
         SystemProperties.clearChangeCallbacksForTest();
         RavenwoodRuntimeNative.clearSystemProperties();
+        if (systemProperties == null) systemProperties = new RavenwoodSystemProperties();
         sProps = new RavenwoodSystemProperties(systemProperties, true);
         for (var entry : systemProperties.getValues().entrySet()) {
             RavenwoodRuntimeNative.setSystemProperty(entry.getKey(), entry.getValue());
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
index f1e1ef6..ced1519 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodSystemProperties.java
@@ -18,12 +18,94 @@
 
 import static com.android.ravenwood.common.RavenwoodCommonUtils.RAVENWOOD_SYSPROP;
 
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Set;
 
 public class RavenwoodSystemProperties {
+    private static final String TAG = "RavenwoodSystemProperties";
+
+    private static final Map<String, String> sDefaultValues = new HashMap<>();
+
+    private static final String[] PARTITIONS = {
+            "bootimage",
+            "odm",
+            "product",
+            "system",
+            "system_ext",
+            "vendor",
+            "vendor_dlkm",
+    };
+
+    /**
+     * More info about property file loading: system/core/init/property_service.cpp
+     * In the following logic, the only partition we would need to consider is "system",
+     * since we only read from system-build.prop
+     */
+    static void initialize(String propFile) {
+        // Load all properties from build.prop
+        try {
+            Files.readAllLines(Path.of(propFile)).stream()
+                    .map(String::trim)
+                    .filter(s -> !s.startsWith("#"))
+                    .map(s -> s.split("\\s*=\\s*", 2))
+                    .filter(a -> a.length == 2)
+                    .forEach(a -> sDefaultValues.put(a[0], a[1]));
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        // If ro.product.${name} is not set, derive from ro.product.${partition}.${name}
+        // If ro.product.cpu.abilist* is not set, derive from ro.${partition}.product.cpu.abilist*
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            final String key;
+            if (entry.getKey().startsWith("ro.product.system.")) {
+                var name = entry.getKey().substring(18);
+                key = "ro.product." + name;
+
+            } else if (entry.getKey().startsWith("ro.system.product.cpu.abilist")) {
+                var name = entry.getKey().substring(22);
+                key = "ro.product.cpu." + name;
+            } else {
+                continue;
+            }
+            if (!sDefaultValues.containsKey(key)) {
+                sDefaultValues.put(key, entry.getValue());
+            }
+        }
+
+        // Some other custom values
+        sDefaultValues.put("ro.board.first_api_level", "1");
+        sDefaultValues.put("ro.product.first_api_level", "1");
+        sDefaultValues.put("ro.soc.manufacturer", "Android");
+        sDefaultValues.put("ro.soc.model", "Ravenwood");
+        sDefaultValues.put(RAVENWOOD_SYSPROP, "1");
+
+        // Log all values
+        sDefaultValues.forEach((key, value) -> RavenwoodCommonUtils.log(TAG, key + "=" + value));
+
+        // Copy ro.product.* and ro.build.* to all partitions, just in case
+        // We don't want to log these because these are just a lot of duplicate values
+        for (var entry : Set.copyOf(sDefaultValues.entrySet())) {
+            var key = entry.getKey();
+            if (key.startsWith("ro.product.") || key.startsWith("ro.build.")) {
+                var name = key.substring(3);
+                for (String partition : PARTITIONS) {
+                    var newKey = "ro." + partition + "." + name;
+                    if (!sDefaultValues.containsKey(newKey)) {
+                        sDefaultValues.put(newKey, entry.getValue());
+                    }
+                }
+            }
+        }
+    }
+
     private volatile boolean mIsImmutable;
 
     private final Map<String, String> mValues = new HashMap<>();
@@ -35,47 +117,15 @@
     private final Set<String> mKeyWritable = new HashSet<>();
 
     public RavenwoodSystemProperties() {
-        // TODO: load these values from build.prop generated files
-        setValueForPartitions("product.brand", "Android");
-        setValueForPartitions("product.device", "Ravenwood");
-        setValueForPartitions("product.manufacturer", "Android");
-        setValueForPartitions("product.model", "Ravenwood");
-        setValueForPartitions("product.name", "Ravenwood");
-
-        setValueForPartitions("product.cpu.abilist", "x86_64");
-        setValueForPartitions("product.cpu.abilist32", "");
-        setValueForPartitions("product.cpu.abilist64", "x86_64");
-
-        setValueForPartitions("build.date", "Thu Jan 01 00:00:00 GMT 2024");
-        setValueForPartitions("build.date.utc", "1704092400");
-        setValueForPartitions("build.id", "MAIN");
-        setValueForPartitions("build.tags", "dev-keys");
-        setValueForPartitions("build.type", "userdebug");
-        setValueForPartitions("build.version.all_codenames", "REL");
-        setValueForPartitions("build.version.codename", "REL");
-        setValueForPartitions("build.version.incremental", "userdebug.ravenwood.20240101");
-        setValueForPartitions("build.version.known_codenames", "REL");
-        setValueForPartitions("build.version.release", "14");
-        setValueForPartitions("build.version.release_or_codename", "VanillaIceCream");
-        setValueForPartitions("build.version.sdk", "34");
-
-        setValue("ro.board.first_api_level", "1");
-        setValue("ro.product.first_api_level", "1");
-
-        setValue("ro.soc.manufacturer", "Android");
-        setValue("ro.soc.model", "Ravenwood");
-
-        setValue("ro.debuggable", "1");
-
-        setValue(RAVENWOOD_SYSPROP, "1");
+        mValues.putAll(sDefaultValues);
     }
 
     /** Copy constructor */
     public RavenwoodSystemProperties(RavenwoodSystemProperties source, boolean immutable) {
-        this.mKeyReadable.addAll(source.mKeyReadable);
-        this.mKeyWritable.addAll(source.mKeyWritable);
-        this.mValues.putAll(source.mValues);
-        this.mIsImmutable = immutable;
+        mKeyReadable.addAll(source.mKeyReadable);
+        mKeyWritable.addAll(source.mKeyWritable);
+        mValues.putAll(source.mValues);
+        mIsImmutable = immutable;
     }
 
     public Map<String, String> getValues() {
@@ -123,36 +173,12 @@
         return mKeyWritable.contains(key);
     }
 
-    private static final String[] PARTITIONS = {
-            "bootimage",
-            "odm",
-            "product",
-            "system",
-            "system_ext",
-            "vendor",
-            "vendor_dlkm",
-    };
-
     private void ensureNotImmutable() {
         if (mIsImmutable) {
             throw new RuntimeException("Unable to update immutable instance");
         }
     }
 
-    /**
-     * Set the given property for all possible partitions where it could be defined. For
-     * example, the value of {@code ro.build.type} is typically also mirrored under
-     * {@code ro.system.build.type}, etc.
-     */
-    private void setValueForPartitions(String key, String value) {
-        ensureNotImmutable();
-
-        setValue("ro." + key, value);
-        for (String partition : PARTITIONS) {
-            setValue("ro." + partition + "." + key, value);
-        }
-    }
-
     public void setValue(String key, Object value) {
         ensureNotImmutable();
 
@@ -195,11 +221,4 @@
             return key;
         }
     }
-
-    /**
-     * Return an immutable, default instance.
-     */
-    // Create a default instance, and make an immutable copy of it.
-    public static final RavenwoodSystemProperties DEFAULT_VALUES =
-            new RavenwoodSystemProperties(new RavenwoodSystemProperties(), true);
 }