Merge "aconfig: create flag info file at build time" into main
diff --git a/core/packaging/flags.mk b/core/packaging/flags.mk
index 4693bcd..ccb502c 100644
--- a/core/packaging/flags.mk
+++ b/core/packaging/flags.mk
@@ -97,42 +97,54 @@
 # $(1): built aconfig flags storage package map file (out)
 # $(2): built aconfig flags storage flag map file (out)
 # $(3): built aconfig flags storage flag val file (out)
-# $(4): installed aconfig flags storage package map file (out)
-# $(5): installed aconfig flags storage flag map file (out)
-# $(6): installed aconfig flags storage flag value file (out)
-# $(7): input aconfig files for the partition (in)
-# $(8): partition name
+# $(4): built aconfig flags storage flag info file (out)
+# $(5): installed aconfig flags storage package map file (out)
+# $(6): installed aconfig flags storage flag map file (out)
+# $(7): installed aconfig flags storage flag value file (out)
+# $(8): installed aconfig flags storage flag info file (out)
+# $(9): input aconfig files for the partition (in)
+# $(10): partition name
 define generate-partition-aconfig-storage-file
 $(eval $(strip $(1)): PRIVATE_OUT := $(strip $(1)))
-$(eval $(strip $(1)): PRIVATE_IN := $(strip $(7)))
-$(strip $(1)): $(ACONFIG) $(strip $(7))
+$(eval $(strip $(1)): PRIVATE_IN := $(strip $(9)))
+$(strip $(1)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(8) --file package_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file package_map --out $$(PRIVATE_OUT) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
 $(eval $(strip $(2)): PRIVATE_OUT := $(strip $(2)))
-$(eval $(strip $(2)): PRIVATE_IN := $(strip $(7)))
-$(strip $(2)): $(ACONFIG) $(strip $(7))
+$(eval $(strip $(2)): PRIVATE_IN := $(strip $(9)))
+$(strip $(2)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(8) --file flag_map --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file flag_map --out $$(PRIVATE_OUT) \
 			$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
 $(eval $(strip $(3)): PRIVATE_OUT := $(strip $(3)))
-$(eval $(strip $(3)): PRIVATE_IN := $(strip $(7)))
-$(strip $(3)): $(ACONFIG) $(strip $(7))
+$(eval $(strip $(3)): PRIVATE_IN := $(strip $(9)))
+$(strip $(3)): $(ACONFIG) $(strip $(9))
 	mkdir -p $$(dir $$(PRIVATE_OUT))
 	$$(if $$(PRIVATE_IN), \
-		$$(ACONFIG) create-storage --container $(8) --file flag_val --out $$(PRIVATE_OUT) \
+		$$(ACONFIG) create-storage --container $(10) --file flag_val --out $$(PRIVATE_OUT) \
 		$$(addprefix --cache ,$$(PRIVATE_IN)), \
 	)
 	touch $$(PRIVATE_OUT)
-$(call copy-one-file, $(strip $(1)), $(4))
-$(call copy-one-file, $(strip $(2)), $(5))
-$(call copy-one-file, $(strip $(3)), $(6))
+$(eval $(strip $(4)): PRIVATE_OUT := $(strip $(4)))
+$(eval $(strip $(4)): PRIVATE_IN := $(strip $(9)))
+$(strip $(4)): $(ACONFIG) $(strip $(9))
+	mkdir -p $$(dir $$(PRIVATE_OUT))
+	$$(if $$(PRIVATE_IN), \
+		$$(ACONFIG) create-storage --container $(10) --file flag_info --out $$(PRIVATE_OUT) \
+		$$(addprefix --cache ,$$(PRIVATE_IN)), \
+	)
+	touch $$(PRIVATE_OUT)
+$(call copy-one-file, $(strip $(1)), $(5))
+$(call copy-one-file, $(strip $(2)), $(6))
+$(call copy-one-file, $(strip $(3)), $(7))
+$(call copy-one-file, $(strip $(4)), $(8))
 endef
 
 ifeq ($(RELEASE_CREATE_ACONFIG_STORAGE_FILE),true)
@@ -140,13 +152,16 @@
 	$(eval aconfig_storage_package_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/package.map) \
 	$(eval aconfig_storage_flag_map.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.map) \
 	$(eval aconfig_storage_flag_val.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.val) \
+	$(eval aconfig_storage_flag_info.$(partition) := $(PRODUCT_OUT)/$(partition)/etc/aconfig/flag.info) \
 	$(eval $(call generate-partition-aconfig-storage-file, \
 				$(TARGET_OUT_FLAGS)/$(partition)/package.map, \
 				$(TARGET_OUT_FLAGS)/$(partition)/flag.map, \
 				$(TARGET_OUT_FLAGS)/$(partition)/flag.val, \
+				$(TARGET_OUT_FLAGS)/$(partition)/flag.info, \
 				$(aconfig_storage_package_map.$(partition)), \
 				$(aconfig_storage_flag_map.$(partition)), \
 				$(aconfig_storage_flag_val.$(partition)), \
+				$(aconfig_storage_flag_info.$(partition)), \
 				$(aconfig_flag_summaries_protobuf.$(partition)), \
 				$(partition), \
 	)) \
@@ -162,6 +177,7 @@
 			$(aconfig_storage_package_map.$(partition)) \
 			$(aconfig_storage_flag_map.$(partition)) \
 			$(aconfig_storage_flag_val.$(partition)) \
+			$(aconfig_storage_flag_info.$(partition)) \
 		))
 
 ALL_DEFAULT_INSTALLED_MODULES += $(required_flags_files)
