Merge "Support aconfig dump --dedup" into main
diff --git a/core/Makefile b/core/Makefile
index b93c321..7d7457e 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -2122,6 +2122,7 @@
 $(if $(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT),$(hide) echo "$(1)_extfs_inode_count=$(BOARD_$(_var)IMAGE_EXTFS_INODE_COUNT)" >> $(2))
 $(if $(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT),$(hide) echo "$(1)_extfs_rsv_pct=$(BOARD_$(_var)IMAGE_EXTFS_RSV_PCT)" >> $(2))
 $(if $(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS),$(hide) echo "$(1)_f2fs_sldc_flags=$(BOARD_$(_var)IMAGE_F2FS_SLOAD_COMPRESS_FLAGS)" >> $(2))
+$(if $(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE),$(hide) echo "$(1)_f2fs_blocksize=$(BOARD_$(_var)IMAGE_F2FS_BLOCKSIZE)" >> $(2))
 $(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS),$(hide) echo "$(1)_f2fs_compress=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_COMPRESS)" >> $(2))
 $(if $(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE),$(hide) echo "$(1)_fs_type=$(BOARD_$(_var)IMAGE_FILE_SYSTEM_TYPE)" >> $(2))
 $(if $(BOARD_$(_var)IMAGE_JOURNAL_SIZE),$(hide) echo "$(1)_journal_size=$(BOARD_$(_var)IMAGE_JOURNAL_SIZE)" >> $(2))
@@ -2212,6 +2213,7 @@
 $(if $(BOARD_EROFS_SHARE_DUP_BLOCKS),$(hide) echo "erofs_share_dup_blocks=$(BOARD_EROFS_SHARE_DUP_BLOCKS)" >> $(1))
 $(if $(BOARD_EROFS_USE_LEGACY_COMPRESSION),$(hide) echo "erofs_use_legacy_compression=$(BOARD_EROFS_USE_LEGACY_COMPRESSION)" >> $(1))
 $(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1))
+$(if $(BOARD_F2FS_BLOCKSIZE),$(hide) echo "f2fs_blocksize=$(BOARD_F2FS_BLOCKSIZE)" >> $(1))
 $(if $(BOARD_FLASH_LOGICAL_BLOCK_SIZE), $(hide) echo "flash_logical_block_size=$(BOARD_FLASH_LOGICAL_BLOCK_SIZE)" >> $(1))
 $(if $(BOARD_FLASH_ERASE_BLOCK_SIZE), $(hide) echo "flash_erase_block_size=$(BOARD_FLASH_ERASE_BLOCK_SIZE)" >> $(1))
 $(if $(filter eng, $(TARGET_BUILD_VARIANT)),$(hide) echo "verity_disable=true" >> $(1))
@@ -3779,7 +3781,7 @@
   ALL_DEFAULT_INSTALLED_MODULES += $(_vendor_dlkm_lib_modules_symlink)
 endif
 
-# Install vendor/etc/linker.config.pb with PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS and STUB_LIBRARIES
+# Install vendor/etc/linker.config.pb with PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS and SOONG_STUB_VENDOR_LIBRARIES
 vendor_linker_config_file := $(TARGET_OUT_VENDOR)/etc/linker.config.pb
 $(vendor_linker_config_file): private_linker_config_fragments := $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS)
 $(vendor_linker_config_file): $(INTERNAL_VENDORIMAGE_FILES) $(PRODUCT_VENDOR_LINKER_CONFIG_FRAGMENTS) | $(HOST_OUT_EXECUTABLES)/conv_linker_config
@@ -3790,7 +3792,7 @@
 		--source $(call normalize-path-list,$(private_linker_config_fragments)) \
 		--output $@
 	$(HOST_OUT_EXECUTABLES)/conv_linker_config systemprovide --source $@ \
-		--output $@ --value "$(STUB_LIBRARIES)" --system "$(TARGET_OUT_VENDOR)"
+		--output $@ --value "$(SOONG_STUB_VENDOR_LIBRARIES)" --system "$(TARGET_OUT_VENDOR)"
 $(call define declare-0p-target,$(vendor_linker_config_file),)
 INTERNAL_VENDORIMAGE_FILES += $(vendor_linker_config_file)
 ALL_DEFAULT_INSTALLED_MODULES += $(vendor_linker_config_file)
@@ -4598,6 +4600,12 @@
     --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH)
 endif
 
+# Append avbpubkey of microdroid-vendor partition into vendor_boot partition.
+ifdef MICRODROID_VENDOR_AVBKEY
+BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \
+    --prop_from_file com.android.build.microdroid-vendor.avbpubkey:$(MICRODROID_VENDOR_AVBKEY)
+endif
+
 BOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS
 INIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS
 VENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS
diff --git a/core/binary.mk b/core/binary.mk
index 4c68ba7..8c107bd 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -289,25 +289,20 @@
 endif
 
 ifneq ($(LOCAL_USE_VNDK),)
-  # Required VNDK version for vendor modules is BOARD_VNDK_VERSION.
-  my_api_level := $(BOARD_VNDK_VERSION)
-  ifeq ($(my_api_level),current)
-    # Build with current PLATFORM_VNDK_VERSION.
-    # If PLATFORM_VNDK_VERSION has a CODENAME, it will return
-    # __ANDROID_API_FUTURE__.
-    my_api_level := $(call codename-or-sdk-to-sdk,$(PLATFORM_VNDK_VERSION))
-  else
-    # Build with current BOARD_VNDK_VERSION.
-    my_api_level := $(call codename-or-sdk-to-sdk,$(BOARD_VNDK_VERSION))
-  endif
   my_cflags += -D__ANDROID_VNDK__
   ifneq ($(LOCAL_USE_VNDK_VENDOR),)
-    # Vendor modules have LOCAL_USE_VNDK_VENDOR when
-    # BOARD_VNDK_VERSION is defined.
+    # Vendor modules have LOCAL_USE_VNDK_VENDOR
     my_cflags += -D__ANDROID_VENDOR__
+
+    ifeq ($(BOARD_API_LEVEL),)
+      # TODO(b/314036847): This is a fallback for UDC targets.
+      # This must be a build failure when UDC is no longer built from this source tree.
+      my_cflags += -D__ANDROID_VENDOR_API__=$(PLATFORM_SDK_VERSION)
+    else
+      my_cflags += -D__ANDROID_VENDOR_API__=$(BOARD_API_LEVEL)
+    endif
   else ifneq ($(LOCAL_USE_VNDK_PRODUCT),)
-    # Product modules have LOCAL_USE_VNDK_PRODUCT when
-    # PRODUCT_PRODUCT_VNDK_VERSION is defined.
+    # Product modules have LOCAL_USE_VNDK_PRODUCT
     my_cflags += -D__ANDROID_PRODUCT__
   endif
 endif
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 83be006..3507961 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -355,6 +355,12 @@
   my_sanitize := $(filter-out cfi,$(my_sanitize))
   my_cflags += -fno-lto
   my_ldflags += -fno-lto
+
+  # TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries
+  # discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus
+  # doesn't match the linker script due to the "__emutls_v." prefix).
+  my_cflags += -fno-sanitize-coverage=stack-depth
+  my_ldflags += -fno-sanitize-coverage=stack-depth
 endif
 
 ifneq ($(filter integer_overflow,$(my_sanitize)),)
diff --git a/core/product.mk b/core/product.mk
index 969b506..91b811d 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -447,6 +447,7 @@
 _product_list_vars += PRODUCT_AFDO_PROFILES
 
 _product_single_value_vars += PRODUCT_NEXT_RELEASE_HIDE_FLAGGED_API
+_product_single_value_vars += PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE
 
 _product_list_vars += PRODUCT_RELEASE_CONFIG_MAPS
 
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 30acbba..5fdf416 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -147,6 +147,7 @@
 $(call add_json_bool, ArtUseReadBarrier,                 $(call invert_bool,$(filter false,$(PRODUCT_ART_USE_READ_BARRIER))))
 $(call add_json_str,  BtConfigIncludeDir,                $(BOARD_BLUETOOTH_BDROID_BUILDCFG_INCLUDE_DIR))
 $(call add_json_list, DeviceKernelHeaders,               $(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) $(TARGET_PRODUCT_KERNEL_HEADERS))
+$(call add_json_str,  VendorApiLevel,                    $(BOARD_API_LEVEL))
 $(call add_json_str,  DeviceVndkVersion,                 $(BOARD_VNDK_VERSION))
 $(call add_json_str,  Platform_vndk_version,             $(PLATFORM_VNDK_VERSION))
 $(call add_json_list, ExtraVndkVersions,                 $(PRODUCT_EXTRA_VNDK_VERSIONS))
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index f31749b..4226ef6 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -94,6 +94,7 @@
     framework-graphics \
     framework-minus-apex \
     framework-minus-apex-install-dependencies \
+    framework-nfc \
     framework-res \
     framework-sysconfig.xml \
     fsck.erofs \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index b02a583..55fcf2f 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -50,6 +50,7 @@
 PRODUCT_BOOT_JARS += \
     framework-minus-apex \
     framework-graphics \
+    framework-nfc \
     ext \
     telephony-common \
     voip-common \
diff --git a/target/product/fullmte.mk b/target/product/fullmte.mk
index 5726c06..5e2a694 100644
--- a/target/product/fullmte.mk
+++ b/target/product/fullmte.mk
@@ -25,3 +25,4 @@
   SANITIZE_TARGET_DIAG := $(strip $(SANITIZE_TARGET_DIAG) memtag_heap)
 endif
 PRODUCT_PRODUCT_PROPERTIES += persist.arm64.memtag.default=sync
+PRODUCT_SCUDO_ALLOCATION_RING_BUFFER_SIZE := 131072
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index 6c93dd7..219b90c 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -56,7 +56,6 @@
     MmsService \
     MtpService \
     MusicFX \
-    NfcNci \
     PacProcessor \
     preinstalled-packages-platform-handheld-system.xml \
     PrintRecommendationService \
@@ -73,6 +72,7 @@
     UserDictionaryProvider \
     VpnDialogs \
     vr \
+    $(RELEASE_PACKAGE_NFC_STACK)
 
 
 PRODUCT_SYSTEM_SERVER_APPS += \
diff --git a/tools/OWNERS b/tools/OWNERS
deleted file mode 100644
index 7d666f1..0000000
--- a/tools/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-per-file warn.py,checkowners.py = chh@google.com
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 9e193ec..1a80b04 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -46,6 +46,7 @@
 message flag_declarations {
   optional string package = 1;
   repeated flag_declaration flag = 2;
+  optional string container = 3;
 };
 
 message flag_value {
@@ -79,7 +80,7 @@
   repeated tracepoint trace = 8;
   optional bool is_fixed_read_only = 9;
   optional bool is_exported = 10;
-
+  optional string container = 11;
 }
 
 message parsed_flags {
diff --git a/tools/aconfig/src/codegen.rs b/tools/aconfig/src/codegen.rs
index b7fb08f..fef7a3f 100644
--- a/tools/aconfig/src/codegen.rs
+++ b/tools/aconfig/src/codegen.rs
@@ -38,6 +38,10 @@
     s.split('.').all(is_valid_name_ident)
 }
 
