Merge "Allow importing starlark code in makefiles"
diff --git a/core/Makefile b/core/Makefile
index 3da1894..622174f 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -5589,6 +5589,8 @@
 .PHONY: fastboot_info
 fastboot_info: $(INSTALLED_FASTBOOT_INFO_TARGET)
 
+droidcore-unbundled: $(INSTALLED_FASTBOOT_INFO_TARGET)
+
 $(call declare-0p-target,$(INSTALLED_MISC_INFO_TARGET))
 
 .PHONY: misc_info
@@ -5910,6 +5912,7 @@
 	    $(LPMAKE) \
 	    $(SELINUX_FC) \
 	    $(INSTALLED_MISC_INFO_TARGET) \
+	    $(INSTALLED_FASTBOOT_INFO_TARGET) \
 	    $(APKCERTS_FILE) \
 	    $(SOONG_APEX_KEYS_FILE) \
 	    $(SOONG_ZIP) \
@@ -6128,6 +6131,9 @@
 	$(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
 	$(hide) cp $(SELINUX_FC) $(zip_root)/META/file_contexts.bin
 	$(hide) cp $(INSTALLED_MISC_INFO_TARGET) $(zip_root)/META/misc_info.txt
+ifneq ($(INSTALLED_FASTBOOT_INFO_TARGET),)
+	$(hide) cp $(INSTALLED_FASTBOOT_INFO_TARGET) $(zip_root)/META/fastboot-info.txt
+endif
 ifneq ($(PRODUCT_SYSTEM_BASE_FS_PATH),)
 	$(hide) cp $(PRODUCT_SYSTEM_BASE_FS_PATH) \
 	  $(zip_root)/META/$(notdir $(PRODUCT_SYSTEM_BASE_FS_PATH))
@@ -6396,11 +6402,10 @@
 PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$(dir $(ZIP2ZIP)):$$PATH \
     $(OTA_FROM_TARGET_FILES) \
         --verbose \
-        --extracted_input_target_files $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) \
         --path $(HOST_OUT) \
         $(if $(OEM_OTA_CONFIG), --oem_settings $(OEM_OTA_CONFIG)) \
         $(2) \
-        $(BUILT_TARGET_FILES_PACKAGE) $(1)
+        $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE)) $(1)
 endef
 
 product_name := $(TARGET_PRODUCT)
@@ -6416,7 +6421,7 @@
 
 $(INTERNAL_OTA_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
 $(INTERNAL_OTA_PACKAGE_TARGET): .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_OTA_METADATA)
-$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
+$(INTERNAL_OTA_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
 	@echo "Package OTA: $@"
 	$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --output_metadata_path $(INTERNAL_OTA_METADATA))
 
@@ -6452,7 +6457,7 @@
 
 INTERNAL_OTA_PARTIAL_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 $(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): KEY_CERT_PAIR := $(DEFAULT_KEY_CERT_PAIR)
-$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
+$(INTERNAL_OTA_PARTIAL_PACKAGE_TARGET): $(BUILT_TARGET_FILES_DIR) $(OTA_FROM_TARGET_FILES) $(INTERNAL_OTATOOLS_FILES)
 	@echo "Package partial OTA: $@"
 	$(call build-ota-package-target,$@,-k $(KEY_CERT_PAIR) --partial "$(BOARD_PARTIAL_OTA_UPDATE_PARTITIONS_LIST)")
 
@@ -6775,7 +6780,7 @@
 # For real devices and for dist builds, build super image from target files to an intermediate directory.
 INTERNAL_SUPERIMAGE_DIST_TARGET := $(call intermediates-dir-for,PACKAGING,super.img)/super.img
 $(INTERNAL_SUPERIMAGE_DIST_TARGET): extracted_input_target_files := $(patsubst %.zip,%,$(BUILT_TARGET_FILES_PACKAGE))
-$(INTERNAL_SUPERIMAGE_DIST_TARGET): $(LPMAKE) $(BUILT_TARGET_FILES_PACKAGE) $(BUILD_SUPER_IMAGE)
+$(INTERNAL_SUPERIMAGE_DIST_TARGET): $(LPMAKE) $(BUILT_TARGET_FILES_DIR) $(BUILD_SUPER_IMAGE)
 	$(call pretty,"Target super fs image from target files: $@")
 	PATH=$(dir $(LPMAKE)):$$PATH \
 	    $(BUILD_SUPER_IMAGE) -v $(extracted_input_target_files) $@
diff --git a/core/config.mk b/core/config.mk
index 4300800..396aad0 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -546,8 +546,10 @@
 
 TARGET_BUILD_USE_PREBUILT_SDKS :=
 DISABLE_PREOPT :=
+DISABLE_PREOPT_BOOT_IMAGES :=
 ifneq (,$(TARGET_BUILD_APPS)$(TARGET_BUILD_UNBUNDLED_IMAGE))
   DISABLE_PREOPT := true
+  DISABLE_PREOPT_BOOT_IMAGES := true
 endif
 ifeq (true,$(TARGET_BUILD_UNBUNDLED))
   ifneq (true,$(UNBUNDLED_BUILD_SDKS_FROM_SOURCE))
@@ -558,6 +560,7 @@
 .KATI_READONLY := \
   TARGET_BUILD_USE_PREBUILT_SDKS \
   DISABLE_PREOPT \
+  DISABLE_PREOPT_BOOT_IMAGES \
 
 prebuilt_sdk_tools := prebuilts/sdk/tools
 prebuilt_sdk_tools_bin := $(prebuilt_sdk_tools)/$(HOST_OS)/bin
diff --git a/core/dex_preopt_config.mk b/core/dex_preopt_config.mk
index e36e2eb..7b9c4db 100644
--- a/core/dex_preopt_config.mk
+++ b/core/dex_preopt_config.mk
@@ -12,9 +12,15 @@
   # would result in passing bad arguments to dex2oat and failing the build.
   ENABLE_PREOPT :=
   ENABLE_PREOPT_BOOT_IMAGES :=
