Merge "Do not set DISABLE_PREOPT_BOOT_IMAGES when doing a VSDK build."
diff --git a/core/Makefile b/core/Makefile
index 4e5787d..c3c5fb3 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -5401,6 +5401,9 @@
 ifdef BOARD_PREBUILT_DTBOIMAGE
 	$(hide) echo "flash dtbo" >> $@
 endif
+ifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),)
+	$(hide) echo "flash vendor_kernel_boot" >> $@
+endif
 ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 	$(hide) echo "flash pvmfw" >> $@
 endif
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index c52fa92..6ba539c 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -27,6 +27,7 @@
 # Add variables to the namespace below:
 
 $(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_MEDIASERVER)
+$(call add_soong_config_var,ANDROID,TARGET_DYNAMIC_64_32_DRMSERVER)
 $(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
 $(call add_soong_config_var,ANDROID,IS_TARGET_MIXED_SEPOLICY)
 ifeq ($(IS_TARGET_MIXED_SEPOLICY),true)
diff --git a/core/config.mk b/core/config.mk
index e65a906..5457fd2 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1211,13 +1211,6 @@
 TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT := $(call numbers_less_than,24,$(TARGET_AVAILABLE_SDK_VERSIONS))
 TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT := $(call numbers_less_than,30,$(TARGET_AVAILABLE_SDK_VERSIONS))
 
-# Missing optional uses-libraries so that the platform doesn't create build rules that depend on
-# them.
-INTERNAL_PLATFORM_MISSING_USES_LIBRARIES := \
-  com.google.android.ble \
-  com.google.android.media.effects \
-  com.google.android.wearable \
-
 # This is the standard way to name a directory containing prebuilt target
 # objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so
 TARGET_PREBUILT_TAG := android-$(TARGET_ARCH)
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 0e84f516..aa638d4 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -446,6 +446,13 @@
 # If local module needs HWASAN, add compiler flags.
 ifneq ($(filter hwaddress,$(my_sanitize)),)
   my_cflags += $(HWADDRESS_SANITIZER_CONFIG_EXTRA_CFLAGS)
+
+  ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)
+    ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)
+      my_linker := /system/bin/linker_hwasan64
+    endif
+  endif
+
 endif
 
 # Use minimal diagnostics when integer overflow is enabled; never do it for HOST modules
diff --git a/core/dex_preopt_odex_install.mk b/core/dex_preopt_odex_install.mk
index 7165bea..14fafa1 100644
--- a/core/dex_preopt_odex_install.mk
+++ b/core/dex_preopt_odex_install.mk
@@ -111,18 +111,19 @@
 # Local module variables and functions used in dexpreopt and manifest_check.
 ################################################################################
 
-my_filtered_optional_uses_libraries := $(filter-out $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES), \
-  $(LOCAL_OPTIONAL_USES_LIBRARIES))
-
 # TODO(b/132357300): This may filter out too much, as PRODUCT_PACKAGES doesn't
 # include all packages (the full list is unknown until reading all Android.mk
 # makefiles). As a consequence, a library may be present but not included in
 # dexpreopt, which will result in class loader context mismatch and a failure
-# to load dexpreopt code on device. We should fix this, either by deferring
-# dependency computation until the full list of product packages is known, or
-# by adding product-specific lists of missing libraries.
+# to load dexpreopt code on device.
+# However, we have to do filtering here. Otherwise, we may include extra
+# libraries that Soong and Make don't generate build rules for (e.g., a library
+# that exists in the source tree but not installable), and therefore get Ninja
+# errors.
+# We have deferred CLC computation to the Ninja phase, but the dependency
+# computation still needs to be done early. For now, this is the best we can do.
 my_filtered_optional_uses_libraries := $(filter $(PRODUCT_PACKAGES), \
-  $(my_filtered_optional_uses_libraries))
+  $(LOCAL_OPTIONAL_USES_LIBRARIES))
 
 ifeq ($(LOCAL_MODULE_CLASS),APPS)
   # compatibility libraries are added to class loader context of an app only if
@@ -464,7 +465,8 @@
 	-global $(PRIVATE_GLOBAL_CONFIG) \
 	-module $(PRIVATE_MODULE_CONFIG) \
 	-dexpreopt_script $@ \