@@ -181,4 +197,5 @@
 	$(eval aconfig_storage_package_map.$(partition):=) \
 	$(eval aconfig_storage_flag_map.$(partition):=) \
 	$(eval aconfig_storage_flag_val.$(partition):=) \
+	$(eval aconfig_storage_flag_info.$(partition):=) \
 )
diff --git a/tools/aconfig/aconfig/src/storage/flag_info.rs b/tools/aconfig/aconfig/src/storage/flag_info.rs
new file mode 100644
index 0000000..04e2b93
--- /dev/null
+++ b/tools/aconfig/aconfig/src/storage/flag_info.rs
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use crate::commands::assign_flag_ids;
+use crate::storage::FlagPackage;
+use aconfig_protos::ProtoFlagPermission;
+use aconfig_storage_file::{
+    FlagInfoHeader, FlagInfoList, FlagInfoNode, StorageFileType, FILE_VERSION,
+};
+use anyhow::{anyhow, Result};
+
+fn new_header(container: &str, num_flags: u32) -> FlagInfoHeader {
+    FlagInfoHeader {
+        version: FILE_VERSION,
+        container: String::from(container),
+        file_type: StorageFileType::FlagInfo as u8,
+        file_size: 0,
+        num_flags,
+        boolean_flag_offset: 0,
+    }
+}
+
+pub fn create_flag_info(container: &str, packages: &[FlagPackage]) -> Result<FlagInfoList> {
+    // create list
+    let num_flags = packages.iter().map(|pkg| pkg.boolean_flags.len() as u32).sum();
+
+    let mut is_flag_rw = vec![false; num_flags as usize];
+    for pkg in packages.iter() {
+        let start_index = pkg.boolean_start_index as usize;
+        let flag_ids = assign_flag_ids(pkg.package_name, pkg.boolean_flags.iter().copied())?;
+        for pf in pkg.boolean_flags.iter() {
+            let fid = flag_ids
+                .get(pf.name())
+                .ok_or(anyhow!(format!("missing flag id for {}", pf.name())))?;
+            is_flag_rw[start_index + (*fid as usize)] =
+                pf.permission() == ProtoFlagPermission::READ_WRITE;
+        }
+    }
+
+    let mut list = FlagInfoList {
+        header: new_header(container, num_flags),
+        nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
+    };
+
+    // initialize all header fields
+    list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
+    let bytes_per_node = FlagInfoNode::create(false).into_bytes().len() as u32;
+    list.header.file_size = list.header.boolean_flag_offset + num_flags * bytes_per_node;
+
+    Ok(list)
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::storage::{group_flags_by_package, tests::parse_all_test_flags};
+
+    pub fn create_test_flag_info_list_from_source() -> Result<FlagInfoList> {
+        let caches = parse_all_test_flags();
+        let packages = group_flags_by_package(caches.iter());
+        create_flag_info("mockup", &packages)
+    }
+
+    #[test]
+    // this test point locks down the flag info creation and each field
+    fn test_list_contents() {
+        let flag_info_list = create_test_flag_info_list_from_source();
+        assert!(flag_info_list.is_ok());
+        let expected_flag_info_list =
+            aconfig_storage_file::test_utils::create_test_flag_info_list();
+        assert_eq!(flag_info_list.unwrap(), expected_flag_info_list);
+    }
+}
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 73339f2..1d8dcfc 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -14,15 +14,16 @@
  * limitations under the License.
  */
 
+pub mod flag_info;
 pub mod flag_table;
 pub mod flag_value;
 pub mod package_table;
 
-use anyhow::{anyhow, Result};
+use anyhow::Result;
 use std::collections::{HashMap, HashSet};
 
 use crate::storage::{
-    flag_table::create_flag_table, flag_value::create_flag_value,
+    flag_info::create_flag_info, flag_table::create_flag_table, flag_value::create_flag_value,
     package_table::create_package_table,
 };
 use aconfig_protos::{ProtoParsedFlag, ProtoParsedFlags};
@@ -106,7 +107,10 @@
             let flag_value = create_flag_value(container, &packages)?;
             Ok(flag_value.into_bytes())
         }
-        _ => Err(anyhow!("aconfig does not support the creation of this storage file type")),
+        StorageFileType::FlagInfo => {
+            let flag_info = create_flag_info(container, &packages)?;
+            Ok(flag_info.into_bytes())
+        }
     }
 }
 
diff --git a/tools/filelistdiff/allowlist b/tools/filelistdiff/allowlist
index 120045e..b3ae819 100644
--- a/tools/filelistdiff/allowlist
+++ b/tools/filelistdiff/allowlist
@@ -41,6 +41,7 @@
 etc/aconfig/flag.map
 etc/aconfig/flag.val
 etc/aconfig/package.map
+etc/aconfig/flag.info
 etc/bpf/uprobestats/BitmapAllocation.o
 etc/bpf/uprobestats/GenericInstrumentation.o
 etc/bpf/uprobestats/ProcessManagement.o