-else ifeq (true,$(DISABLE_PREOPT))
-  # Disable dexpreopt for libraries/apps, but do compile boot images.
-  ENABLE_PREOPT :=
+else
+  ifeq (true,$(DISABLE_PREOPT))
+    # Disable dexpreopt for libraries/apps, but may compile boot images.
+    ENABLE_PREOPT :=
+  endif
+  ifeq (true,$(DISABLE_PREOPT_BOOT_IMAGES))
+    # Disable dexpreopt for boot images, but may compile libraries/apps.
+    ENABLE_PREOPT_BOOT_IMAGES :=
+  endif
 endif
 
 # The default value for LOCAL_DEX_PREOPT
diff --git a/tools/aconfig/Android.bp b/tools/aconfig/Android.bp
index e762f33..9617e0e 100644
--- a/tools/aconfig/Android.bp
+++ b/tools/aconfig/Android.bp
@@ -23,6 +23,7 @@
         "libprotobuf",
         "libserde",
         "libserde_json",
+        "libtinytemplate",
     ],
 }
 
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index b439858..8517dd2 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -14,6 +14,7 @@
 protobuf = "3.2.0"
 serde = { version = "1.0.152", features = ["derive"] }
 serde_json = "1.0.93"
+tinytemplate = "1.2.1"
 
 [build-dependencies]
 protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 6eac414..a3b1fec 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -20,38 +20,66 @@
 
 package android.aconfig;
 
+// messages used in both aconfig input and output
+
 enum flag_state {
   ENABLED = 1;
   DISABLED = 2;
 }
 