-	-out_dir $(OUT_DIR)
+	-out_dir $(OUT_DIR) \
+	-product_packages $(PRODUCT_OUT)/product_packages.txt
 
   my_dexpreopt_deps := $(my_dex_jar)
   my_dexpreopt_deps += $(if $(my_process_profile),$(LOCAL_DEX_PREOPT_PROFILE))
diff --git a/core/instrumentation_test_config_template.xml b/core/instrumentation_test_config_template.xml
index 6ca964e..379126c 100644
--- a/core/instrumentation_test_config_template.xml
+++ b/core/instrumentation_test_config_template.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -24,7 +24,7 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.{TEST_TYPE}" >
-        <option name="package" value="{PACKAGE}" />
+        {EXTRA_TEST_RUNNER_CONFIGS}<option name="package" value="{PACKAGE}" />
         <option name="runner" value="{RUNNER}" />
     </test>
 </configuration>
diff --git a/core/java_host_test_config_template.xml b/core/java_host_test_config_template.xml
index 26c1caf..e123dc7 100644
--- a/core/java_host_test_config_template.xml
+++ b/core/java_host_test_config_template.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
+<!-- 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.
@@ -21,6 +21,6 @@
     {EXTRA_CONFIGS}
 
     <test class="com.android.tradefed.testtype.HostTest" >
-        <option name="jar" value="{MODULE}.jar" />
+        {EXTRA_TEST_RUNNER_CONFIGS}<option name="jar" value="{MODULE}.jar" />
     </test>
 </configuration>
diff --git a/core/main.mk b/core/main.mk
index 903e261..2281b7a 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -337,6 +337,7 @@
 
 ifeq ($(AB_OTA_UPDATER),true)
 ADDITIONAL_PRODUCT_PROPERTIES += ro.product.ab_ota_partitions=$(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS)))
+ADDITIONAL_VENDOR_PROPERTIES += ro.vendor.build.ab_ota_partitions=$(subst $(space),$(comma),$(sort $(AB_OTA_PARTITIONS)))
 endif
 
 # Set this property for VTS to skip large page size tests on unsupported devices.
diff --git a/core/native_test_config_template.xml b/core/native_test_config_template.xml
index ea982cf..788157c 100644
--- a/core/native_test_config_template.xml
+++ b/core/native_test_config_template.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
+<!-- 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.
@@ -26,7 +26,7 @@
     </target_preparer>
 
     <test class="com.android.tradefed.testtype.GTest" >
-        <option name="native-test-device-path" value="{TEST_INSTALL_BASE}" />
+        {EXTRA_TEST_RUNNER_CONFIGS}<option name="native-test-device-path" value="{TEST_INSTALL_BASE}" />
         <option name="module-name" value="{MODULE}" />
     </test>
 </configuration>
diff --git a/core/product_config.mk b/core/product_config.mk
index 01ad030..3f9eb24 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -287,6 +287,15 @@
 $(foreach include_tag,$(PRODUCT_INCLUDE_TAGS), \
 	$(if $(filter $(include_tag),$(BLUEPRINT_INCLUDE_TAGS_ALLOWLIST)),,\
 	$(call pretty-error, $(include_tag) is not in BLUEPRINT_INCLUDE_TAGS_ALLOWLIST: $(BLUEPRINT_INCLUDE_TAGS_ALLOWLIST))))
+# Create default PRODUCT_INCLUDE_TAGS
+ifeq (, $(PRODUCT_INCLUDE_TAGS))
+# Soong analysis is global: even though a module might not be relevant to a specific product (e.g. build_tools for aosp_arm),
+# we still analyse it.
+# This means that in setups where we two have two prebuilts of module_sdk, we need a "default" to use in analysis
+# This should be a no-op in aosp and internal since no Android.bp file contains blueprint_package_includes
+PRODUCT_INCLUDE_TAGS += com.android.mainline # Use the big android one by default
+endif
+
 #############################################################################
 
 # Quick check and assign default values
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 1021650..46f06f7 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -1,5 +1,5 @@
 SOONG_MAKEVARS_MK := $(SOONG_OUT_DIR)/make_vars-$(TARGET_PRODUCT).mk
-SOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.variables
+SOONG_VARIABLES := $(SOONG_OUT_DIR)/soong.$(TARGET_PRODUCT).variables
 SOONG_ANDROID_MK := $(SOONG_OUT_DIR)/Android-$(TARGET_PRODUCT).mk
 
 include $(BUILD_SYSTEM)/art_config.mk