+pub fn is_valid_container_ident(s: &str) -> bool {
+    is_valid_name_ident(s) || s.split('.').all(is_valid_name_ident)
+}
+
 pub fn create_device_config_ident(package: &str, flag_name: &str) -> Result<String> {
     ensure!(is_valid_package_ident(package), "bad package");
     ensure!(is_valid_name_ident(flag_name), "bad flag name");
@@ -85,6 +89,29 @@
     }
 
     #[test]
+    fn test_is_valid_container_ident() {
+        assert!(is_valid_container_ident("foo.bar"));
+        assert!(is_valid_container_ident("foo.bar_baz"));
+        assert!(is_valid_container_ident("foo.bar.a123"));
+        assert!(is_valid_container_ident("foo"));
+        assert!(is_valid_container_ident("foo_bar_123"));
+
+        assert!(!is_valid_container_ident(""));
+        assert!(!is_valid_container_ident("foo._bar"));
+        assert!(!is_valid_container_ident("_foo"));
+        assert!(!is_valid_container_ident("123_foo"));
+        assert!(!is_valid_container_ident("foo-bar"));
+        assert!(!is_valid_container_ident("foo-b\u{00e5}r"));
+        assert!(!is_valid_container_ident("foo.bar.123"));
+        assert!(!is_valid_container_ident(".foo.bar"));
+        assert!(!is_valid_container_ident("foo.bar."));
+        assert!(!is_valid_container_ident("."));
+        assert!(!is_valid_container_ident(".."));
+        assert!(!is_valid_container_ident("foo..bar"));
+        assert!(!is_valid_container_ident("foo.__bar"));
+    }
+
+    #[test]
     fn test_create_device_config_ident() {
         assert_eq!(
             "com.foo.bar.some_flag",
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index 5aa373a..c536260 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -162,6 +162,8 @@
 
     virtual bool disabled_rw() = 0;
 
+    virtual bool disabled_rw_exported() = 0;
+
     virtual bool disabled_rw_in_other_namespace() = 0;
 
     virtual bool enabled_fixed_ro() = 0;
@@ -181,6 +183,10 @@
     return provider_->disabled_rw();
 }
 
+inline bool disabled_rw_exported() {
+    return provider_->disabled_rw_exported();
+}
+
 inline bool disabled_rw_in_other_namespace() {
     return provider_->disabled_rw_in_other_namespace();
 }
@@ -206,6 +212,8 @@
 
 bool com_android_aconfig_test_disabled_rw();
 
+bool com_android_aconfig_test_disabled_rw_exported();
+
 bool com_android_aconfig_test_disabled_rw_in_other_namespace();
 
 bool com_android_aconfig_test_enabled_fixed_ro();
@@ -241,6 +249,10 @@
 
     virtual void disabled_rw(bool val) = 0;
 
+    virtual bool disabled_rw_exported() = 0;
+
+    virtual void disabled_rw_exported(bool val) = 0;
+
     virtual bool disabled_rw_in_other_namespace() = 0;
 
     virtual void disabled_rw_in_other_namespace(bool val) = 0;
@@ -278,6 +290,14 @@
     provider_->disabled_rw(val);
 }
 
+inline bool disabled_rw_exported() {
+    return provider_->disabled_rw_exported();
+}
+
+inline void disabled_rw_exported(bool val) {
+    provider_->disabled_rw_exported(val);
+}
+
 inline bool disabled_rw_in_other_namespace() {
     return provider_->disabled_rw_in_other_namespace();
 }
@@ -327,6 +347,10 @@
 
 void set_com_android_aconfig_test_disabled_rw(bool val);
 
+bool com_android_aconfig_test_disabled_rw_exported();
+
+void set_com_android_aconfig_test_disabled_rw_exported(bool val);
+
 bool com_android_aconfig_test_disabled_rw_in_other_namespace();
 
 void set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val);
@@ -377,14 +401,24 @@
                 return cache_[0];
             }
 
-            virtual bool disabled_rw_in_other_namespace() override {
+            virtual bool disabled_rw_exported() override {
                 if (cache_[1] == -1) {
                     cache_[1] = server_configurable_flags::GetServerConfigurableFlag(
+                        "aconfig_flags.aconfig_test",
+                        "com.android.aconfig.test.disabled_rw_exported",
+                        "false") == "true";
+                }
+                return cache_[1];
+            }
+
+            virtual bool disabled_rw_in_other_namespace() override {
+                if (cache_[2] == -1) {
+                    cache_[2] = server_configurable_flags::GetServerConfigurableFlag(
                         "aconfig_flags.other_namespace",
                         "com.android.aconfig.test.disabled_rw_in_other_namespace",
                         "false") == "true";
                 }
-                return cache_[1];
+                return cache_[2];
             }
 
             virtual bool enabled_fixed_ro() override {
@@ -396,17 +430,17 @@
             }
 
             virtual bool enabled_rw() override {
-                if (cache_[2] == -1) {
-                    cache_[2] = server_configurable_flags::GetServerConfigurableFlag(
+                if (cache_[3] == -1) {
+                    cache_[3] = server_configurable_flags::GetServerConfigurableFlag(
                         "aconfig_flags.aconfig_test",
                         "com.android.aconfig.test.enabled_rw",
                         "true") == "true";
                 }
-                return cache_[2];
+                return cache_[3];
             }
 
     private:
-        std::vector<int8_t> cache_ = std::vector<int8_t>(3, -1);
+        std::vector<int8_t> cache_ = std::vector<int8_t>(4, -1);
     };
 
     std::unique_ptr<flag_provider_interface> provider_ =
@@ -421,6 +455,10 @@
     return com::android::aconfig::test::disabled_rw();
 }
 
+bool com_android_aconfig_test_disabled_rw_exported() {
+    return com::android::aconfig::test::disabled_rw_exported();
+}
+
 bool com_android_aconfig_test_disabled_rw_in_other_namespace() {
     return com::android::aconfig::test::disabled_rw_in_other_namespace();
 }
@@ -485,6 +523,22 @@
                 overrides_["disabled_rw"] = val;
             }
 
+            virtual bool disabled_rw_exported() override {
+                auto it = overrides_.find("disabled_rw_exported");
+                  if (it != overrides_.end()) {
+                      return it->second;
+                } else {
+                  return server_configurable_flags::GetServerConfigurableFlag(
+                      "aconfig_flags.aconfig_test",
+                      "com.android.aconfig.test.disabled_rw_exported",
+                      "false") == "true";
+                }
+            }
+
+            virtual void disabled_rw_exported(bool val) override {
+                overrides_["disabled_rw_exported"] = val;
+            }
+
             virtual bool disabled_rw_in_other_namespace() override {
                 auto it = overrides_.find("disabled_rw_in_other_namespace");
                   if (it != overrides_.end()) {
@@ -570,11 +624,20 @@
     com::android::aconfig::test::disabled_rw(val);
 }
 
+
+bool com_android_aconfig_test_disabled_rw_exported() {
+    return com::android::aconfig::test::disabled_rw_exported();
+}
+
+void set_com_android_aconfig_test_disabled_rw_exported(bool val) {
+    com::android::aconfig::test::disabled_rw_exported(val);
+}
+
+
 bool com_android_aconfig_test_disabled_rw_in_other_namespace() {
     return com::android::aconfig::test::disabled_rw_in_other_namespace();
 }
 
-
 void set_com_android_aconfig_test_disabled_rw_in_other_namespace(bool val) {
     com::android::aconfig::test::disabled_rw_in_other_namespace(val);
 }
@@ -634,6 +697,8 @@
                 match mode {
                     CodegenMode::Production => EXPORTED_PROD_HEADER_EXPECTED,
                     CodegenMode::Test => EXPORTED_TEST_HEADER_EXPECTED,
+                    CodegenMode::Exported =>
+                        todo!("exported mode not yet supported for cpp, see b/313894653."),
                 },
                 generated_files_map.get(&target_file_path).unwrap()
             )
@@ -647,6 +712,8 @@
                 match mode {
                     CodegenMode::Production => PROD_SOURCE_FILE_EXPECTED,
                     CodegenMode::Test => TEST_SOURCE_FILE_EXPECTED,
+                    CodegenMode::Exported =>
+                        todo!("exported mode not yet supported for cpp, see b/313894653."),
                 },
                 generated_files_map.get(&target_file_path).unwrap()
             )
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index a822cd5..b3e5e6c 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -39,6 +39,7 @@
         flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect();
     let is_read_write = flag_elements.iter().any(|elem| elem.is_read_write);
     let is_test_mode = codegen_mode == CodegenMode::Test;
+    let library_exported = codegen_mode == CodegenMode::Exported;
     let context = Context {
         flag_elements,
         namespace_flags,
@@ -46,6 +47,7 @@
         is_read_write,
         properties_set,
         package_name: package.to_string(),
+        library_exported,
     };
     let mut template = TinyTemplate::new();
     template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
@@ -103,6 +105,7 @@
     pub is_read_write: bool,
     pub properties_set: BTreeSet<String>,
     pub package_name: String,
+    pub library_exported: bool,
 }
 
 #[derive(Serialize, Debug)]
@@ -120,6 +123,7 @@
     pub is_read_write: bool,
     pub method_name: String,
     pub properties: String,
+    pub exported: bool,
 }
 
 fn create_flag_element(package: &str, pf: &ProtoParsedFlag) -> FlagElement {
@@ -133,6 +137,7 @@
         is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
         method_name: format_java_method_name(pf.name()),
         properties: format_property_name(pf.namespace()),
+        exported: pf.is_exported.unwrap_or(false),
     }
 }
 
@@ -179,6 +184,8 @@
         @UnsupportedAppUsage
         boolean disabledRw();
         @UnsupportedAppUsage
+        boolean disabledRwExported();
+        @UnsupportedAppUsage
         boolean disabledRwInOtherNamespace();
         @com.android.aconfig.annotations.AssumeTrueForR8
         @UnsupportedAppUsage
@@ -202,6 +209,8 @@
         /** @hide */
         public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
         /** @hide */
+        public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
+        /** @hide */
         public static final String FLAG_DISABLED_RW_IN_OTHER_NAMESPACE = "com.android.aconfig.test.disabled_rw_in_other_namespace";
         /** @hide */
         public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro";
@@ -220,6 +229,10 @@
             return FEATURE_FLAGS.disabledRw();
         }
         @UnsupportedAppUsage
+        public static boolean disabledRwExported() {
+            return FEATURE_FLAGS.disabledRwExported();
+        }
+        @UnsupportedAppUsage
         public static boolean disabledRwInOtherNamespace() {
             return FEATURE_FLAGS.disabledRwInOtherNamespace();
         }
@@ -262,6 +275,11 @@
         }
         @Override
         @UnsupportedAppUsage
+        public boolean disabledRwExported() {
+            return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+        }
+        @Override
+        @UnsupportedAppUsage
         public boolean disabledRwInOtherNamespace() {
             return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE);
         }