-enum permission {
+enum flag_permission {
   READ_ONLY = 1;
   READ_WRITE = 2;
 }
 
-message value {
+// aconfig input messages: configuration and override data
+
+message flag_value {
   required flag_state state = 1;
-  required permission permission = 2;
+  required flag_permission permission = 2;
   optional uint32 since = 3;
 }
 
-message flag {
-  required string id = 1;
+message flag_definition {
+  required string name = 1;
   required string description = 2;
-  repeated value value = 3;
+  repeated flag_value value = 3;
 };
 
-message android_config {
-  repeated flag flag = 1;
+message namespace {
+  required string namespace = 1;
+  repeated flag_definition flag = 2;
 };
 
-message override {
-  required string id = 1;
+message flag_override {
+  required string namespace = 1;
+  required string name = 2;
+  required flag_state state = 3;
+  required flag_permission permission = 4;
+};
+
+message flag_overrides {
+  repeated flag_override flag_override = 1;
+};
+
+// aconfig output messages: parsed and verified configuration and override data
+
+message tracepoint {
+  // path to config or override file releative to $TOP
+  required string source = 1;
   required flag_state state = 2;
-  required permission permission = 3;
-};
+  required flag_permission permission = 3;
+}
 
-message override_config {
-  repeated override override = 1;
-};
+message parsed_flag {
+  required string namespace = 1;
+  required string name = 2;
+  required string description = 3;
+  required flag_state state = 4;
+  required flag_permission permission = 5;
+  repeated tracepoint trace = 6;
+}
+
+message parsed_flags {
+  repeated parsed_flag parsed_flag = 1;
+}
diff --git a/tools/aconfig/src/aconfig.rs b/tools/aconfig/src/aconfig.rs
index f10ca1f..8fe82b6 100644
--- a/tools/aconfig/src/aconfig.rs
+++ b/tools/aconfig/src/aconfig.rs
@@ -18,9 +18,11 @@
 use protobuf::{Enum, EnumOrUnknown};
 use serde::{Deserialize, Serialize};
 
+use crate::cache::{Cache, Item, Tracepoint};
 use crate::protos::{
-    ProtoAndroidConfig, ProtoFlag, ProtoFlagState, ProtoOverride, ProtoOverrideConfig,
-    ProtoPermission, ProtoValue,
+    ProtoFlagDefinition, ProtoFlagDefinitionValue, ProtoFlagOverride, ProtoFlagOverrides,
+    ProtoFlagPermission, ProtoFlagState, ProtoNamespace, ProtoParsedFlag, ProtoParsedFlags,
+    ProtoTracepoint,
 };
 
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
@@ -41,24 +43,42 @@
     }
 }
 
+impl From<FlagState> for ProtoFlagState {
+    fn from(state: FlagState) -> Self {
+        match state {
+            FlagState::Enabled => ProtoFlagState::ENABLED,
+            FlagState::Disabled => ProtoFlagState::DISABLED,
+        }
+    }
+}
+
 #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone, Copy)]
 pub enum Permission {
     ReadOnly,
     ReadWrite,
 }
 
-impl TryFrom<EnumOrUnknown<ProtoPermission>> for Permission {
+impl TryFrom<EnumOrUnknown<ProtoFlagPermission>> for Permission {
     type Error = Error;
 
-    fn try_from(proto: EnumOrUnknown<ProtoPermission>) -> Result<Self, Self::Error> {
-        match ProtoPermission::from_i32(proto.value()) {
-            Some(ProtoPermission::READ_ONLY) => Ok(Permission::ReadOnly),
-            Some(ProtoPermission::READ_WRITE) => Ok(Permission::ReadWrite),
+    fn try_from(proto: EnumOrUnknown<ProtoFlagPermission>) -> Result<Self, Self::Error> {
+        match ProtoFlagPermission::from_i32(proto.value()) {
+            Some(ProtoFlagPermission::READ_ONLY) => Ok(Permission::ReadOnly),
+            Some(ProtoFlagPermission::READ_WRITE) => Ok(Permission::ReadWrite),
             None => Err(anyhow!("unknown permission enum value {}", proto.value())),
         }
     }
 }
 
+impl From<Permission> for ProtoFlagPermission {
+    fn from(permission: Permission) -> Self {
+        match permission {
+            Permission::ReadOnly => ProtoFlagPermission::READ_ONLY,
+            Permission::ReadWrite => ProtoFlagPermission::READ_WRITE,
+        }
+    }
+}
+
 #[derive(Debug, PartialEq, Eq)]
 pub struct Value {
     state: FlagState,
@@ -77,10 +97,10 @@
     }
 }
 
-impl TryFrom<ProtoValue> for Value {
+impl TryFrom<ProtoFlagDefinitionValue> for Value {
     type Error = Error;
 
-    fn try_from(proto: ProtoValue) -> Result<Self, Self::Error> {
+    fn try_from(proto: ProtoFlagDefinitionValue) -> Result<Self, Self::Error> {
         let Some(proto_state) = proto.state else {
             return Err(anyhow!("missing 'state' field"));
         };
@@ -95,7 +115,7 @@
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct Flag {
-    pub id: String,
+    pub name: String,
     pub description: String,
 
     // ordered by Value.since; guaranteed to contain at least one item (the default value, with
@@ -106,17 +126,11 @@
 impl Flag {
     #[allow(dead_code)] // only used in unit tests
     pub fn try_from_text_proto(text_proto: &str) -> Result<Flag> {
-        let proto: ProtoFlag = crate::protos::try_from_text_proto(text_proto)
+        let proto: ProtoFlagDefinition = crate::protos::try_from_text_proto(text_proto)
             .with_context(|| text_proto.to_owned())?;
         proto.try_into()
     }
 
-    pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Flag>> {
-        let proto: ProtoAndroidConfig = crate::protos::try_from_text_proto(text_proto)
-            .with_context(|| text_proto.to_owned())?;
-        proto.flag.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
-    }
-
     pub fn resolve(&self, build_id: u32) -> (FlagState, Permission) {
         let mut state = self.values[0].state;
         let mut permission = self.values[0].permission;
@@ -131,12 +145,12 @@
     }
 }
 
-impl TryFrom<ProtoFlag> for Flag {
+impl TryFrom<ProtoFlagDefinition> for Flag {
     type Error = Error;
 
-    fn try_from(proto: ProtoFlag) -> Result<Self, Self::Error> {
-        let Some(id) = proto.id else {
-            return Err(anyhow!("missing 'id' field"));
+    fn try_from(proto: ProtoFlagDefinition) -> Result<Self, Self::Error> {
+        let Some(name) = proto.name else {
+            return Err(anyhow!("missing 'name' field"));
         };
         let Some(description) = proto.description else {
             return Err(anyhow!("missing 'description' field"));
@@ -150,8 +164,8 @@
             let v: Value = proto_value.try_into()?;
             if values.iter().any(|w| v.since == w.since) {
                 let msg = match v.since {
-                    None => format!("flag {}: multiple default values", id),
-                    Some(x) => format!("flag {}: multiple values for since={}", id, x),
+                    None => format!("flag {}: multiple default values", name),
+                    Some(x) => format!("flag {}: multiple values for since={}", name, x),
                 };
                 return Err(anyhow!(msg));
             }
@@ -159,13 +173,35 @@
         }
         values.sort_by_key(|v| v.since);
 
-        Ok(Flag { id, description, values })
+        Ok(Flag { name, description, values })
+    }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct Namespace {
+    pub namespace: String,
+    pub flags: Vec<Flag>,
+}
+
+impl Namespace {
+    pub fn try_from_text_proto(text_proto: &str) -> Result<Namespace> {
+        let proto: ProtoNamespace = crate::protos::try_from_text_proto(text_proto)
+            .with_context(|| text_proto.to_owned())?;
+        let Some(namespace) = proto.namespace else {
+            return Err(anyhow!("missing 'namespace' field"));
+        };
+        let mut flags = vec![];
+        for proto_flag in proto.flag.into_iter() {
+            flags.push(proto_flag.try_into()?);
+        }
+        Ok(Namespace { namespace, flags })
     }
 }
 
 #[derive(Debug, PartialEq, Eq)]
 pub struct Override {
-    pub id: String,
+    pub namespace: String,
+    pub name: String,
     pub state: FlagState,
     pub permission: Permission,
 }
@@ -173,22 +209,25 @@
 impl Override {
     #[allow(dead_code)] // only used in unit tests
     pub fn try_from_text_proto(text_proto: &str) -> Result<Override> {
-        let proto: ProtoOverride = crate::protos::try_from_text_proto(text_proto)?;
+        let proto: ProtoFlagOverride = crate::protos::try_from_text_proto(text_proto)?;
         proto.try_into()
     }
 
     pub fn try_from_text_proto_list(text_proto: &str) -> Result<Vec<Override>> {
-        let proto: ProtoOverrideConfig = crate::protos::try_from_text_proto(text_proto)?;
-        proto.override_.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
+        let proto: ProtoFlagOverrides = crate::protos::try_from_text_proto(text_proto)?;
+        proto.flag_override.into_iter().map(|proto_flag| proto_flag.try_into()).collect()
     }
 }
 
-impl TryFrom<ProtoOverride> for Override {
+impl TryFrom<ProtoFlagOverride> for Override {
     type Error = Error;
 
-    fn try_from(proto: ProtoOverride) -> Result<Self, Self::Error> {
-        let Some(id) = proto.id else {
-            return Err(anyhow!("missing 'id' field"));
+    fn try_from(proto: ProtoFlagOverride) -> Result<Self, Self::Error> {
+        let Some(namespace) = proto.namespace else {
+            return Err(anyhow!("missing 'namespace' field"));
+        };
+        let Some(name) = proto.name else {
+            return Err(anyhow!("missing 'name' field"));
         };
         let Some(proto_state) = proto.state else {
             return Err(anyhow!("missing 'state' field"));
@@ -198,7 +237,42 @@
             return Err(anyhow!("missing 'permission' field"));
         };
         let permission = proto_permission.try_into()?;
-        Ok(Override { id, state, permission })
+        Ok(Override { namespace, name, state, permission })
+    }
+}
+
+impl From<Cache> for ProtoParsedFlags {
+    fn from(cache: Cache) -> Self {
+        let mut proto = ProtoParsedFlags::new();
+        for item in cache.into_iter() {
+            proto.parsed_flag.push(item.into());
+        }
+        proto
+    }
+}
+
+impl From<Item> for ProtoParsedFlag {
+    fn from(item: Item) -> Self {
+        let mut proto = crate::protos::ProtoParsedFlag::new();
+        proto.set_namespace(item.namespace.to_owned());
+        proto.set_name(item.name.clone());
+        proto.set_description(item.description.clone());
+        proto.set_state(item.state.into());
+        proto.set_permission(item.permission.into());
+        for trace in item.trace.into_iter() {
+            proto.trace.push(trace.into());
+        }
+        proto
+    }
+}
+
+impl From<Tracepoint> for ProtoTracepoint {
+    fn from(tracepoint: Tracepoint) -> Self {
+        let mut proto = ProtoTracepoint::new();
+        proto.set_source(format!("{}", tracepoint.source));
+        proto.set_state(tracepoint.state.into());
+        proto.set_permission(tracepoint.permission.into());
+        proto
     }
 }
 
@@ -209,7 +283,7 @@
     #[test]
     fn test_flag_try_from_text_proto() {
         let expected = Flag {
-            id: "1234".to_owned(),
+            name: "1234".to_owned(),
             description: "Description of the flag".to_owned(),
             values: vec![
                 Value::default(FlagState::Disabled, Permission::ReadOnly),
@@ -218,7 +292,7 @@
         };
 
         let s = r#"
-        id: "1234"
+        name: "1234"
         description: "Description of the flag"
         value {
             state: DISABLED
@@ -238,7 +312,7 @@
     #[test]
     fn test_flag_try_from_text_proto_bad_input() {
         let s = r#"
-        id: "a"
+        name: "a"
         description: "Description of the flag"
         "#;
         let error = Flag::try_from_text_proto(s).unwrap_err();
@@ -255,7 +329,7 @@
         assert!(format!("{:?}", error).contains("Message not initialized"));
 
         let s = r#"
-        id: "a"
+        name: "a"
         description: "Description of the flag"
         value {
             state: ENABLED
@@ -271,23 +345,27 @@
     }
 
     #[test]
-    fn test_flag_try_from_text_proto_list() {
-        let expected = vec![
-            Flag {
-                id: "a".to_owned(),
-                description: "A".to_owned(),
-                values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
-            },
-            Flag {
-                id: "b".to_owned(),
-                description: "B".to_owned(),
-                values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
-            },
-        ];
+    fn test_namespace_try_from_text_proto() {
+        let expected = Namespace {
+            namespace: "ns".to_owned(),
+            flags: vec![
+                Flag {
+                    name: "a".to_owned(),
+                    description: "A".to_owned(),
+                    values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
+                },
+                Flag {
+                    name: "b".to_owned(),
+                    description: "B".to_owned(),
+                    values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
+                },
+            ],
+        };
 
         let s = r#"
+        namespace: "ns"
         flag {
-            id: "a"
+            name: "a"
             description: "A"
             value {
                 state: ENABLED
@@ -295,7 +373,7 @@
             }
         }
         flag {
-            id: "b"
+            name: "b"
             description: "B"
             value {
                 state: DISABLED
@@ -303,7 +381,7 @@
             }
         }
         "#;
-        let actual = Flag::try_from_text_proto_list(s).unwrap();
+        let actual = Namespace::try_from_text_proto(s).unwrap();
 
         assert_eq!(expected, actual);
     }
@@ -311,13 +389,15 @@
     #[test]
     fn test_override_try_from_text_proto_list() {
         let expected = Override {
-            id: "1234".to_owned(),
+            namespace: "ns".to_owned(),
+            name: "1234".to_owned(),
             state: FlagState::Enabled,
             permission: Permission::ReadOnly,
         };
 
         let s = r#"
-        id: "1234"
+        namespace: "ns"
+        name: "1234"
         state: ENABLED
         permission: READ_ONLY
         "#;
@@ -329,7 +409,7 @@
     #[test]
     fn test_flag_resolve() {
         let flag = Flag {
-            id: "a".to_owned(),
+            name: "a".to_owned(),
             description: "A".to_owned(),
             values: vec![
                 Value::default(FlagState::Disabled, Permission::ReadOnly),
diff --git a/tools/aconfig/src/cache.rs b/tools/aconfig/src/cache.rs
index 94443d7..4b46c42 100644
--- a/tools/aconfig/src/cache.rs
+++ b/tools/aconfig/src/cache.rs
@@ -22,7 +22,7 @@
 use crate::commands::Source;
 
 #[derive(Serialize, Deserialize, Debug)]
-pub struct TracePoint {
+pub struct Tracepoint {
     pub source: Source,
     pub state: FlagState,
     pub permission: Permission,
@@ -30,22 +30,28 @@
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct Item {
-    pub id: String,
+    // TODO: duplicating the Cache.namespace as Item.namespace makes the internal representation
+    // closer to the proto message `parsed_flag`; hopefully this will enable us to replace the Item
+    // struct and use a newtype instead once aconfig has matured. Until then, namespace should
+    // really be a Cow<String>.
+    pub namespace: String,
+    pub name: String,
     pub description: String,
     pub state: FlagState,
     pub permission: Permission,
-    pub trace: Vec<TracePoint>,
+    pub trace: Vec<Tracepoint>,
 }
 
 #[derive(Serialize, Deserialize, Debug)]
 pub struct Cache {
     build_id: u32,
+    namespace: String,
     items: Vec<Item>,
 }
 
 impl Cache {
-    pub fn new(build_id: u32) -> Cache {
-        Cache { build_id, items: vec![] }
+    pub fn new(build_id: u32, namespace: String) -> Cache {
+        Cache { build_id, namespace, items: vec![] }
     }
 
     pub fn read_from_reader(reader: impl Read) -> Result<Cache> {
@@ -57,31 +63,36 @@
     }
 
     pub fn add_flag(&mut self, source: Source, flag: Flag) -> Result<()> {
-        if self.items.iter().any(|item| item.id == flag.id) {
+        if self.items.iter().any(|item| item.name == flag.name) {
             return Err(anyhow!(
                 "failed to add flag {} from {}: flag already defined",
-                flag.id,
+                flag.name,
                 source,
             ));
         }
         let (state, permission) = flag.resolve(self.build_id);
         self.items.push(Item {
-            id: flag.id.clone(),
+            namespace: self.namespace.clone(),
+            name: flag.name.clone(),
             description: flag.description,
             state,
             permission,
-            trace: vec![TracePoint { source, state, permission }],
+            trace: vec![Tracepoint { source, state, permission }],
         });
         Ok(())
     }
 
     pub fn add_override(&mut self, source: Source, override_: Override) -> Result<()> {
-        let Some(existing_item) = self.items.iter_mut().find(|item| item.id == override_.id) else {
-            return Err(anyhow!("failed to override flag {}: unknown flag", override_.id));
+        if override_.namespace != self.namespace {
+            // TODO: print warning?
+            return Ok(());
+        }
+        let Some(existing_item) = self.items.iter_mut().find(|item| item.name == override_.name) else {
+            return Err(anyhow!("failed to override flag {}: unknown flag", override_.name));
         };
         existing_item.state = override_.state;
         existing_item.permission = override_.permission;
-        existing_item.trace.push(TracePoint {
+        existing_item.trace.push(Tracepoint {
             source,
             state: override_.state,
             permission: override_.permission,
@@ -92,9 +103,11 @@
     pub fn iter(&self) -> impl Iterator<Item = &Item> {
         self.items.iter()
     }
-}
 
-impl Item {}
+    pub fn into_iter(self) -> impl Iterator<Item = Item> {
+        self.items.into_iter()
+    }
+}
 
 #[cfg(test)]
 mod tests {
@@ -103,12 +116,12 @@
 
     #[test]
     fn test_add_flag() {
-        let mut cache = Cache::new(1);
+        let mut cache = Cache::new(1, "ns".to_string());
         cache
             .add_flag(
                 Source::File("first.txt".to_string()),
                 Flag {
-                    id: "foo".to_string(),
+                    name: "foo".to_string(),
                     description: "desc".to_string(),
                     values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
                 },
@@ -118,7 +131,7 @@
             .add_flag(
                 Source::File("second.txt".to_string()),
                 Flag {
-                    id: "foo".to_string(),
+                    name: "foo".to_string(),
                     description: "desc".to_string(),
                     values: vec![Value::default(FlagState::Disabled, Permission::ReadOnly)],
                 },
@@ -132,17 +145,18 @@
 
     #[test]
     fn test_add_override() {
-        fn check(cache: &Cache, id: &str, expected: (FlagState, Permission)) -> bool {
-            let item = cache.iter().find(|&item| item.id == id).unwrap();
+        fn check(cache: &Cache, name: &str, expected: (FlagState, Permission)) -> bool {
+            let item = cache.iter().find(|&item| item.name == name).unwrap();
             item.state == expected.0 && item.permission == expected.1
         }
 
-        let mut cache = Cache::new(1);
+        let mut cache = Cache::new(1, "ns".to_string());
         let error = cache
             .add_override(
                 Source::Memory,
                 Override {
-                    id: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadOnly,
                 },
@@ -154,7 +168,7 @@
             .add_flag(
                 Source::File("first.txt".to_string()),
                 Flag {
-                    id: "foo".to_string(),
+                    name: "foo".to_string(),
                     description: "desc".to_string(),
                     values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
                 },
@@ -167,7 +181,8 @@
             .add_override(
                 Source::Memory,
                 Override {
-                    id: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    name: "foo".to_string(),
                     state: FlagState::Disabled,
                     permission: Permission::ReadWrite,
                 },
@@ -180,12 +195,27 @@
             .add_override(
                 Source::Memory,
                 Override {
-                    id: "foo".to_string(),
+                    namespace: "ns".to_string(),
+                    name: "foo".to_string(),
                     state: FlagState::Enabled,
                     permission: Permission::ReadWrite,
                 },
             )
             .unwrap();
         assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
+
+        // different namespace -> no-op
+        cache
+            .add_override(
+                Source::Memory,
+                Override {
+                    namespace: "some-other-namespace".to_string(),
+                    name: "foo".to_string(),
+                    state: FlagState::Enabled,
+                    permission: Permission::ReadOnly,
+                },
+            )
+            .unwrap();
+        assert!(check(&cache, "foo", (FlagState::Enabled, Permission::ReadWrite)));
     }
 }
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
new file mode 100644
index 0000000..9d52cce
--- /dev/null
+++ b/tools/aconfig/src/codegen_java.rs
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use anyhow::Result;
+use serde::Serialize;
+use tinytemplate::TinyTemplate;
+
+use crate::aconfig::{FlagState, Permission};
+use crate::cache::{Cache, Item};
+
+pub struct GeneratedFile {
+    pub file_content: String,
+    pub file_name: String,
+}
+
+pub fn generate_java_code(cache: &Cache) -> Result<GeneratedFile> {
+    let class_elements: Vec<ClassElement> = cache.iter().map(create_class_element).collect();
+    let readwrite = class_elements.iter().any(|item| item.readwrite);
+    let namespace = uppercase_first_letter(
+        cache.iter().find(|item| !item.namespace.is_empty()).unwrap().namespace.as_str(),
+    );
+    let context = Context { namespace: namespace.clone(), readwrite, class_elements };
+    let mut template = TinyTemplate::new();
+    template.add_template("java_code_gen", include_str!("../templates/java.template"))?;
+    let file_content = template.render("java_code_gen", &context)?;
+    Ok(GeneratedFile { file_content, file_name: format!("{}.java", namespace) })
+}
+
+#[derive(Serialize)]
+struct Context {
+    pub namespace: String,
+    pub readwrite: bool,
+    pub class_elements: Vec<ClassElement>,
+}
+
+#[derive(Serialize)]
+struct ClassElement {
+    pub method_name: String,
+    pub readwrite: bool,
+    pub default_value: String,
+    pub feature_name: String,
+    pub flag_name: String,
+}
+
+fn create_class_element(item: &Item) -> ClassElement {
+    ClassElement {
+        method_name: item.name.clone(),
+        readwrite: item.permission == Permission::ReadWrite,
+        default_value: if item.state == FlagState::Enabled {
+            "true".to_string()
+        } else {
+            "false".to_string()
+        },
+        feature_name: item.name.clone(),
+        flag_name: item.name.clone(),
+    }
+}
+
+fn uppercase_first_letter(s: &str) -> String {
+    s.chars()
+        .enumerate()
+        .map(
+            |(index, ch)| {
+                if index == 0 {
+                    ch.to_ascii_uppercase()
+                } else {
+                    ch.to_ascii_lowercase()
+                }
+            },
+        )
+        .collect()
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::aconfig::{Flag, Value};
+    use crate::commands::Source;
+
+    #[test]
+    fn test_generate_java_code() {
+        let namespace = "TeSTFlaG";
+        let mut cache = Cache::new(1, namespace.to_string());
+        cache
+            .add_flag(
+                Source::File("test.txt".to_string()),
+                Flag {
+                    name: "test".to_string(),
+                    description: "buildtime enable".to_string(),
+                    values: vec![Value::default(FlagState::Enabled, Permission::ReadOnly)],
+                },
+            )
+            .unwrap();
+        cache
+            .add_flag(
+                Source::File("test2.txt".to_string()),
+                Flag {
+                    name: "test2".to_string(),
+                    description: "runtime disable".to_string(),
+                    values: vec![Value::default(FlagState::Disabled, Permission::ReadWrite)],
+                },
+            )
+            .unwrap();
+        let expect_content = "package com.android.aconfig;
+
+        import android.provider.DeviceConfig;
+
+        public final class Testflag {
+
+            public static boolean test() {
+                return true;
+            }
+
+            public static boolean test2() {
+                return DeviceConfig.getBoolean(
+                    \"Testflag\",
+                    \"test2__test2\",
+                    false
+                );
+            }
+
+        }
+        ";
+        let expected_file_name = format!("{}.java", uppercase_first_letter(namespace));
+        let generated_file = generate_java_code(&cache).unwrap();
+        assert_eq!(expected_file_name, generated_file.file_name);
+        assert_eq!(expect_content.replace(' ', ""), generated_file.file_content.replace(' ', ""));
+    }
+}
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 73d3357..1487e72 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-use anyhow::{Context, Result};
+use anyhow::{ensure, Context, Result};
 use clap::ValueEnum;
+use protobuf::Message;
 use serde::{Deserialize, Serialize};
 use std::fmt;
 use std::io::Read;
 
-use crate::aconfig::{Flag, Override};
+use crate::aconfig::{Namespace, Override};
 use crate::cache::Cache;
+use crate::codegen_java::{generate_java_code, GeneratedFile};
+use crate::protos::ProtoParsedFlags;
 
 #[derive(Serialize, Deserialize, Clone, Debug)]
 pub enum Source {
@@ -44,15 +47,27 @@
     pub reader: Box<dyn Read>,
 }
 
-pub fn create_cache(build_id: u32, aconfigs: Vec<Input>, overrides: Vec<Input>) -> Result<Cache> {
-    let mut cache = Cache::new(build_id);
+pub fn create_cache(
+    build_id: u32,
+    namespace: &str,
+    aconfigs: Vec<Input>,
+    overrides: Vec<Input>,
+) -> Result<Cache> {
+    let mut cache = Cache::new(build_id, namespace.to_owned());
 
     for mut input in aconfigs {
         let mut contents = String::new();
         input.reader.read_to_string(&mut contents)?;
-        let flags = Flag::try_from_text_proto_list(&contents)
+        let ns = Namespace::try_from_text_proto(&contents)
             .with_context(|| format!("Failed to parse {}", input.source))?;
-        for flag in flags {
+        ensure!(
+            namespace == ns.namespace,
+            "Failed to parse {}: expected namespace {}, got {}",
+            input.source,
+            namespace,
+            ns.namespace
+        );
+        for flag in ns.flags.into_iter() {
             cache.add_flag(input.source.clone(), flag)?;
         }
     }
@@ -70,26 +85,40 @@
     Ok(cache)
 }
 
+pub fn generate_code(cache: &Cache) -> Result<GeneratedFile> {
+    generate_java_code(cache)
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
 pub enum Format {
     Text,
     Debug,
+    Protobuf,
 }
 
-pub fn dump_cache(cache: Cache, format: Format) -> Result<()> {
+pub fn dump_cache(cache: Cache, format: Format) -> Result<Vec<u8>> {
     match format {
         Format::Text => {
+            let mut lines = vec![];
             for item in cache.iter() {
-                println!("{}: {:?}", item.id, item.state);
+                lines.push(format!("{}: {:?}\n", item.name, item.state));
             }
+            Ok(lines.concat().into())
         }
         Format::Debug => {
+            let mut lines = vec![];
             for item in cache.iter() {
-                println!("{:?}", item);
+                lines.push(format!("{:?}\n", item));
             }
+            Ok(lines.concat().into())
+        }
+        Format::Protobuf => {
+            let parsed_flags: ProtoParsedFlags = cache.into();
+            let mut output = vec![];
+            parsed_flags.write_to_vec(&mut output)?;
+            Ok(output)
         }
     }
-    Ok(())
 }
 
 #[cfg(test)]
@@ -97,30 +126,80 @@
     use super::*;
     use crate::aconfig::{FlagState, Permission};
 
-    #[test]
-    fn test_create_cache() {
+    fn create_test_cache() -> Cache {
         let s = r#"
+        namespace: "ns"
         flag {
-            id: "a"
+            name: "a"
             description: "Description of a"
             value {
                 state: ENABLED
                 permission: READ_WRITE
             }
         }
+        flag {
+            name: "b"
+            description: "Description of b"
+            value {
+                state: ENABLED
+                permission: READ_ONLY
+            }
+        }
         "#;
         let aconfigs = vec![Input { source: Source::Memory, reader: Box::new(s.as_bytes()) }];
         let o = r#"
-        override {
-            id: "a"
+        flag_override {
+            namespace: "ns"
+            name: "a"
             state: DISABLED
             permission: READ_ONLY
         }
         "#;
         let overrides = vec![Input { source: Source::Memory, reader: Box::new(o.as_bytes()) }];
-        let cache = create_cache(1, aconfigs, overrides).unwrap();
-        let item = cache.iter().find(|&item| item.id == "a").unwrap();
+        create_cache(1, "ns", aconfigs, overrides).unwrap()
+    }
+
+    #[test]
+    fn test_create_cache() {
+        let cache = create_test_cache(); // calls create_cache
+        let item = cache.iter().find(|&item| item.name == "a").unwrap();
         assert_eq!(FlagState::Disabled, item.state);
         assert_eq!(Permission::ReadOnly, item.permission);
     }
+
+    #[test]
+    fn test_dump_text_format() {
+        let cache = create_test_cache();
+        let bytes = dump_cache(cache, Format::Text).unwrap();
+        let text = std::str::from_utf8(&bytes).unwrap();
+        assert!(text.contains("a: Disabled"));
+    }
+
+    #[test]
+    fn test_dump_protobuf_format() {
+        use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoTracepoint};
+        use protobuf::Message;
+
+        let cache = create_test_cache();
+        let bytes = dump_cache(cache, Format::Protobuf).unwrap();
+        let actual = ProtoParsedFlags::parse_from_bytes(&bytes).unwrap();
+
+        assert_eq!(
+            vec!["a".to_string(), "b".to_string()],
+            actual.parsed_flag.iter().map(|item| item.name.clone().unwrap()).collect::<Vec<_>>()
+        );
+
+        let item =
+            actual.parsed_flag.iter().find(|item| item.name == Some("b".to_string())).unwrap();
+        assert_eq!(item.namespace(), "ns");
+        assert_eq!(item.name(), "b");
+        assert_eq!(item.description(), "Description of b");
+        assert_eq!(item.state(), ProtoFlagState::ENABLED);
+        assert_eq!(item.permission(), ProtoFlagPermission::READ_ONLY);
+        let mut tp = ProtoTracepoint::new();
+        tp.set_source("<memory>".to_string());
+        tp.set_state(ProtoFlagState::ENABLED);
+        tp.set_permission(ProtoFlagPermission::READ_ONLY);
+        assert_eq!(item.trace, vec![tp]);
+    }
 }
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index 62750ae..f29186a 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -19,9 +19,12 @@
 use anyhow::Result;
 use clap::{builder::ArgAction, builder::EnumValueParser, Arg, ArgMatches, Command};
 use std::fs;
+use std::io;
+use std::io::Write;
 
 mod aconfig;
 mod cache;
+mod codegen_java;
 mod commands;
 mod protos;
 
@@ -39,17 +42,26 @@
                         .value_parser(clap::value_parser!(u32))
                         .required(true),
                 )
+                .arg(Arg::new("namespace").long("namespace").required(true))
                 .arg(Arg::new("aconfig").long("aconfig").action(ArgAction::Append))
                 .arg(Arg::new("override").long("override").action(ArgAction::Append))
                 .arg(Arg::new("cache").long("cache").required(true)),
         )
         .subcommand(
-            Command::new("dump").arg(Arg::new("cache").long("cache").required(true)).arg(
-                Arg::new("format")
-                    .long("format")
-                    .value_parser(EnumValueParser::<commands::Format>::new())
-                    .default_value("text"),
-            ),
+            Command::new("create-java-lib")
+                .arg(Arg::new("cache").long("cache").required(true))
+                .arg(Arg::new("out").long("out").required(true)),
+        )
+        .subcommand(
+            Command::new("dump")
+                .arg(Arg::new("cache").long("cache").required(true))
+                .arg(
+                    Arg::new("format")
+                        .long("format")
+                        .value_parser(EnumValueParser::<commands::Format>::new())
+                        .default_value("text"),
+                )
+                .arg(Arg::new("out").long("out").default_value("-")),
         )
 }
 
@@ -67,19 +79,38 @@
     match matches.subcommand() {
         Some(("create-cache", sub_matches)) => {
             let build_id = *sub_matches.get_one::<u32>("build-id").unwrap();
+            let namespace = sub_matches.get_one::<String>("namespace").unwrap();
             let aconfigs = open_zero_or_more_files(sub_matches, "aconfig")?;
             let overrides = open_zero_or_more_files(sub_matches, "override")?;
-            let cache = commands::create_cache(build_id, aconfigs, overrides)?;
+            let cache = commands::create_cache(build_id, namespace, aconfigs, overrides)?;
             let path = sub_matches.get_one::<String>("cache").unwrap();
             let file = fs::File::create(path)?;
             cache.write_to_writer(file)?;
         }
+        Some(("create-java-lib", sub_matches)) => {
+            let path = sub_matches.get_one::<String>("cache").unwrap();
+            let file = fs::File::open(path)?;
+            let cache = Cache::read_from_reader(file)?;
+            let out = sub_matches.get_one::<String>("out").unwrap();
+            let generated_file = commands::generate_code(&cache).unwrap();
+            fs::write(
+                format!("{}/{}", out, generated_file.file_name),
+                generated_file.file_content,
+            )?;
+        }
         Some(("dump", sub_matches)) => {
             let path = sub_matches.get_one::<String>("cache").unwrap();
             let file = fs::File::open(path)?;
             let cache = Cache::read_from_reader(file)?;
             let format = sub_matches.get_one("format").unwrap();
-            commands::dump_cache(cache, *format)?;
+            let output = commands::dump_cache(cache, *format)?;
+            let path = sub_matches.get_one::<String>("out").unwrap();
+            let mut file: Box<dyn Write> = if path == "-" {
+                Box::new(io::stdout())
+            } else {
+                Box::new(fs::File::create(path)?)
+            };
+            file.write_all(&output)?;
         }
         _ => unreachable!(),
     }
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index 604eca4..5965a09 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -28,51 +28,69 @@
 
 // ---- When building with the Android tool-chain ----
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Android_config as ProtoAndroidConfig;
+pub use aconfig_protos::aconfig::Namespace as ProtoNamespace;
 
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Value as ProtoValue;
+pub use aconfig_protos::aconfig::Flag_value as ProtoFlagDefinitionValue;
 
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Flag as ProtoFlag;
+pub use aconfig_protos::aconfig::Flag_definition as ProtoFlagDefinition;
 
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Override_config as ProtoOverrideConfig;
+pub use aconfig_protos::aconfig::Flag_overrides as ProtoFlagOverrides;
 
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Override as ProtoOverride;
+pub use aconfig_protos::aconfig::Flag_override as ProtoFlagOverride;
 
 #[cfg(not(feature = "cargo"))]
-pub use aconfig_protos::aconfig::Permission as ProtoPermission;
+pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
 
 #[cfg(not(feature = "cargo"))]
 pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
 
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(not(feature = "cargo"))]
+pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
+
 // ---- When building with cargo ----
 #[cfg(feature = "cargo")]
 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Android_config as ProtoAndroidConfig;
+pub use aconfig::Namespace as ProtoNamespace;
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Value as ProtoValue;
+pub use aconfig::Flag_value as ProtoFlagDefinitionValue;
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Flag as ProtoFlag;
+pub use aconfig::Flag_definition as ProtoFlagDefinition;
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Override_config as ProtoOverrideConfig;
+pub use aconfig::Flag_overrides as ProtoFlagOverrides;
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Override as ProtoOverride;
+pub use aconfig::Flag_override as ProtoFlagOverride;
 
 #[cfg(feature = "cargo")]
-pub use aconfig::Permission as ProtoPermission;
+pub use aconfig::Flag_permission as ProtoFlagPermission;
 
 #[cfg(feature = "cargo")]
 pub use aconfig::Flag_state as ProtoFlagState;
 
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flags as ProtoParsedFlags;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Parsed_flag as ProtoParsedFlag;
+
+#[cfg(feature = "cargo")]
+pub use aconfig::Tracepoint as ProtoTracepoint;
+
 // ---- Common for both the Android tool-chain and cargo ----
 use anyhow::Result;
 
diff --git a/tools/aconfig/templates/java.template b/tools/aconfig/templates/java.template
new file mode 100644
index 0000000..3854579
--- /dev/null
+++ b/tools/aconfig/templates/java.template
@@ -0,0 +1,19 @@
+package com.android.aconfig;
+{{ if readwrite }}
+import android.provider.DeviceConfig;
+{{ endif }}
+public final class {namespace} \{
+    {{ for item in class_elements}}
+    public static boolean {item.method_name}() \{
+        {{ if item.readwrite- }}
+        return DeviceConfig.getBoolean(
+            "{namespace}",
+            "{item.feature_name}__{item.flag_name}",
+            {item.default_value}
+        ); 
+        {{ -else- }}
+        return {item.default_value};
+        {{ -endif }}
+    }
+    {{ endfor }}
+}
diff --git a/tools/finalization/localonly-steps.sh b/tools/finalization/localonly-steps.sh
index 6107b3e..7318ca1 100755
--- a/tools/finalization/localonly-steps.sh
+++ b/tools/finalization/localonly-steps.sh
@@ -17,7 +17,7 @@
     $top/build/soong/soong_ui.bash --make-mode TARGET_PRODUCT=sdk TARGET_BUILD_VARIANT=userdebug sdk dist sdk_repo DIST_DIR=out/dist
 
     # Build Modules SDKs.
-    TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh"
+    TARGET_BUILD_VARIANT=userdebug UNBUNDLED_BUILD_SDKS_FROM_SOURCE=true DIST_DIR=out/dist "$top/vendor/google/build/mainline_modules_sdks.sh" --build-release=latest
 
     # Update prebuilts.
     "$top/prebuilts/build-tools/path/linux-x86/python3" -W ignore::DeprecationWarning "$top/prebuilts/sdk/update_prebuilts.py" --local_mode -f ${FINAL_PLATFORM_SDK_VERSION} -e ${FINAL_MAINLINE_EXTENSION} --bug 1 1
diff --git a/tools/releasetools/non_ab_ota.py b/tools/releasetools/non_ab_ota.py
index c4fd809..667891c 100644
--- a/tools/releasetools/non_ab_ota.py
+++ b/tools/releasetools/non_ab_ota.py
@@ -23,6 +23,7 @@
 from check_target_files_vintf import CheckVintfIfTrebleEnabled, HasPartition
 from common import OPTIONS
 from ota_utils import UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata, PropertyFiles
+import subprocess
 
 logger = logging.getLogger(__name__)
 
@@ -277,7 +278,8 @@
   needed_property_files = (
       NonAbOtaPropertyFiles(),
   )
-  FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
+  FinalizeMetadata(metadata, staging_file, output_file,
+                   needed_property_files, package_key=OPTIONS.package_key)
 
 
 def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file):
@@ -532,7 +534,8 @@
   needed_property_files = (
       NonAbOtaPropertyFiles(),
   )
-  FinalizeMetadata(metadata, staging_file, output_file, needed_property_files, package_key=OPTIONS.package_key)
+  FinalizeMetadata(metadata, staging_file, output_file,
+                   needed_property_files, package_key=OPTIONS.package_key)
 
 
 def GenerateNonAbOtaPackage(target_file, output_file, source_file=None):
@@ -555,8 +558,18 @@
   if OPTIONS.extracted_input is not None:
     OPTIONS.input_tmp = OPTIONS.extracted_input
   else:
-    logger.info("unzipping target target-files...")
-    OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
+    if not os.path.isdir(target_file):
+      logger.info("unzipping target target-files...")
+      OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN)
+    else:
+      OPTIONS.input_tmp = target_file
+      tmpfile = common.MakeTempFile(suffix=".zip")
+      os.unlink(tmpfile)
+      common.RunAndCheckOutput(
+          ["zip", tmpfile, "-r", ".", "-0"], cwd=target_file)
+      assert zipfile.is_zipfile(tmpfile)
+      target_file = tmpfile
+
   OPTIONS.target_tmp = OPTIONS.input_tmp
 
   # If the caller explicitly specified the device-specific extensions path via
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 1a4a895..04ef5ef 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -273,7 +273,6 @@
                        PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, ExtractTargetFiles, CopyTargetFilesDir)
 from common import DoesInputFileContain, IsSparseImage
 import target_files_diff
-from check_target_files_vintf import CheckVintfIfTrebleEnabled
 from non_ab_ota import GenerateNonAbOtaPackage
 from payload_signer import PayloadSigner
 
@@ -954,6 +953,7 @@
   target_info.info_dict['ab_partitions'] = common.ReadFromInputFile(target_file,
                                                                     AB_PARTITIONS).strip().split("\n")
 
+  from check_target_files_vintf import CheckVintfIfTrebleEnabled
   CheckVintfIfTrebleEnabled(target_file, target_info)
 
   # Metadata to comply with Android OTA package format.