@@ -251,8 +251,6 @@
 
 $(call add_json_list, TargetFSConfigGen,                 $(TARGET_FS_CONFIG_GEN))
 
-$(call add_json_list, MissingUsesLibraries,              $(INTERNAL_PLATFORM_MISSING_USES_LIBRARIES))
-
 $(call add_json_map, VendorVars)
 $(foreach namespace,$(sort $(SOONG_CONFIG_NAMESPACES)),\
   $(call add_json_map, $(namespace))\
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index 40be80e..e2d5fb4 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -54,6 +54,8 @@
 
 # Include 64-bit mediaserver to support 64-bit only devices
 TARGET_DYNAMIC_64_32_MEDIASERVER := true
+# Include 64-bit drmserver to support 64-bit only devices
+TARGET_DYNAMIC_64_32_DRMSERVER := true
 
 include build/make/target/board/BoardConfigGsiCommon.mk
 
diff --git a/target/board/generic_x86_64/BoardConfig.mk b/target/board/generic_x86_64/BoardConfig.mk
index e7f2ae0..36136f4 100755
--- a/target/board/generic_x86_64/BoardConfig.mk
+++ b/target/board/generic_x86_64/BoardConfig.mk
@@ -24,6 +24,8 @@
 
 # Include 64-bit mediaserver to support 64-bit only devices
 TARGET_DYNAMIC_64_32_MEDIASERVER := true
+# Include 64-bit drmserver to support 64-bit only devices
+TARGET_DYNAMIC_64_32_DRMSERVER := true
 
 include build/make/target/board/BoardConfigGsiCommon.mk
 
diff --git a/target/board/gsi_arm64/BoardConfig.mk b/target/board/gsi_arm64/BoardConfig.mk
index db95082..7910b1d 100644
--- a/target/board/gsi_arm64/BoardConfig.mk
+++ b/target/board/gsi_arm64/BoardConfig.mk
@@ -29,6 +29,8 @@
 
 # Include 64-bit mediaserver to support 64-bit only devices
 TARGET_DYNAMIC_64_32_MEDIASERVER := true
+# Include 64-bit drmserver to support 64-bit only devices
+TARGET_DYNAMIC_64_32_DRMSERVER := true
 
 # TODO(b/111434759, b/111287060) SoC specific hacks
 BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index d65e5a4..a23fdd5 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -265,7 +265,6 @@
     sm \
     snapshotctl \
     snapuserd \
-    SoundPicker \
     storaged \
     surfaceflinger \
     svc \
@@ -291,6 +290,13 @@
     wifi.rc \
     wm \
 
+# These packages are not used on Android TV
+ifneq ($(PRODUCT_IS_ATV),true)
+  PRODUCT_PACKAGES += \
+      SoundPicker \
+
+endif
+
 # VINTF data for system image
 PRODUCT_PACKAGES += \
     system_manifest.xml \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index 752b199..1e28c80 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -115,4 +115,4 @@
     dalvik.vm.dex2oat-Xms=64m \
     dalvik.vm.dex2oat-Xmx=512m \
 
-PRODUCT_ENABLE_UFFD_GC := false  # TODO(jiakaiz): Change this to "default".
+PRODUCT_ENABLE_UFFD_GC := default
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 30810fa..44ad3dd 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -19,6 +19,7 @@
 use std::io::{Read, Write};
 
 use crate::aconfig::{FlagDeclaration, FlagState, FlagValue, Permission};
+use crate::codegen;
 use crate::commands::Source;
 
 const DEFAULT_FLAG_STATE: FlagState = FlagState::Disabled;
@@ -108,7 +109,7 @@
 
 impl CacheBuilder {
     pub fn new(namespace: String) -> Result<CacheBuilder> {
-        ensure!(!namespace.is_empty(), "empty namespace");
+        ensure!(codegen::is_valid_identifier(&namespace), "bad namespace");
         let cache = Cache { namespace, items: vec![] };
         Ok(CacheBuilder { cache })
     }
@@ -118,7 +119,7 @@
         source: Source,
         declaration: FlagDeclaration,
     ) -> Result<&mut CacheBuilder> {
-        ensure!(!declaration.name.is_empty(), "empty flag name");
+        ensure!(codegen::is_valid_identifier(&declaration.name), "bad flag name");
         ensure!(!declaration.description.is_empty(), "empty flag description");
         ensure!(
             self.cache.items.iter().all(|item| item.name != declaration.name),
@@ -146,8 +147,8 @@
         source: Source,
         value: FlagValue,
     ) -> Result<&mut CacheBuilder> {
-        ensure!(!value.namespace.is_empty(), "empty flag namespace");
-        ensure!(!value.name.is_empty(), "empty flag name");
+        ensure!(codegen::is_valid_identifier(&value.namespace), "bad flag namespace");
+        ensure!(codegen::is_valid_identifier(&value.name), "bad flag name");
         ensure!(
             value.namespace == self.cache.namespace,
             "failed to set values for flag {}/{} from {}: expected namespace {}",
@@ -270,14 +271,14 @@
             .add_flag_value(
                 Source::Memory,
                 FlagValue {
-                    namespace: "some-other-namespace".to_string(),
+                    namespace: "some_other_namespace".to_string(),
                     name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
                 },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "failed to set values for flag some-other-namespace/foo from <memory>: expected namespace ns");
+        assert_eq!(&format!("{:?}", error), "failed to set values for flag some_other_namespace/foo from <memory>: expected namespace ns");
 
         let cache = builder.build();
         let item = cache.iter().find(|&item| item.name == "foo").unwrap();
@@ -300,7 +301,7 @@
                 FlagDeclaration { name: "".to_string(), description: "Description".to_string() },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "empty flag name");
+        assert_eq!(&format!("{:?}", error), "bad flag name");
 
         let error = builder
             .add_flag_declaration(
@@ -332,7 +333,7 @@
                 },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "empty flag namespace");
+        assert_eq!(&format!("{:?}", error), "bad flag namespace");
 
         let error = builder
             .add_flag_value(
@@ -345,7 +346,7 @@
                 },
             )
             .unwrap_err();
-        assert_eq!(&format!("{:?}", error), "empty flag name");
+        assert_eq!(&format!("{:?}", error), "bad flag name");
     }
 
     #[test]