@@ -302,6 +320,7 @@
             Map.ofEntries(
                 Map.entry(Flags.FLAG_DISABLED_RO, false),
                 Map.entry(Flags.FLAG_DISABLED_RW, false),
+                Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
                 Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
                 Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
                 Map.entry(Flags.FLAG_ENABLED_RO, false),
@@ -336,6 +355,7 @@
             private static boolean aconfig_test_is_cached = false;
             private static boolean other_namespace_is_cached = false;
             private static boolean disabledRw = false;
+            private static boolean disabledRwExported = false;
             private static boolean disabledRwInOtherNamespace = false;
             private static boolean enabledRw = true;
 
@@ -345,6 +365,8 @@
                     Properties properties = DeviceConfig.getProperties("aconfig_test");
                     disabledRw =
                         properties.getBoolean("com.android.aconfig.test.disabled_rw", false);
+                    disabledRwExported =
+                        properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
                     enabledRw =
                         properties.getBoolean("com.android.aconfig.test.enabled_rw", true);
                 } catch (NullPointerException e) {
@@ -394,6 +416,14 @@
             }
             @Override
             @UnsupportedAppUsage
+            public boolean disabledRwExported() {
+                if (!aconfig_test_is_cached) {
+                    load_overrides_aconfig_test();
+                }
+                return disabledRwExported;
+            }
+            @Override
+            @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 if (!other_namespace_is_cached) {
                     load_overrides_other_namespace();
@@ -449,6 +479,202 @@
     }
 
     #[test]
+    fn test_generate_java_code_exported() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            parsed_flags.parsed_flag.iter(),
+            CodegenMode::Exported,
+        )
+        .unwrap();
+
+        let expect_flags_content = r#"
+        package com.android.aconfig.test;
+        // TODO(b/303773055): Remove the annotation after access issue is resolved.
+        import android.compat.annotation.UnsupportedAppUsage;
+        /** @hide */
+        public final class Flags {
+            /** @hide */
+            public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
+            /** @hide */
+            public static final String FLAG_DISABLED_RW_EXPORTED = "com.android.aconfig.test.disabled_rw_exported";
+
+            @UnsupportedAppUsage
+            public static boolean disabledRw() {
+                return FEATURE_FLAGS.disabledRw();
+            }
+            @UnsupportedAppUsage
+            public static boolean disabledRwExported() {
+                return FEATURE_FLAGS.disabledRwExported();
+            }
+            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+        }
+        "#;
+
+        let expect_feature_flags_content = r#"
+        package com.android.aconfig.test;
+        // TODO(b/303773055): Remove the annotation after access issue is resolved.
+        import android.compat.annotation.UnsupportedAppUsage;
+        /** @hide */
+        public interface FeatureFlags {
+            @UnsupportedAppUsage
+            boolean disabledRw();
+            @UnsupportedAppUsage
+            boolean disabledRwExported();
+        }
+        "#;
+
+        let expect_feature_flags_impl_content = r#"
+        package com.android.aconfig.test;
+        // TODO(b/303773055): Remove the annotation after access issue is resolved.
+        import android.compat.annotation.UnsupportedAppUsage;
+        import android.provider.DeviceConfig;
+        import android.provider.DeviceConfig.Properties;
+        /** @hide */
+        public final class FeatureFlagsImpl implements FeatureFlags {
+            private static boolean aconfig_test_is_cached = false;
+            private static boolean other_namespace_is_cached = false;
+            private static boolean disabledRw = false;
+            private static boolean disabledRwExported = false;
+
+
+            private void load_overrides_aconfig_test() {
+                try {
+                    Properties properties = DeviceConfig.getProperties("aconfig_test");
+                    disabledRw =
+                        properties.getBoolean("com.android.aconfig.test.disabled_rw", false);
+                    disabledRwExported =
+                        properties.getBoolean("com.android.aconfig.test.disabled_rw_exported", false);
+                } catch (NullPointerException e) {
+                    throw new RuntimeException(
+                        "Cannot read value from namespace aconfig_test "
+                        + "from DeviceConfig. It could be that the code using flag "
+                        + "executed before SettingsProvider initialization. Please use "
+                        + "fixed read-only flag by adding is_fixed_read_only: true in "
+                        + "flag declaration.",
+                        e
+                    );
+                }
+                aconfig_test_is_cached = true;
+            }
+
+            private void load_overrides_other_namespace() {
+                try {
+                    Properties properties = DeviceConfig.getProperties("other_namespace");
+                } catch (NullPointerException e) {
+                    throw new RuntimeException(
+                        "Cannot read value from namespace other_namespace "
+                        + "from DeviceConfig. It could be that the code using flag "
+                        + "executed before SettingsProvider initialization. Please use "
+                        + "fixed read-only flag by adding is_fixed_read_only: true in "
+                        + "flag declaration.",
+                        e
+                    );
+                }
+                other_namespace_is_cached = true;
+            }
+
+            @Override
+            @UnsupportedAppUsage
+            public boolean disabledRw() {
+                if (!aconfig_test_is_cached) {
+                    load_overrides_aconfig_test();
+                }
+                return disabledRw;
+            }
+
+            @Override
+            @UnsupportedAppUsage
+            public boolean disabledRwExported() {
+                if (!aconfig_test_is_cached) {
+                    load_overrides_aconfig_test();
+                }
+                return disabledRwExported;
+            }
+        }"#;
+
+        let expect_fake_feature_flags_impl_content = r#"
+        package com.android.aconfig.test;
+        // TODO(b/303773055): Remove the annotation after access issue is resolved.
+        import android.compat.annotation.UnsupportedAppUsage;
+        import java.util.HashMap;
+        import java.util.Map;
+        /** @hide */
+        public class FakeFeatureFlagsImpl implements FeatureFlags {
+            public FakeFeatureFlagsImpl() {
+                resetAll();
+            }
+            @Override
+            @UnsupportedAppUsage
+            public boolean disabledRw() {
+                return getValue(Flags.FLAG_DISABLED_RW);
+            }
+            @Override
+            @UnsupportedAppUsage
+            public boolean disabledRwExported() {
+                return getValue(Flags.FLAG_DISABLED_RW_EXPORTED);
+            }
+            public void setFlag(String flagName, boolean value) {
+                if (!this.mFlagMap.containsKey(flagName)) {
+                    throw new IllegalArgumentException("no such flag " + flagName);
+                }
+                this.mFlagMap.put(flagName, value);
+            }
+            public void resetAll() {
+                for (Map.Entry entry : mFlagMap.entrySet()) {
+                    entry.setValue(null);
+                }
+            }
+            private boolean getValue(String flagName) {
+                Boolean value = this.mFlagMap.get(flagName);
+                if (value == null) {
+                    throw new IllegalArgumentException(flagName + " is not set");
+                }
+                return value;
+            }
+            private Map<String, Boolean> mFlagMap = new HashMap<>(
+                Map.ofEntries(
+                    Map.entry(Flags.FLAG_DISABLED_RO, false),
+                    Map.entry(Flags.FLAG_DISABLED_RW, false),
+                    Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false),
+                    Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false),
+                    Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false),
+                    Map.entry(Flags.FLAG_ENABLED_RO, false),
+                    Map.entry(Flags.FLAG_ENABLED_RW, false)
+                )
+            );
+        }
+    "#;
+
+        let mut file_set = HashMap::from([
+            ("com/android/aconfig/test/Flags.java", expect_flags_content),
+            ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content),
+            (
+                "com/android/aconfig/test/FakeFeatureFlagsImpl.java",
+                expect_fake_feature_flags_impl_content,
+            ),
+        ]);
+
+        for file in generated_files {
+            let file_path = file.path.to_str().unwrap();
+            assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
+            assert_eq!(
+                None,
+                crate::test::first_significant_code_diff(
+                    file_set.get(file_path).unwrap(),
+                    &String::from_utf8(file.contents).unwrap()
+                ),
+                "File {} content is not correct",
+                file_path
+            );
+            file_set.remove(file_path);
+        }
+
+        assert!(file_set.is_empty());
+    }
+
+    #[test]
     fn test_generate_java_code_test() {
         let parsed_flags = crate::test::parse_test_flags();
         let generated_files = generate_java_code(
@@ -489,6 +715,12 @@
             }
             @Override
             @UnsupportedAppUsage
+            public boolean disabledRwExported() {
+                throw new UnsupportedOperationException(
+                    "Method is not implemented.");
+            }
+            @Override
+            @UnsupportedAppUsage
             public boolean disabledRwInOtherNamespace() {
                 throw new UnsupportedOperationException(
                     "Method is not implemented.");
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
index d8675e7..502cec8 100644
--- a/tools/aconfig/src/codegen_rust.rs
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -45,6 +45,9 @@
         match codegen_mode {
             CodegenMode::Production => include_str!("../templates/rust_prod.template"),
             CodegenMode::Test => include_str!("../templates/rust_test.template"),
+            CodegenMode::Exported => {
+                todo!("exported mode not yet supported for rust, see b/313894653.")
+            }
         },
     )?;
     let contents = template.render("rust_code_gen", &context)?;
@@ -104,6 +107,12 @@
         "com.android.aconfig.test.disabled_rw",
         "false") == "true";
 
+    /// flag value cache for disabled_rw_exported
+    static ref CACHED_disabled_rw_exported: bool = flags_rust::GetServerConfigurableFlag(
+        "aconfig_flags.aconfig_test",
+        "com.android.aconfig.test.disabled_rw_exported",
+        "false") == "true";
+
     /// flag value cache for disabled_rw_in_other_namespace
     static ref CACHED_disabled_rw_in_other_namespace: bool = flags_rust::GetServerConfigurableFlag(
         "aconfig_flags.other_namespace",
@@ -115,6 +124,7 @@
         "aconfig_flags.aconfig_test",
         "com.android.aconfig.test.enabled_rw",
         "true") == "true";
+
 }
 
 impl FlagProvider {
@@ -128,6 +138,11 @@
         *CACHED_disabled_rw
     }
 
+    /// query flag disabled_rw_exported
+    pub fn disabled_rw_exported(&self) -> bool {
+        *CACHED_disabled_rw_exported
+    }
+
     /// query flag disabled_rw_in_other_namespace
     pub fn disabled_rw_in_other_namespace(&self) -> bool {
         *CACHED_disabled_rw_in_other_namespace
@@ -164,6 +179,12 @@
     PROVIDER.disabled_rw()
 }
 
+/// query flag disabled_rw_exported
+#[inline(always)]
+pub fn disabled_rw_exported() -> bool {
+    PROVIDER.disabled_rw_exported()
+}
+
 /// query flag disabled_rw_in_other_namespace
 #[inline(always)]
 pub fn disabled_rw_in_other_namespace() -> bool {
@@ -228,6 +249,21 @@
         self.overrides.insert("disabled_rw", val);
     }
 