diff --git a/tools/aconfig/src/codegen.rs b/tools/aconfig/src/codegen.rs
new file mode 100644
index 0000000..b60ec51
--- /dev/null
+++ b/tools/aconfig/src/codegen.rs
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+pub fn is_valid_identifier(s: &str) -> bool {
+    // Identifiers must match [a-z][a-z0-9_]*
+    let mut chars = s.chars();
+    let Some(first) = chars.next() else {
+        return false;
+    };
+    if !first.is_ascii_lowercase() {
+        return false;
+    }
+    chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_is_valid_identifier() {
+        assert!(is_valid_identifier("foo"));
+        assert!(is_valid_identifier("foo_bar_123"));
+
+        assert!(!is_valid_identifier(""));
+        assert!(!is_valid_identifier("123_foo"));
+        assert!(!is_valid_identifier("foo-bar"));
+        assert!(!is_valid_identifier("foo-b\u{00e5}r"));
+    }
+}
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 733b1c5..98288e7 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -31,7 +31,7 @@
     let mut template = TinyTemplate::new();
     template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
     let contents = template.render("java_code_gen", &context)?;
-    let mut path: PathBuf = namespace.split('.').collect();
+    let mut path: PathBuf = ["aconfig", namespace].iter().collect();
     // TODO: Allow customization of the java class name
     path.push("Flags.java");
     Ok(OutputFile { contents: contents.into(), path })
@@ -76,7 +76,7 @@
 
     #[test]
     fn test_generate_java_code() {
-        let namespace = "com.example";
+        let namespace = "example";
         let mut builder = CacheBuilder::new(namespace.to_string()).unwrap();
         builder
             .add_flag_declaration(
@@ -106,7 +106,7 @@
             )
             .unwrap();
         let cache = builder.build();
-        let expect_content = r#"package com.example;
+        let expect_content = r#"package aconfig.example;
 
         import android.provider.DeviceConfig;
 
@@ -118,7 +118,7 @@
 
             public static boolean test2() {
                 return DeviceConfig.getBoolean(
-                    "com.example",
+                    "example",
                     "test2__test2",
                     false
                 );
@@ -127,7 +127,7 @@
         }
         "#;
         let file = generate_java_code(&cache).unwrap();
-        assert_eq!("com/example/Flags.java", file.path.to_str().unwrap());
+        assert_eq!("aconfig/example/Flags.java", file.path.to_str().unwrap());
         assert_eq!(
             expect_content.replace(' ', ""),
             String::from_utf8(file.contents).unwrap().replace(' ', "")
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
new file mode 100644
index 0000000..fe4231b
--- /dev/null
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+use anyhow::Result;
+use serde::Serialize;
+use tinytemplate::TinyTemplate;
+
+use crate::aconfig::{FlagState, Permission};
+use crate::cache::{Cache, Item};
+use crate::commands::OutputFile;
+
+pub fn generate_rust_code(cache: &Cache) -> Result<OutputFile> {
+    let namespace = cache.namespace();
+    let parsed_flags: Vec<TemplateParsedFlag> =
+        cache.iter().map(|item| create_template_parsed_flag(namespace, item)).collect();
+    let context = TemplateContext { namespace: namespace.to_string(), parsed_flags };
+    let mut template = TinyTemplate::new();
+    template.add_template("rust_code_gen", include_str!("../templates/rust.template"))?;
+    let contents = template.render("rust_code_gen", &context)?;
+    let path = ["src", "lib.rs"].iter().collect();
+    Ok(OutputFile { contents: contents.into(), path })
+}
+
+#[derive(Serialize)]
+struct TemplateContext {
+    pub namespace: String,
+    pub parsed_flags: Vec<TemplateParsedFlag>,
+}
+
+#[derive(Serialize)]
+struct TemplateParsedFlag {
+    pub name: String,
+    pub fn_name: String,
+
+    // TinyTemplate's conditionals are limited to single <bool> expressions; list all options here
+    // Invariant: exactly one of these fields will be true
+    pub is_read_only_enabled: bool,
+    pub is_read_only_disabled: bool,
+    pub is_read_write: bool,
+}
+
+#[allow(clippy::nonminimal_bool)]
+fn create_template_parsed_flag(namespace: &str, item: &Item) -> TemplateParsedFlag {
+    let template = TemplateParsedFlag {
+        name: item.name.clone(),
+        fn_name: format!("{}_{}", namespace, &item.name),
+        is_read_only_enabled: item.permission == Permission::ReadOnly
+            && item.state == FlagState::Enabled,
+        is_read_only_disabled: item.permission == Permission::ReadOnly
+            && item.state == FlagState::Disabled,
+        is_read_write: item.permission == Permission::ReadWrite,
+    };
+    #[rustfmt::skip]
+    debug_assert!(
+        (template.is_read_only_enabled && !template.is_read_only_disabled && !template.is_read_write) ||
+        (!template.is_read_only_enabled && template.is_read_only_disabled && !template.is_read_write) ||
+        (!template.is_read_only_enabled && !template.is_read_only_disabled && template.is_read_write),
+        "TemplateParsedFlag invariant failed: {} {} {}",
+        template.is_read_only_enabled,
+        template.is_read_only_disabled,
+        template.is_read_write,
+    );
+    template
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::commands::{create_cache, Input, Source};
+
+    #[test]
+    fn test_generate_rust_code() {
+        let cache = create_cache(
+            "test",
+            vec![Input {
+                source: Source::File("testdata/test.aconfig".to_string()),
+                reader: Box::new(include_bytes!("../testdata/test.aconfig").as_slice()),
+            }],
+            vec![
+                Input {
+                    source: Source::File("testdata/first.values".to_string()),
+                    reader: Box::new(include_bytes!("../testdata/first.values").as_slice()),
+                },
+                Input {
+                    source: Source::File("testdata/test.aconfig".to_string()),
+                    reader: Box::new(include_bytes!("../testdata/second.values").as_slice()),
+                },
+            ],
+        )
+        .unwrap();
+        let generated = generate_rust_code(&cache).unwrap();
+        assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
+        let expected = r#"
+#[inline(always)]
+pub const fn r#test_disabled_ro() -> bool {
+    false
+}
+
+#[inline(always)]
+pub fn r#test_disabled_rw() -> bool {
+    flags_rust::GetServerConfigurableFlag("test", "disabled_rw", "false") == "true"
+}
+
+#[inline(always)]
+pub const fn r#test_enabled_ro() -> bool {
+    true
+}
+
+#[inline(always)]
+pub fn r#test_enabled_rw() -> bool {
+    flags_rust::GetServerConfigurableFlag("test", "enabled_rw", "false") == "true"
+}
+"#;
+        assert_eq!(expected.trim(), String::from_utf8(generated.contents).unwrap().trim());
+    }
+}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 22de331..cce1d7f 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -26,6 +26,7 @@
 use crate::cache::{Cache, CacheBuilder};
 use crate::codegen_cpp::generate_cpp_code;
 use crate::codegen_java::generate_java_code;