+    /// query flag disabled_rw_exported
+    pub fn disabled_rw_exported(&self) -> bool {
+        self.overrides.get("disabled_rw_exported").copied().unwrap_or(
+            flags_rust::GetServerConfigurableFlag(
+                "aconfig_flags.aconfig_test",
+                "com.android.aconfig.test.disabled_rw_exported",
+                "false") == "true"
+        )
+    }
+
+    /// set flag disabled_rw_exported
+    pub fn set_disabled_rw_exported(&mut self, val: bool) {
+        self.overrides.insert("disabled_rw_exported", val);
+    }
+
     /// query flag disabled_rw_in_other_namespace
     pub fn disabled_rw_in_other_namespace(&self) -> bool {
         self.overrides.get("disabled_rw_in_other_namespace").copied().unwrap_or(
@@ -317,6 +353,18 @@
     PROVIDER.lock().unwrap().set_disabled_rw(val);
 }
 
+/// query flag disabled_rw_exported
+#[inline(always)]
+pub fn disabled_rw_exported() -> bool {
+    PROVIDER.lock().unwrap().disabled_rw_exported()
+}
+
+/// set flag disabled_rw_exported
+#[inline(always)]
+pub fn set_disabled_rw_exported(val: bool) {
+    PROVIDER.lock().unwrap().set_disabled_rw_exported(val);
+}
+
 /// query flag disabled_rw_in_other_namespace
 #[inline(always)]
 pub fn disabled_rw_in_other_namespace() -> bool {
@@ -383,6 +431,8 @@
                 match mode {
                     CodegenMode::Production => PROD_EXPECTED,
                     CodegenMode::Test => TEST_EXPECTED,
+                    CodegenMode::Exported =>
+                        todo!("exported mode not yet supported for rust, see b/313894653."),
                 },
                 &String::from_utf8(generated.contents).unwrap()
             )
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index ed7489f..37ee79e 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -57,6 +57,7 @@
 
 pub fn parse_flags(
     package: &str,
+    container: Option<&str>,
     declarations: Vec<Input>,
     values: Vec<Input>,
     default_permission: ProtoFlagPermission,
@@ -79,12 +80,24 @@
             package,
             flag_declarations.package()
         );
+        if let Some(c) = container {
+            ensure!(
+                c == flag_declarations.container(),
+                "failed to parse {}: expected container {}, got {}",
+                input.source,
+                c,
+                flag_declarations.container()
+            );
+        }
         for mut flag_declaration in flag_declarations.flag.into_iter() {
             crate::protos::flag_declaration::verify_fields(&flag_declaration)
                 .with_context(|| input.error_context())?;
 
             // create ParsedFlag using FlagDeclaration and default values
             let mut parsed_flag = ProtoParsedFlag::new();
+            if let Some(c) = container {
+                parsed_flag.set_container(c.to_string());
+            }
             parsed_flag.set_package(package.to_string());
             parsed_flag.set_name(flag_declaration.take_name());
             parsed_flag.set_namespace(flag_declaration.take_namespace());
@@ -171,6 +184,7 @@
 pub enum CodegenMode {
     Production,
     Test,
+    Exported,
 }
 
 pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
@@ -261,9 +275,10 @@
         DumpFormat::Text => {
             for parsed_flag in parsed_flags.parsed_flag.into_iter() {
                 let line = format!(
-                    "{}.{}: {:?} + {:?}\n",
+                    "{}.{} [{}]: {:?} + {:?}\n",
                     parsed_flag.package(),
                     parsed_flag.name(),
+                    parsed_flag.container(),
                     parsed_flag.permission(),
                     parsed_flag.state()
                 );
@@ -275,9 +290,10 @@
                 let sources: Vec<_> =
                     parsed_flag.trace.iter().map(|tracepoint| tracepoint.source()).collect();
                 let line = format!(
-                    "{}.{}: {:?} + {:?} ({})\n",
+                    "{}.{} [{}]: {:?} + {:?} ({})\n",
                     parsed_flag.package(),
                     parsed_flag.name(),
+                    parsed_flag.container(),
                     parsed_flag.permission(),
                     parsed_flag.state(),
                     sources.join(", ")
@@ -335,7 +351,7 @@
         assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());
         assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());
 
-        assert_eq!(6, parsed_flags.parsed_flag.len());
+        assert_eq!(7, parsed_flags.parsed_flag.len());
         for pf in parsed_flags.parsed_flag.iter() {
             if pf.name() == "enabled_fixed_ro" {
                 continue;
@@ -376,6 +392,7 @@
 
         let flags_bytes = crate::commands::parse_flags(
             "com.first",
+            None,
             declaration,
             value,
             ProtoFlagPermission::READ_ONLY,
@@ -390,9 +407,72 @@
     }
 
     #[test]
+    fn test_parse_flags_package_mismatch_between_declaration_and_command_line() {
+        let first_flag = r#"
+        package: "com.declaration.package"
+        container: "first.container"
+        flag {
+            name: "first"
+            namespace: "first_ns"
+            description: "This is the description of the first flag."
+            bug: "123"
+        }
+        "#;
+        let declaration =
+            vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
+
+        let value: Vec<Input> = vec![];
+
+        let error = crate::commands::parse_flags(
+            "com.argument.package",
+            Some("first.container"),
+            declaration,
+            value,
+            ProtoFlagPermission::READ_WRITE,
+        )
+        .unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "failed to parse memory: expected package com.argument.package, got com.declaration.package"
+        );
+    }
+
+    #[test]
+    fn test_parse_flags_container_mismatch_between_declaration_and_command_line() {
+        let first_flag = r#"
+        package: "com.first"
+        container: "declaration.container"
+        flag {
+            name: "first"
+            namespace: "first_ns"
+            description: "This is the description of the first flag."
+            bug: "123"
+        }
+        "#;
+        let declaration =
+            vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
+
+        let value: Vec<Input> = vec![];
+
+        let error = crate::commands::parse_flags(
+            "com.first",
+            Some("argument.container"),
+            declaration,
+            value,
+            ProtoFlagPermission::READ_WRITE,
+        )
+        .unwrap_err();
+        assert_eq!(
+            format!("{:?}", error),
+            "failed to parse memory: expected container argument.container, got declaration.container"
+        );
+    }
+
+    #[test]
     fn test_parse_flags_override_fixed_read_only() {
         let first_flag = r#"
         package: "com.first"
+        container: "com.first.container"
         flag {
             name: "first"
             namespace: "first_ns"
@@ -418,6 +498,7 @@
         }];
         let error = crate::commands::parse_flags(
             "com.first",
+            Some("com.first.container"),
             declaration,
             value,
             ProtoFlagPermission::READ_WRITE,
@@ -434,7 +515,7 @@
         let input = parse_test_flags_as_input();
         let bytes = create_device_config_defaults(input).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\nother_namespace:com.android.aconfig.test.disabled_rw_in_other_namespace=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
+        assert_eq!("aconfig_test:com.android.aconfig.test.disabled_rw=disabled\naconfig_test:com.android.aconfig.test.disabled_rw_exported=disabled\nother_namespace:com.android.aconfig.test.disabled_rw_in_other_namespace=disabled\naconfig_test:com.android.aconfig.test.enabled_rw=enabled\n", text);
     }
 
     #[test]
@@ -442,7 +523,7 @@
         let input = parse_test_flags_as_input();
         let bytes = create_device_config_sysprops(input).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.disabled_rw_in_other_namespace=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
+        assert_eq!("persist.device_config.com.android.aconfig.test.disabled_rw=false\npersist.device_config.com.android.aconfig.test.disabled_rw_exported=false\npersist.device_config.com.android.aconfig.test.disabled_rw_in_other_namespace=false\npersist.device_config.com.android.aconfig.test.enabled_rw=true\n", text);
     }
 
     #[test]
@@ -450,7 +531,9 @@
         let input = parse_test_flags_as_input();
         let bytes = dump_parsed_flags(vec![input], DumpFormat::Text, false).unwrap();
         let text = std::str::from_utf8(&bytes).unwrap();
-        assert!(text.contains("com.android.aconfig.test.disabled_ro: READ_ONLY + DISABLED"));
+        assert!(
+            text.contains("com.android.aconfig.test.disabled_ro [system]: READ_ONLY + DISABLED")
+        );
     }
 
     #[test]
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 17c388d..90464c5 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -42,6 +42,8 @@
         .subcommand(
             Command::new("create-cache")
                 .arg(Arg::new("package").long("package").required(true))
+                // TODO(b/312769710): Make this argument required.
+                .arg(Arg::new("container").long("container"))
                 .arg(Arg::new("declarations").long("declarations").action(ArgAction::Append))
                 .arg(Arg::new("values").long("values").action(ArgAction::Append))
                 .arg(
@@ -120,6 +122,13 @@
         .ok_or(anyhow!("internal error: required argument '{}' not found", arg_name))
 }
 
+fn get_optional_arg<'a, T>(matches: &'a ArgMatches, arg_name: &str) -> Option<&'a T>
+where
+    T: Any + Clone + Send + Sync + 'static,
+{
+    matches.get_one::<T>(arg_name)
+}
+
 fn open_zero_or_more_files(matches: &ArgMatches, arg_name: &str) -> Result<Vec<Input>> {
     let mut opened_files = vec![];
     for path in matches.get_many::<String>(arg_name).unwrap_or_default() {
@@ -168,12 +177,20 @@
     match matches.subcommand() {
         Some(("create-cache", sub_matches)) => {
             let package = get_required_arg::<String>(sub_matches, "package")?;
+            let container =
+                get_optional_arg::<String>(sub_matches, "container").map(|c| c.as_str());
             let declarations = open_zero_or_more_files(sub_matches, "declarations")?;
             let values = open_zero_or_more_files(sub_matches, "values")?;
             let default_permission =
                 get_required_arg::<protos::ProtoFlagPermission>(sub_matches, "default-permission")?;
-            let output = commands::parse_flags(package, declarations, values, *default_permission)
-                .context("failed to create cache")?;
+            let output = commands::parse_flags(
+                package,
+                container,
+                declarations,
+                values,
+                *default_permission,
+            )
+            .context("failed to create cache")?;
             let path = get_required_arg::<String>(sub_matches, "cache")?;
             write_output_to_file_or_stdout(path, &output)?;
         }
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index 8e76468..3d9089c 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -111,11 +111,16 @@
 
     pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
         ensure_required_fields!("flag declarations", pdf, "package");
+        // TODO(b/312769710): Make the container field required.
 
         ensure!(
             codegen::is_valid_package_ident(pdf.package()),
             "bad flag declarations: bad package"
         );
+        ensure!(
+            !pdf.has_container() || codegen::is_valid_container_ident(pdf.container()),
+            "bad flag declarations: bad container"
+        );
         for flag_declaration in pdf.flag.iter() {
             super::flag_declaration::verify_fields(flag_declaration)?;
         }
@@ -207,6 +212,10 @@
         );
 
         ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
+        ensure!(
+            !pf.has_container() || codegen::is_valid_container_ident(pf.container()),
+            "bad parsed flag: bad container"
+        );
         ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
         ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
         ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
@@ -309,6 +318,7 @@
         let flag_declarations = flag_declarations::try_from_text_proto(
             r#"
 package: "com.foo.bar"
+container: "system"
 flag {
     name: "first"
     namespace: "first_ns"
@@ -327,6 +337,7 @@
         )
         .unwrap();
         assert_eq!(flag_declarations.package(), "com.foo.bar");
+        assert_eq!(flag_declarations.container(), "system");
         let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
         assert_eq!(first.name(), "first");
         assert_eq!(first.namespace(), "first_ns");
@@ -342,9 +353,26 @@
         assert!(second.is_fixed_read_only());
         assert!(!second.is_exported());
 
+        // valid input: missing container in flag declarations is supported
+        let flag_declarations = flag_declarations::try_from_text_proto(
+            r#"
+package: "com.foo.bar"
+flag {
+    name: "first"
+    namespace: "first_ns"
+    description: "This is the description of the first flag."
+    bug: "123"
+}
+"#,
+        )
+        .unwrap();
+        assert_eq!(flag_declarations.container(), "");
+        assert!(!flag_declarations.has_container());
+
         // bad input: missing package in flag declarations
         let error = flag_declarations::try_from_text_proto(
             r#"
+container: "system"
 flag {
     name: "first"
     namespace: "first_ns"
@@ -364,6 +392,7 @@
         let error = flag_declarations::try_from_text_proto(
             r#"
 package: "com.foo.bar"
+container: "system"
 flag {
     name: "first"
     description: "This is the description of the first flag."
@@ -382,6 +411,7 @@
         let error = flag_declarations::try_from_text_proto(
             r#"
 package: "_com.FOO__BAR"
+container: "system"
 flag {
     name: "first"
     namespace: "first_ns"
@@ -401,6 +431,7 @@
         let error = flag_declarations::try_from_text_proto(
             r#"
 package: "com.foo.bar"
+container: "system"
 flag {
     name: "FIRST"
     namespace: "first_ns"
@@ -420,6 +451,7 @@
         let error = flag_declarations::try_from_text_proto(
             r#"
 package: "com.foo.bar"
+container: "system"
 flag {
     name: "first"
     namespace: "first_ns"
@@ -434,6 +466,7 @@
         let error = flag_declarations::try_from_text_proto(
             r#"
 package: "com.foo.bar"
+container: "system"
 flag {
     name: "first"
     namespace: "first_ns"
@@ -445,6 +478,25 @@
         )
         .unwrap_err();
         assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
+
+        // bad input: invalid container name in flag declaration
+        let error = flag_declarations::try_from_text_proto(
+            r#"
+package: "com.foo.bar"
+container: "__bad_bad_container.com"
+flag {
+    name: "first"
+    namespace: "first_ns"
+    description: "This is the description of the first flag."
+    bug: "123"
+    bug: "abc"
+}
+"#,
+        )
+        .unwrap_err();
+        assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
+
+        // TODO(b/312769710): Verify error when container is missing.
     }
 
     #[test]
@@ -559,6 +611,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 parsed_flag {
     package: "com.second"
@@ -579,6 +632,7 @@
         permission: READ_ONLY
     }
     is_fixed_read_only: true
+    container: "system"
 }
 "#;
         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -613,6 +667,7 @@
     description: "This is the description of the first flag."
     state: DISABLED
     permission: READ_ONLY
+    container: "system"
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
@@ -631,6 +686,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
@@ -651,6 +707,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 parsed_flag {
     package: "aaa.aaa"
@@ -665,6 +722,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
@@ -688,6 +746,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 parsed_flag {
     package: "com.foo"
@@ -702,6 +761,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
@@ -725,6 +785,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 parsed_flag {
     package: "com.foo"
@@ -739,6 +800,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
@@ -766,6 +828,7 @@
         state: ENABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -792,6 +855,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 parsed_flag {
     package: "com.second"
@@ -806,6 +870,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -824,6 +889,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -842,6 +908,7 @@
         state: DISABLED
         permission: READ_ONLY
     }
+    container: "system"
 }
 "#;
         let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/src/test.rs
index 31c67b3..8df4493 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/src/test.rs
@@ -43,6 +43,7 @@
   }
   is_fixed_read_only: false
   is_exported: false
+  container: "system"
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -59,6 +60,29 @@
   }
   is_fixed_read_only: false
   is_exported: true
+  container: "system"
+}
+parsed_flag {
+  package: "com.android.aconfig.test"
+  name: "disabled_rw_exported"
+  namespace: "aconfig_test"
+  description: "This flag is exported"
+  bug: "111"
+  state: DISABLED
+  permission: READ_WRITE
+  trace {
+    source: "tests/test.aconfig"
+    state: DISABLED
+    permission: READ_WRITE
+  }
+  trace {
+    source: "tests/first.values"
+    state: DISABLED
+    permission: READ_WRITE
+  }
+  is_fixed_read_only: false
+  is_exported: true
+  container: "system"
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -80,6 +104,7 @@
   }
   is_fixed_read_only: false
   is_exported: false
+  container: "system"
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -101,6 +126,7 @@
   }
   is_fixed_read_only: true
   is_exported: false
+  container: "system"
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -127,6 +153,7 @@
   }
   is_fixed_read_only: false
   is_exported: false
+  container: "system"
 }
 parsed_flag {
   package: "com.android.aconfig.test"
@@ -148,12 +175,14 @@
   }
   is_fixed_read_only: false
   is_exported: false
+  container: "system"
 }
 "#;
 
     pub fn parse_test_flags() -> ProtoParsedFlags {
         let bytes = crate::commands::parse_flags(
             "com.android.aconfig.test",
+            Some("system"),
             vec![Input {
                 source: "tests/test.aconfig".to_string(),
                 reader: Box::new(include_bytes!("../tests/test.aconfig").as_slice()),
diff --git a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
index 933d6a7..fd2e26a 100644
--- a/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FakeFeatureFlagsImpl.java.template
@@ -12,11 +12,23 @@
     }
 
 {{ for item in flag_elements}}
+{{ if library_exported }}
+
+{{ if item.exported }}
     @Override
     @UnsupportedAppUsage
     public boolean {item.method_name}() \{
         return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
     }
+{{ endif }}
+
+{{ else }}
+    @Override
+    @UnsupportedAppUsage
+    public boolean {item.method_name}() \{
+        return getValue(Flags.FLAG_{item.flag_name_constant_suffix});
+    }
+{{ endif }}
 {{ endfor}}
     public void setFlag(String flagName, boolean value) \{
         if (!this.mFlagMap.containsKey(flagName)) \{
diff --git a/tools/aconfig/templates/FeatureFlags.java.template b/tools/aconfig/templates/FeatureFlags.java.template
index da850ae..180f882 100644
--- a/tools/aconfig/templates/FeatureFlags.java.template
+++ b/tools/aconfig/templates/FeatureFlags.java.template
@@ -5,6 +5,15 @@
 /** @hide */
 public interface FeatureFlags \{
 {{ for item in flag_elements }}
+{{ if library_exported }}
+
+{{ if item.exported }}
+    @UnsupportedAppUsage
+    boolean {item.method_name}();
+{{ endif }}
+
+{{ else }}
+
 {{ -if not item.is_read_write }}
 {{ -if item.default_value }}
     @com.android.aconfig.annotations.AssumeTrueForR8
@@ -14,5 +23,7 @@
 {{ endif }}
     @UnsupportedAppUsage
     boolean {item.method_name}();
+
+{{ endif }}
 {{ endfor }}
 }
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/templates/FeatureFlagsImpl.java.template
index ec8822c..a15c859 100644
--- a/tools/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FeatureFlagsImpl.java.template
@@ -14,9 +14,17 @@
 {{- endfor- }}
 
 {{ for flag in flag_elements }}
+{{ if library_exported }}
+{{ if flag.exported }}
+    private static boolean {flag.method_name} = false;
+{{ endif }}
+
+{{ else }}
+
 {{- if flag.is_read_write }}
     private static boolean {flag.method_name} = {flag.default_value};
 {{- endif- }}
+{{ endif }}
 {{ endfor }}
 
 {{ for namespace_with_flags in namespace_flags }}
@@ -25,10 +33,21 @@
             Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
 
             {{- for flag in namespace_with_flags.flags }}
-            {{- if flag.is_read_write }}
+            {{ if library_exported }}
+
+            {{ if flag.exported }}
+            {flag.method_name} =
+                properties.getBoolean("{flag.device_config_flag}", false);
+            {{ endif }}
+
+            {{ else }}
+
+            {{ if flag.is_read_write }}
             {flag.method_name} =
                 properties.getBoolean("{flag.device_config_flag}", {flag.default_value});
-            {{- endif- }}
+            {{ endif }}
+
+            {{ endif }}
             {{ endfor }}
         } catch (NullPointerException e) \{
             throw new RuntimeException(
@@ -46,6 +65,9 @@
 {{ endif- }}
 
 {{ for flag in flag_elements }}
+{{ if library_exported }}
+
+{{ if flag.exported }}
     @Override
     @UnsupportedAppUsage
     public boolean {flag.method_name}() \{
@@ -58,6 +80,23 @@
         return {flag.default_value};
     {{ endif- }}
     }
+{{ endif }}
+
+{{ else }}
+    @Override
+    @UnsupportedAppUsage
+    public boolean {flag.method_name}() \{
+    {{ -if flag.is_read_write }}
+        if (!{flag.device_config_namespace}_is_cached) \{
+            load_overrides_{flag.device_config_namespace}();
+        }
+        return {flag.method_name};
+    {{ else }}
+        return {flag.default_value};
+    {{ endif- }}
+    }
+{{ endif }}
+
 {{ endfor }}
 }
 {{ else }}
diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/templates/Flags.java.template
index cf6604c..9f4c52f 100644
--- a/tools/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/templates/Flags.java.template
@@ -6,10 +6,28 @@
 /** @hide */
 public final class Flags \{
 {{- for item in flag_elements}}
+    {{ if library_exported }}
+    {{ if item.exported }}
     /** @hide */
     public static final String FLAG_{item.flag_name_constant_suffix} = "{item.device_config_flag}";
+    {{ endif }}
+    {{ else }}
+    /** @hide */
+    public static final String FLAG_{item.flag_name_constant_suffix} = "{item.device_config_flag}";
+    {{ endif }}
 {{- endfor }}
 {{ for item in flag_elements}}
+{{ if library_exported }}
+
+{{ if item.exported }}
+    @UnsupportedAppUsage
+    public static boolean {item.method_name}() \{
+        return FEATURE_FLAGS.{item.method_name}();
+    }
+{{ endif }}
+
+{{ else }}
+
 {{ -if not item.is_read_write }}
 {{ -if item.default_value }}
     @com.android.aconfig.annotations.AssumeTrueForR8
@@ -21,6 +39,7 @@
     public static boolean {item.method_name}() \{
         return FEATURE_FLAGS.{item.method_name}();
     }
+{{ endif }}
 {{ endfor }}
 {{ -if is_test_mode }}
     public static void setFeatureFlags(FeatureFlags featureFlags) \{
diff --git a/tools/aconfig/tests/first.values b/tools/aconfig/tests/first.values
index 07d8d1d..b248d43 100644
--- a/tools/aconfig/tests/first.values
+++ b/tools/aconfig/tests/first.values
@@ -28,3 +28,9 @@
     state: ENABLED
     permission: READ_ONLY
 }
+flag_value {
+    package: "com.android.aconfig.test"
+    name: "disabled_rw_exported"
+    state: DISABLED
+    permission: READ_WRITE
+}
diff --git a/tools/aconfig/tests/test.aconfig b/tools/aconfig/tests/test.aconfig
index b49b665..e44d154 100644
--- a/tools/aconfig/tests/test.aconfig
+++ b/tools/aconfig/tests/test.aconfig
@@ -1,4 +1,5 @@
 package: "com.android.aconfig.test"
+container: "system"
 
 # This flag's final value is calculated from:
 # - test.aconfig: DISABLED + READ_WRITE (default)
@@ -59,3 +60,11 @@
     description: "This flag is DISABLED + READ_WRITE, and is defined in another namespace"
     bug: "999"
 }
+
+flag {
+    name: "disabled_rw_exported"
+    namespace: "aconfig_test"
+    description: "This flag is exported"
+    bug: "111"
+    is_exported: true
+}
\ No newline at end of file
diff --git a/tools/checkowners.py b/tools/checkowners.py
deleted file mode 100755
index f037321..0000000
--- a/tools/checkowners.py
+++ /dev/null
@@ -1,87 +0,0 @@
-#!/usr/bin/python
-
-"""Parse and check syntax errors of a given OWNERS file."""
-
-import argparse
-import re
-import sys
-import urllib.request, urllib.parse, urllib.error
-import urllib.request, urllib.error, urllib.parse
-
-parser = argparse.ArgumentParser(description='Check OWNERS file syntax')
-parser.add_argument('-v', '--verbose', dest='verbose',
-                    action='store_true', default=False,
-                    help='Verbose output to debug')
-parser.add_argument('-c', '--check_address', dest='check_address',
-                    action='store_true', default=False,
-                    help='Check email addresses')
-parser.add_argument(dest='owners', metavar='OWNERS', nargs='+',
-                    help='Path to OWNERS file')
-args = parser.parse_args()
-
-gerrit_server = 'https://android-review.googlesource.com'
-checked_addresses = {}
-
-
-def echo(msg):
-  if args.verbose:
-    print(msg)
-
-
-def find_address(address):
-  if address not in checked_addresses:
-    request = (gerrit_server + '/accounts/?n=1&q=email:'
-               + urllib.parse.quote(address))
-    echo('Checking email address: ' + address)
-    result = urllib.request.urlopen(request).read()
-    checked_addresses[address] = result.find('"_account_id":') >= 0
-    if checked_addresses[address]:
-      echo('Found email address: ' + address)
-  return checked_addresses[address]
-
-
-def check_address(fname, num, address):
-  if find_address(address):
-    return 0
-  print('%s:%d: ERROR: unknown email address: %s' % (fname, num, address))
-  return 1
-
-
-def main():
-  # One regular expression to check all valid lines.
-  noparent = 'set +noparent'
-  email = '([^@ ]+@[^ @]+|\\*)'
-  emails = '(%s( *, *%s)*)' % (email, email)
-  file_directive = 'file: *([^ :]+ *: *)?[^ ]+'
-  directive = '(%s|%s|%s)' % (emails, noparent, file_directive)
-  glob = '[a-zA-Z0-9_\\.\\-\\*\\?]+'
-  globs = '(%s( *, *%s)*)' % (glob, glob)
-  perfile = 'per-file +' + globs + ' *= *' + directive
-  include = 'include +([^ :]+ *: *)?[^ ]+'
-  pats = '(|%s|%s|%s|%s|%s)$' % (noparent, email, perfile, include, file_directive)
-  patterns = re.compile(pats)
-  address_pattern = re.compile('([^@ ]+@[^ @]+)')
-  perfile_pattern = re.compile('per-file +.*=(.*)')
-
-  error = 0
-  for fname in args.owners:
-    echo('Checking file: ' + fname)
-    num = 0
-    for line in open(fname, 'r'):
-      num += 1
-      stripped_line = re.sub('#.*$', '', line).strip()
-      if not patterns.match(stripped_line):
-        error += 1
-        print('%s:%d: ERROR: unknown line [%s]' % (fname, num, line.strip()))
-      elif args.check_address:
-        if perfile_pattern.match(stripped_line):
-          for addr in perfile_pattern.match(stripped_line).group(1).split(','):
-            a = addr.strip()
-            if a and a != '*':
-              error += check_address(fname, num, addr.strip())
-        elif address_pattern.match(stripped_line):
-          error += check_address(fname, num, stripped_line)
-  sys.exit(error)
-
-if __name__ == '__main__':
-  main()
diff --git a/tools/finalization/README.md b/tools/finalization/README.md
index 501f260..cc97d1f 100644
--- a/tools/finalization/README.md
+++ b/tools/finalization/README.md
@@ -12,10 +12,8 @@
 
 ## CI:
 Performed in build targets in Finalization branches.
-1. [Finalization Step 1 for Main, git_main-fina-1-release](https://android-build.googleplex.com/builds/branches/git_main-fina-1-release/grid). Test [1st step/Finalize SDK](./finalize-aidl-vndk-sdk-resources.sh).
-2. [Finalization Step 1 for UDC, git_udc-fina-1-release](https://android-build.googleplex.com/builds/branches/git_udc-fina-1-release/grid). Same but for udc-dev.
-3. [Finalization Step 2 for Main, git_main-fina-2-release](https://android-build.googleplex.com/builds/branches/git_main-fina-2-release/grid). Test [1st step/Finalize SDK](./finalize-aidl-vndk-sdk-resources.sh) and [2nd step/Finalize Android](./finalize-sdk-rel.sh). Use [local finalization](./localonly-steps.sh) to build and copy presubmits.
-4. [Finalization Step 2 for UDC, git_udc-fina-2-release](https://android-build.googleplex.com/builds/branches/git_udc-fina-2-release/grid). Same but for udc-dev.
+1. [Finalization Step 1, git_main-fina-1-release](https://android-build.corp.google.com/build_explorer/branch/git_main-fina-1-release). Test [1st step/Finalize SDK](./finalize-aidl-vndk-sdk-resources.sh).
+3. [Finalization Step 2, git_main-fina-2-release](https://android-build.corp.google.com/build_explorer/branch/git_main-fina-2-release). Test [1st step/Finalize SDK](./finalize-aidl-vndk-sdk-resources.sh) and [2nd step/Finalize Android](./finalize-sdk-rel.sh). Use [local finalization](./localonly-steps.sh) to build and copy presubmits.
 5. [Local finalization steps](./localonly-steps.sh) are done only during local testing or in the CI lab. Normally these steps use artifacts from other builds.
 
 ## Utility:
diff --git a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
index 6d13325..37c0011 100755
--- a/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
+++ b/tools/finalization/finalize-aidl-vndk-sdk-resources.sh
@@ -137,6 +137,13 @@
     local version_codes="$top/platform_testing/libraries/compatibility-common-util/src/com/android/compatibility/common/util/VersionCodes.java"
     sed -i -e "/=.*$((${FINAL_PLATFORM_SDK_VERSION}-1));/a \\    ${SDK_VERSION}" $version_codes
 
+    # tools/platform-compat
+    local class2nonsdklist="$top/tools/platform-compat/java/com/android/class2nonsdklist/Class2NonSdkList.java"
+    if ! grep -q "\.*map.put($((${FINAL_PLATFORM_SDK_VERSION}))" $class2nonsdklist ; then
+      local sdk_version="map.put(${FINAL_PLATFORM_SDK_VERSION}, FLAG_UNSUPPORTED);"
+      sed -i -e "/.*map.put($((${FINAL_PLATFORM_SDK_VERSION}-1))/a \\        ${sdk_version}" $class2nonsdklist
+    fi
+
     # Finalize resources
     "$top/frameworks/base/tools/aapt2/tools/finalize_res.py" \
            "$top/frameworks/base/core/res/res/values/public-staging.xml" \
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 5f99f6c..ee266b7 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -168,6 +168,7 @@
         "apexd_host",
         "brillo_update_payload",
         "checkvintf",
+        "generate_gki_certificate",
         "lz4",
         "toybox",
         "unpack_bootimg",
@@ -244,6 +245,7 @@
         "boot_signer",
         "brotli",
         "bsdiff",
+        "generate_gki_certificate",
         "imgdiff",
         "lz4",
         "mkbootfs",
@@ -308,6 +310,7 @@
         "brotli",
         "bsdiff",
         "deapexer",
+        "generate_gki_certificate",
         "imgdiff",
         "lz4",
         "mkbootfs",
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 34b7172..bde152f 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -437,6 +437,8 @@
         sldc_flags = sldc_flags_str.split()
         build_command.append(str(len(sldc_flags)))
         build_command.extend(sldc_flags)
+    f2fs_blocksize = prop_dict.get("f2fs_blocksize", "4096")
+    build_command.extend(["-b", f2fs_blocksize])
   else:
     raise BuildImageError(
         "Error: unknown filesystem type: {}".format(fs_type))
@@ -721,6 +723,7 @@
       "system_f2fs_compress",
       "system_f2fs_sldc_flags",
       "f2fs_sparse_flag",
+      "f2fs_blocksize",
       "skip_fsck",
       "ext_mkuserimg",
       "avb_enable",
@@ -770,6 +773,7 @@
       (True, "{}_extfs_inode_count", "extfs_inode_count"),
       (True, "{}_f2fs_compress", "f2fs_compress"),
       (True, "{}_f2fs_sldc_flags", "f2fs_sldc_flags"),
+      (True, "{}_f2fs_blocksize", "f2fs_block_size"),
       (True, "{}_reserved_size", "partition_reserved_size"),
       (True, "{}_squashfs_block_size", "squashfs_block_size"),
       (True, "{}_squashfs_compressor", "squashfs_compressor"),
@@ -817,7 +821,6 @@
   d["mount_point"] = mount_point
   if mount_point == "system":
     copy_prop("system_headroom", "partition_headroom")
-    copy_prop("system_root_image", "system_root_image")
     copy_prop("root_dir", "root_dir")
     copy_prop("root_fs_config", "root_fs_config")
   elif mount_point == "data":
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index a4c92ae..63fd854 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1156,8 +1156,7 @@
     return self.build_props.get(prop)
 
 
-def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
-                      system_root_image=False):
+def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path):
   class Partition(object):
     def __init__(self, mount_point, fs_type, device, length, context, slotselect):
       self.mount_point = mount_point
@@ -1216,12 +1215,6 @@
                                device=pieces[0], length=length, context=context,
                                slotselect=slotselect)
 
-  # / is used for the system mount point when the root directory is included in
-  # system. Other areas assume system is always at "/system" so point /system
-  # at /.
-  if system_root_image:
-    assert '/system' not in d and '/' in d
-    d["/system"] = d["/"]
   return d
 
 
@@ -1237,22 +1230,19 @@
   # ../RAMDISK/system/etc/recovery.fstab. This function has to handle both
   # cases, since it may load the info_dict from an old build (e.g. when
   # generating incremental OTAs from that build).
-  system_root_image = info_dict.get('system_root_image') == 'true'
   if info_dict.get('no_recovery') != 'true':
     recovery_fstab_path = 'RECOVERY/RAMDISK/system/etc/recovery.fstab'
     if not DoesInputFileContain(input_file, recovery_fstab_path):
       recovery_fstab_path = 'RECOVERY/RAMDISK/etc/recovery.fstab'
     return LoadRecoveryFSTab(
-        read_helper, info_dict['fstab_version'], recovery_fstab_path,
-        system_root_image)
+        read_helper, info_dict['fstab_version'], recovery_fstab_path)
 
   if info_dict.get('recovery_as_boot') == 'true':
     recovery_fstab_path = 'BOOT/RAMDISK/system/etc/recovery.fstab'
     if not DoesInputFileContain(input_file, recovery_fstab_path):
       recovery_fstab_path = 'BOOT/RAMDISK/etc/recovery.fstab'
     return LoadRecoveryFSTab(
-        read_helper, info_dict['fstab_version'], recovery_fstab_path,
-        system_root_image)
+        read_helper, info_dict['fstab_version'], recovery_fstab_path)
 
   return None
 
@@ -1575,6 +1565,50 @@
       pubkey_path=pubkey_path)
 
 
+def _HasGkiCertificationArgs():
+  return ("gki_signing_key_path" in OPTIONS.info_dict and
+          "gki_signing_algorithm" in OPTIONS.info_dict)
+
+
+def _GenerateGkiCertificate(image, image_name):
+  key_path = OPTIONS.info_dict.get("gki_signing_key_path")
+  algorithm = OPTIONS.info_dict.get("gki_signing_algorithm")
+
+  key_path = ResolveAVBSigningPathArgs(key_path)
+
+  # Checks key_path exists, before processing --gki_signing_* args.
+  if not os.path.exists(key_path):
+    raise ExternalError(
+        'gki_signing_key_path: "{}" not found'.format(key_path))
+
+  output_certificate = tempfile.NamedTemporaryFile()
+  cmd = [
+      "generate_gki_certificate",
+      "--name", image_name,
+      "--algorithm", algorithm,
+      "--key", key_path,
+      "--output", output_certificate.name,
+      image,
+  ]
+
+  signature_args = OPTIONS.info_dict.get("gki_signing_signature_args", "")
+  signature_args = signature_args.strip()
+  if signature_args:
+    cmd.extend(["--additional_avb_args", signature_args])
+
+  args = OPTIONS.info_dict.get("avb_boot_add_hash_footer_args", "")
+  args = args.strip()
+  if args:
+    cmd.extend(["--additional_avb_args", args])
+
+  RunAndCheckOutput(cmd)
+
+  output_certificate.seek(os.SEEK_SET, 0)
+  data = output_certificate.read()
+  output_certificate.close()
+  return data
+
+
 def BuildVBMeta(image_path, partitions, name, needed_partitions,
                 resolve_rollback_index_location_conflict=False):
   """Creates a VBMeta image.
@@ -1797,6 +1831,29 @@
 
   RunAndCheckOutput(cmd)
 
+  if _HasGkiCertificationArgs():
+    if not os.path.exists(img.name):
+      raise ValueError("Cannot find GKI boot.img")
+    if kernel_path is None or not os.path.exists(kernel_path):
+      raise ValueError("Cannot find GKI kernel.img")
+
+    # Certify GKI images.
+    boot_signature_bytes = b''
+    boot_signature_bytes += _GenerateGkiCertificate(img.name, "boot")
+    boot_signature_bytes += _GenerateGkiCertificate(
+        kernel_path, "generic_kernel")
+
+    BOOT_SIGNATURE_SIZE = 16 * 1024
+    if len(boot_signature_bytes) > BOOT_SIGNATURE_SIZE:
+      raise ValueError(
+          f"GKI boot_signature size must be <= {BOOT_SIGNATURE_SIZE}")
+    boot_signature_bytes += (
+        b'\0' * (BOOT_SIGNATURE_SIZE - len(boot_signature_bytes)))
+    assert len(boot_signature_bytes) == BOOT_SIGNATURE_SIZE
+
+    with open(img.name, 'ab') as f:
+      f.write(boot_signature_bytes)
+
   # Sign the image if vboot is non-empty.
   if info_dict.get("vboot"):
     path = "/" + partition_name
@@ -1910,10 +1967,8 @@
   if info_dict.get("recovery_as_boot") == "true":
     return True  # the recovery-as-boot boot.img has a RECOVERY ramdisk.
 
-  if info_dict.get("system_root_image") == "true":
-    # The ramdisk content is merged into the system.img, so there is NO
-    # ramdisk in the boot.img or boot-<kernel version>.img.
-    return False
+  if info_dict.get("gki_boot_image_without_ramdisk") == "true":
+    return False  # A GKI boot.img has no ramdisk since Android-13.
 
   if info_dict.get("init_boot") == "true":
     # The ramdisk is moved to the init_boot.img, so there is NO
@@ -3783,14 +3838,11 @@
     output_sink(recovery_img_path, recovery_img.data)
 
   else:
-    system_root_image = info_dict.get("system_root_image") == "true"
     include_recovery_dtbo = info_dict.get("include_recovery_dtbo") == "true"
     include_recovery_acpio = info_dict.get("include_recovery_acpio") == "true"
     path = os.path.join(input_dir, recovery_resource_dat_path)
-    # With system-root-image, boot and recovery images will have mismatching
-    # entries (only recovery has the ramdisk entry) (Bug: 72731506). Use bsdiff
-    # to handle such a case.
-    if system_root_image or include_recovery_dtbo or include_recovery_acpio:
+    # Use bsdiff to handle mismatching entries (Bug: 72731506)
+    if include_recovery_dtbo or include_recovery_acpio:
       diff_program = ["bsdiff"]
       bonus_args = ""
       assert not os.path.exists(path)
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 2fbb3b0..5867c6f 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -83,9 +83,8 @@
 
   --replace_verity_public_key <key>
       Replace the certificate (public key) used for verity verification. The
-      key file replaces the one at BOOT/RAMDISK/verity_key (or ROOT/verity_key
-      for devices using system_root_image). It expects the key filename WITH
-      the extension (e.g. verity_key.pub).
+      key file replaces the one at BOOT/RAMDISK/verity_key. It expects the key
+      filename WITH the extension (e.g. verity_key.pub).
 
   --replace_verity_keyid <path_to_X509_PEM_cert_file>
       Replace the veritykeyid in BOOT/cmdline of input_target_file_zip
@@ -123,6 +122,17 @@
       mounted on the partition (e.g. "--signing_helper /path/to/helper"). The
       args will be appended to the existing ones in info dict.
 
+  --gki_signing_algorithm <algorithm>
+  --gki_signing_key <key>
+      Use the specified algorithm (e.g. SHA256_RSA4096) and the key to generate
+      'boot signature' in a v4 boot.img. Otherwise it uses the existing values
+      in info dict.
+
+  --gki_signing_extra_args <args>
+      Specify any additional args that are needed to generate 'boot signature'
+      (e.g. --prop foo:bar). The args will be appended to the existing ones
+      in info dict.
+
   --android_jar_path <path>
       Path to the android.jar to repack the apex file.
 
@@ -182,6 +192,9 @@
 OPTIONS.avb_keys = {}
 OPTIONS.avb_algorithms = {}
 OPTIONS.avb_extra_args = {}
+OPTIONS.gki_signing_key = None
+OPTIONS.gki_signing_algorithm = None
+OPTIONS.gki_signing_extra_args = None
 OPTIONS.android_jar_path = None
 OPTIONS.vendor_partitions = set()
 OPTIONS.vendor_otatools = None
@@ -538,11 +551,9 @@
         [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
          if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
   except ValueError:
-    # Sets this to zero for targets without APK files.
+    # Sets this to zero for targets without APK files, e.g., gki_arm64.
     maxsize = 0
 
-  system_root_image = misc_info.get("system_root_image") == "true"
-
   for info in input_tf_zip.infolist():
     filename = info.filename
     if filename.startswith("IMAGES/"):
@@ -754,6 +765,9 @@
   if misc_info.get('avb_enable') == 'true':
     RewriteAvbProps(misc_info)
 
+  # Replace the GKI signing key for boot.img, if any.
+  ReplaceGkiSigningKey(misc_info)
+
   # Write back misc_info with the latest values.
   ReplaceMiscInfoTxt(input_tf_zip, output_tf_zip, misc_info)
 
@@ -1035,6 +1049,27 @@
       misc_info[args_key] = result
 
 
+def ReplaceGkiSigningKey(misc_info):
+  """Replaces the GKI signing key."""
+
+  key = OPTIONS.gki_signing_key
+  if not key:
+    return
+
+  algorithm = OPTIONS.gki_signing_algorithm
+  if not algorithm:
+    raise ValueError("Missing --gki_signing_algorithm")
+
+  print('Replacing GKI signing key with "%s" (%s)' % (key, algorithm))
+  misc_info["gki_signing_algorithm"] = algorithm
+  misc_info["gki_signing_key_path"] = key
+
+  extra_args = OPTIONS.gki_signing_extra_args
+  if extra_args:
+    print('Setting GKI signing args: "%s"' % (extra_args))
+    misc_info["gki_signing_signature_args"] = extra_args
+
+
 def BuildKeyMap(misc_info, key_mapping_options):
   for s, d in key_mapping_options:
     if s is None:   # -d option
@@ -1388,6 +1423,12 @@
       # 'oem=--signing_helper_with_files=/tmp/avbsigner.sh'.
       partition, extra_args = a.split("=", 1)
       OPTIONS.avb_extra_args[partition] = extra_args
+    elif o == "--gki_signing_key":
+      OPTIONS.gki_signing_key = a
+    elif o == "--gki_signing_algorithm":
+      OPTIONS.gki_signing_algorithm = a
+    elif o == "--gki_signing_extra_args":
+      OPTIONS.gki_signing_extra_args = a
     elif o == "--vendor_otatools":
       OPTIONS.vendor_otatools = a
     elif o == "--vendor_partitions":
@@ -1451,6 +1492,9 @@
           "avb_extra_custom_image_key=",
           "avb_extra_custom_image_algorithm=",
           "avb_extra_custom_image_extra_args=",
+          "gki_signing_key=",
+          "gki_signing_algorithm=",
+          "gki_signing_extra_args=",
           "vendor_partitions=",
           "vendor_otatools=",
           "allow_gsi_debug_sepolicy",
diff --git a/tools/releasetools/test_build_image.py b/tools/releasetools/test_build_image.py
index cfae7a5..d4f7ccc 100644
--- a/tools/releasetools/test_build_image.py
+++ b/tools/releasetools/test_build_image.py
@@ -99,11 +99,10 @@
     }
     self.assertRaises(BuildImageError, CheckHeadroom, ext4fs_output, prop_dict)
 
-  def test_SetUpInDirAndFsConfig_SystemRootImageTrue_NonSystem(self):
+  def test_SetUpInDirAndFsConfig_NonSystem(self):
     prop_dict = {
         'fs_config': 'fs-config',
         'mount_point': 'vendor',
-        'system_root_image': 'true',
     }
     in_dir, fs_config = SetUpInDirAndFsConfig('/path/to/in_dir', prop_dict)
     self.assertEqual('/path/to/in_dir', in_dir)
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index c61c290..9b2e667 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1348,7 +1348,6 @@
   INFO_DICT_DEFAULT = {
       'recovery_api_version': 3,
       'fstab_version': 2,
-      'system_root_image': 'true',
       'no_recovery': 'true',
       'recovery_as_boot': 'true',
   }
@@ -1377,14 +1376,8 @@
       info_values = ''.join(
           ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
       common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
-
-      FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
-      if info_dict.get('system_root_image') == 'true':
-        fstab_values = FSTAB_TEMPLATE.format('/')
-      else:
-        fstab_values = FSTAB_TEMPLATE.format('/system')
-      common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
-
+      common.ZipWriteStr(target_files_zip, fstab_path,
+                         "/dev/block/system /system ext4 ro,barrier=1 defaults")
       common.ZipWriteStr(
           target_files_zip, 'META/file_contexts', 'file-contexts')
     return target_files
@@ -1397,7 +1390,6 @@
       loaded_dict = common.LoadInfoDict(target_files_zip)
       self.assertEqual(3, loaded_dict['recovery_api_version'])
       self.assertEqual(2, loaded_dict['fstab_version'])
-      self.assertIn('/', loaded_dict['fstab'])
       self.assertIn('/system', loaded_dict['fstab'])
 
   def test_LoadInfoDict_legacyRecoveryFstabPath(self):
@@ -1408,7 +1400,6 @@
       loaded_dict = common.LoadInfoDict(target_files_zip)
       self.assertEqual(3, loaded_dict['recovery_api_version'])
       self.assertEqual(2, loaded_dict['fstab_version'])
-      self.assertIn('/', loaded_dict['fstab'])
       self.assertIn('/system', loaded_dict['fstab'])
 
   @test_utils.SkipIfExternalToolsUnavailable()
@@ -1420,7 +1411,6 @@
     loaded_dict = common.LoadInfoDict(unzipped)
     self.assertEqual(3, loaded_dict['recovery_api_version'])
     self.assertEqual(2, loaded_dict['fstab_version'])
-    self.assertIn('/', loaded_dict['fstab'])
     self.assertIn('/system', loaded_dict['fstab'])
 
   @test_utils.SkipIfExternalToolsUnavailable()
@@ -1432,15 +1422,11 @@
     loaded_dict = common.LoadInfoDict(unzipped)
     self.assertEqual(3, loaded_dict['recovery_api_version'])
     self.assertEqual(2, loaded_dict['fstab_version'])
-    self.assertIn('/', loaded_dict['fstab'])
     self.assertIn('/system', loaded_dict['fstab'])
 
-  def test_LoadInfoDict_systemRootImageFalse(self):
-    # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
-    # launched prior to P will likely have this config.
+  def test_LoadInfoDict_recoveryAsBootFalse(self):
     info_dict = copy.copy(self.INFO_DICT_DEFAULT)
     del info_dict['no_recovery']
-    del info_dict['system_root_image']
     del info_dict['recovery_as_boot']
     target_files = self._test_LoadInfoDict_createTargetFiles(
         info_dict,
@@ -1452,22 +1438,6 @@
       self.assertNotIn('/', loaded_dict['fstab'])
       self.assertIn('/system', loaded_dict['fstab'])
 
-  def test_LoadInfoDict_recoveryAsBootFalse(self):
-    # Devices using system-as-root, but with standalone recovery image. Non-A/B
-    # devices launched since P will likely have this config.
-    info_dict = copy.copy(self.INFO_DICT_DEFAULT)
-    del info_dict['no_recovery']
-    del info_dict['recovery_as_boot']
-    target_files = self._test_LoadInfoDict_createTargetFiles(
-        info_dict,
-        'RECOVERY/RAMDISK/system/etc/recovery.fstab')
-    with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
-      loaded_dict = common.LoadInfoDict(target_files_zip)
-      self.assertEqual(3, loaded_dict['recovery_api_version'])
-      self.assertEqual(2, loaded_dict['fstab_version'])
-      self.assertIn('/', loaded_dict['fstab'])
-      self.assertIn('/system', loaded_dict['fstab'])
-
   def test_LoadInfoDict_noRecoveryTrue(self):
     # Device doesn't have a recovery partition at all.
     info_dict = copy.copy(self.INFO_DICT_DEFAULT)
@@ -1499,7 +1469,6 @@
     loaded_dict = common.LoadInfoDict(unzipped, True)
     self.assertEqual(3, loaded_dict['recovery_api_version'])
     self.assertEqual(2, loaded_dict['fstab_version'])
-    self.assertIn('/', loaded_dict['fstab'])
     self.assertIn('/system', loaded_dict['fstab'])
     self.assertEqual(
         os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
@@ -1636,6 +1605,40 @@
     self.assertEqual(3, chained_partition_args.rollback_index_location)
     self.assertTrue(os.path.exists(chained_partition_args.pubkey_path))
 
+  def test_GenerateGkiCertificate_KeyPathNotFound(self):
+    pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
+    self.assertFalse(os.path.exists(pubkey))
+
+    common.OPTIONS.info_dict = {
+        'gki_signing_key_path': pubkey,
+        'gki_signing_algorithm': 'SHA256_RSA4096',
+        'gki_signing_signature_args': '--prop foo:bar',
+    }
+    common.OPTIONS.search_path = None
+    test_file = tempfile.NamedTemporaryFile()
+    self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
+                      test_file.name, 'generic_kernel')
+
+  def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
+    pubkey = 'no_testkey_gki.pem'
+    self.assertFalse(os.path.exists(pubkey))
+
+    # Tests it should raise ExternalError if no key found under
+    # OPTIONS.search_path.
+    search_path_dir = common.MakeTempDir()
+    search_pubkey = os.path.join(search_path_dir, pubkey)
+    self.assertFalse(os.path.exists(search_pubkey))
+
+    common.OPTIONS.search_path = search_path_dir
+    common.OPTIONS.info_dict = {
+        'gki_signing_key_path': pubkey,
+        'gki_signing_algorithm': 'SHA256_RSA4096',
+        'gki_signing_signature_args': '--prop foo:bar',
+    }
+    test_file = tempfile.NamedTemporaryFile()
+    self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
+                      test_file.name, 'generic_kernel')
+
 
 class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
   """Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 9cc6df4..0cd7dac 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -23,7 +23,8 @@
 import test_utils
 from sign_target_files_apks import (
     CheckApkAndApexKeysAvailable, EditTags, GetApkFileInfo, ReadApexKeysInfo,
-    ReplaceCerts, RewriteAvbProps, RewriteProps, WriteOtacerts)
+    ReplaceCerts, ReplaceGkiSigningKey, RewriteAvbProps, RewriteProps,
+    WriteOtacerts)
 
 
 class SignTargetFilesApksTest(test_utils.ReleaseToolsTestCase):
@@ -535,3 +536,52 @@
             'system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.pem',
             'build/make/target/product/security/testkey', None),
         }, keys_info)
+
+  def test_ReplaceGkiSigningKey(self):
+    common.OPTIONS.gki_signing_key = 'release_gki_key'
+    common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
+    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
+
+    misc_info = {
+        'gki_signing_key_path': 'default_gki_key',
+        'gki_signing_algorithm': 'default_gki_algorithm',
+        'gki_signing_signature_args': 'default_gki_signature_args',
+    }
+    expected_dict = {
+        'gki_signing_key_path': 'release_gki_key',
+        'gki_signing_algorithm': 'release_gki_algorithm',
+        'gki_signing_signature_args': 'release_gki_signature_extra_args',
+    }
+    ReplaceGkiSigningKey(misc_info)
+    self.assertDictEqual(expected_dict, misc_info)
+
+  def test_ReplaceGkiSigningKey_MissingSigningAlgorithm(self):
+    common.OPTIONS.gki_signing_key = 'release_gki_key'
+    common.OPTIONS.gki_signing_algorithm = None
+    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
+
+    misc_info = {
+        'gki_signing_key_path': 'default_gki_key',
+        'gki_signing_algorithm': 'default_gki_algorithm',
+        'gki_signing_signature_args': 'default_gki_signature_args',
+    }
+    self.assertRaises(ValueError, ReplaceGkiSigningKey, misc_info)
+
+  def test_ReplaceGkiSigningKey_MissingSigningKeyNop(self):
+    common.OPTIONS.gki_signing_key = None
+    common.OPTIONS.gki_signing_algorithm = 'release_gki_algorithm'
+    common.OPTIONS.gki_signing_extra_args = 'release_gki_signature_extra_args'
+
+    # No change to misc_info if common.OPTIONS.gki_signing_key is missing.
+    misc_info = {
+        'gki_signing_key_path': 'default_gki_key',
+        'gki_signing_algorithm': 'default_gki_algorithm',
+        'gki_signing_signature_args': 'default_gki_signature_args',
+    }
+    expected_dict = {
+        'gki_signing_key_path': 'default_gki_key',
+        'gki_signing_algorithm': 'default_gki_algorithm',
+        'gki_signing_signature_args': 'default_gki_signature_args',
+    }
+    ReplaceGkiSigningKey(misc_info)
+    self.assertDictEqual(expected_dict, misc_info)
diff --git a/tools/releasetools/test_validate_target_files.py b/tools/releasetools/test_validate_target_files.py
index 48b563d..4d4b9e5 100644
--- a/tools/releasetools/test_validate_target_files.py
+++ b/tools/releasetools/test_validate_target_files.py
@@ -156,7 +156,6 @@
         verity_key_mincrypt)
 
     info_dict = {
-        'system_root_image' : 'true',
         'verity' : 'true',
     }
     options = {
diff --git a/tools/releasetools/validate_target_files.py b/tools/releasetools/validate_target_files.py
index 84a2f7e..8da4fa2 100755
--- a/tools/releasetools/validate_target_files.py
+++ b/tools/releasetools/validate_target_files.py
@@ -132,7 +132,7 @@
     return
 
   # Verify IMAGES/system.img if applicable.
-  # Some targets are system.img-less.
+  # Some targets, e.g., gki_arm64, gki_x86_64, etc., are system.img-less.
   if 'IMAGES/system.img' in input_zip.namelist():
     CheckAllFiles('system')
 
@@ -361,18 +361,15 @@
           "Mismatching mincrypt verity key files"
       logging.info('Verified the content of /verity_key')
 
-    # For devices with a separate ramdisk (i.e. non-system-as-root), there must
-    # be a copy in ramdisk.
-    if info_dict.get("system_root_image") != "true":
-      verity_key_ramdisk = os.path.join(
-          input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
-      assert os.path.exists(
-          verity_key_ramdisk), 'Missing verity_key in ramdisk'
+    verity_key_ramdisk = os.path.join(
+        input_tmp, 'BOOT', 'RAMDISK', 'verity_key')
+    assert os.path.exists(
+        verity_key_ramdisk), 'Missing verity_key in ramdisk'
 
-      assert filecmp.cmp(
-          verity_key_mincrypt, verity_key_ramdisk, shallow=False), \
-          'Mismatching verity_key files in root and ramdisk'
-      logging.info('Verified the content of /verity_key in ramdisk')
+    assert filecmp.cmp(
+        verity_key_mincrypt, verity_key_ramdisk, shallow=False), \
+        'Mismatching verity_key files in root and ramdisk'
+    logging.info('Verified the content of /verity_key in ramdisk')
 
     # Then verify the verity signed system/vendor/product images, against the
     # verity pubkey in mincrypt format.