+use crate::codegen_rust::generate_rust_code;
 use crate::protos::ProtoParsedFlags;
 
 #[derive(Serialize, Deserialize, Clone, Debug)]
@@ -100,6 +101,10 @@
     generate_cpp_code(cache)
 }
 
+pub fn create_rust_lib(cache: &Cache) -> Result<OutputFile> {
+    generate_rust_code(cache)
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
 pub enum DumpFormat {
     Text,
@@ -125,7 +130,7 @@
             DumpFormat::Debug => {
                 let mut lines = vec![];
                 for item in cache.iter() {
-                    lines.push(format!("{:?}\n", item));
+                    lines.push(format!("{:#?}\n", item));
                 }
                 output.append(&mut lines.concat().into());
             }
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index d02307d..1d2ec95 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -26,8 +26,10 @@
 
 mod aconfig;
 mod cache;
+mod codegen;
 mod codegen_cpp;
 mod codegen_java;
+mod codegen_rust;
 mod commands;
 mod protos;
 
@@ -55,6 +57,11 @@
                 .arg(Arg::new("out").long("out").required(true)),
         )
         .subcommand(
+            Command::new("create-rust-lib")
+                .arg(Arg::new("cache").long("cache").required(true))
+                .arg(Arg::new("out").long("out").required(true)),
+        )
+        .subcommand(
             Command::new("dump")
                 .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
                 .arg(
@@ -129,6 +136,14 @@
             let generated_file = commands::create_cpp_lib(&cache)?;
             write_output_file_realtive_to_dir(&dir, &generated_file)?;
         }
+        Some(("create-rust-lib", sub_matches)) => {
+            let path = get_required_arg::<String>(sub_matches, "cache")?;
+            let file = fs::File::open(path)?;
+            let cache = Cache::read_from_reader(file)?;
+            let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
+            let generated_file = commands::create_rust_lib(&cache)?;
+            write_output_file_realtive_to_dir(&dir, &generated_file)?;
+        }
         Some(("dump", sub_matches)) => {
             let mut caches = Vec::new();
             for path in sub_matches.get_many::<String>("cache").unwrap_or_default() {
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
index 89da18b..30c7ad7 100644
--- a/tools/aconfig/templates/java.template
+++ b/tools/aconfig/templates/java.template
@@ -1,4 +1,4 @@
-package {namespace};
+package aconfig.{namespace};
 {{ if readwrite }}
 import android.provider.DeviceConfig;
 {{ endif }}
@@ -10,7 +10,7 @@
             "{namespace}",
             "{item.feature_name}__{item.flag_name}",
             {item.default_value}
-        ); 
+        );
         {{ -else- }}
         return {item.default_value};
         {{ -endif }}
diff --git a/tools/aconfig/templates/rust.template b/tools/aconfig/templates/rust.template
new file mode 100644
index 0000000..d7f4e8d
--- /dev/null
+++ b/tools/aconfig/templates/rust.template
@@ -0,0 +1,23 @@
+{{- for parsed_flag in parsed_flags -}}
+{{- if parsed_flag.is_read_only_disabled -}}
+#[inline(always)]
+pub const fn r#{parsed_flag.fn_name}() -> bool \{
+    false
+}
+
+{{ endif -}}
+{{- if parsed_flag.is_read_only_enabled -}}
+#[inline(always)]
+pub const fn r#{parsed_flag.fn_name}() -> bool \{
+    true
+}
+
+{{ endif -}}
+{{- if parsed_flag.is_read_write -}}
+#[inline(always)]
+pub fn r#{parsed_flag.fn_name}() -> bool \{
+    flags_rust::GetServerConfigurableFlag("{namespace}", "{parsed_flag.name}", "false") == "true"
+}
+
+{{ endif -}}
+{{- endfor -}}
diff --git a/tools/aconfig/testdata/first.values b/tools/aconfig/testdata/first.values
new file mode 100644
index 0000000..3c49111
--- /dev/null
+++ b/tools/aconfig/testdata/first.values
@@ -0,0 +1,18 @@
+flag_value {
+    namespace: "test"
+    name: "disabled_ro"
+    state: DISABLED
+    permission: READ_ONLY
+}
+flag_value {
+    namespace: "test"
+    name: "enabled_ro"
+    state: DISABLED
+    permission: READ_WRITE
+}
+flag_value {
+    namespace: "test"
+    name: "enabled_rw"
+    state: ENABLED
+    permission: READ_WRITE
+}
diff --git a/tools/aconfig/testdata/second.values b/tools/aconfig/testdata/second.values
new file mode 100644
index 0000000..3fe11ab
--- /dev/null
+++ b/tools/aconfig/testdata/second.values
@@ -0,0 +1,6 @@
+flag_value {
+    namespace: "test"
+    name: "enabled_ro"
+    state: ENABLED
+    permission: READ_ONLY
+}
diff --git a/tools/aconfig/testdata/test.aconfig b/tools/aconfig/testdata/test.aconfig
new file mode 100644
index 0000000..986a526
--- /dev/null
+++ b/tools/aconfig/testdata/test.aconfig
@@ -0,0 +1,33 @@
+namespace: "test"
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
+# - first.values: DISABLED + READ_ONLY
+flag {
+    name: "disabled_ro"
+    description: "This flag is DISABLED + READ_ONLY"
+}
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
+flag {
+    name: "disabled_rw"
+    description: "This flag is DISABLED + READ_WRITE"
+}
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
+# - first.values: DISABLED + READ_WRITE
+# - second.values: ENABLED + READ_ONLY
+flag {
+    name: "enabled_ro"
+    description: "This flag is ENABLED + READ_ONLY"
+}
+
+# This flag's final value is calculated from:
+# - test.aconfig: DISABLED + READ_WRITE (default)
+# - first.values: ENABLED + READ_WRITE
+flag {
+    name: "enabled_rw"
+    description: "This flag is ENABLED + READ_WRITE"
+}
diff --git a/tools/releasetools/merge/merge_target_files.py b/tools/releasetools/merge/merge_target_files.py
index ba2b14f..d8f7b15 100755
--- a/tools/releasetools/merge/merge_target_files.py
+++ b/tools/releasetools/merge/merge_target_files.py
@@ -165,17 +165,24 @@
     pass
 
 
-def include_meta_in_list(item_list):
-  """Include all `META/*` files in the item list.
+def include_extra_in_list(item_list):
+  """
+  1. Include all `META/*` files in the item list.
 
   To ensure that `AddImagesToTargetFiles` can still be used with vendor item
   list that do not specify all of the required META/ files, those files should
   be included by default. This preserves the backward compatibility of
   `rebuild_image_with_sepolicy`.
+
+  2. Include `SYSTEM/build.prop` file in the item list.
+
+  To ensure that `AddImagesToTargetFiles` for GRF vendor images, can still
+  access SYSTEM/build.prop to pass GetPartitionFingerprint check in BuildInfo
+  constructor.
   """
   if not item_list:
     return None
-  return list(item_list) + ['META/*']
+  return list(item_list) + ['META/*'] + ['SYSTEM/build.prop']
 
 
 def create_merged_package(temp_dir):
@@ -289,7 +296,7 @@
   merge_utils.CollectTargetFiles(
       input_zipfile_or_dir=OPTIONS.vendor_target_files,
       output_dir=vendor_target_files_dir,
-      item_list=include_meta_in_list(OPTIONS.vendor_item_list))
+      item_list=include_extra_in_list(OPTIONS.vendor_item_list))
 
   # Copy the partition contents from the merged target-files archive to the
   # vendor target-files archive.
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index fa9516e..63a863e 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -817,8 +817,8 @@
     target_dir = ExtractTargetFiles(target_file)
     cmd = ["delta_generator",
            "--out_file", payload_file]
-    with open(os.path.join(target_dir, "META", "ab_partitions.txt")) as fp:
-      ab_partitions = fp.read().strip().split("\n")
+    with open(os.path.join(target_dir, "META", "ab_partitions.txt"), "r") as fp:
+      ab_partitions = fp.read().strip().splitlines()
     cmd.extend(["--partition_names", ":".join(ab_partitions)])
     cmd.extend(
         ["--new_partitions", GetPartitionImages(target_dir, ab_partitions, False)])