aconfig: add api to create flag info file based on package map and flag
map file
Bug: b/321077378
Test: atest aconfig_storage_file.test
Change-Id: I957e231bc11db856a8c6753771eaafea2f168352
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
index c67e70e..3fff263 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
@@ -130,6 +130,11 @@
let node = Self { attributes: read_u8_from_bytes(bytes, &mut head)? };
Ok(node)
}
+
+ /// Create flag info node
+ pub fn create(is_flag_rw: bool) -> Self {
+ Self { attributes: if is_flag_rw { FlagInfoBit::IsReadWrite as u8 } else { 0u8 } }
+ }
}
/// Flag info list struct
@@ -221,7 +226,7 @@
let bytes = &flag_info_list.into_bytes();
let mut head = 0;
let version = read_u32_from_bytes(bytes, &mut head).unwrap();
- assert_eq!(version, 1234)
+ assert_eq!(version, 1);
}
#[test]
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index 85f1f0a..3b975d9 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -44,14 +44,16 @@
use std::collections::hash_map::DefaultHasher;
use std::fs::File;
use std::hash::{Hash, Hasher};
-use std::io::Read;
+use std::io::{Read, Write};
pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
-use crate::AconfigStorageError::{BytesParseFail, HashTableSizeLimit, InvalidStoredFlagType};
+use crate::AconfigStorageError::{
+ BytesParseFail, FileCreationFail, HashTableSizeLimit, InvalidStoredFlagType,
+};
/// Storage file version
pub const FILE_VERSION: u32 = 1;
@@ -164,7 +166,6 @@
InvalidStoredFlagType(#[source] anyhow::Error),
}
-
/// Get the right hash table size given number of entries in the table. Use a
/// load factor of 0.5 for performance.
pub fn get_table_size(entries: u32) -> Result<u32, AconfigStorageError> {
@@ -263,25 +264,82 @@
let (package_name, package_offset) = package_info[node.package_id as usize];
let flag_offset = package_offset + node.flag_id as u32;
let flag_value = flag_value_list.booleans[flag_offset as usize];
- flags.push((String::from(package_name), node.flag_name.clone(), node.flag_type, flag_value));
+ flags.push((
+ String::from(package_name),
+ node.flag_name.clone(),
+ node.flag_type,
+ flag_value,
+ ));
}
- flags.sort_by(|v1, v2| {
- match v1.0.cmp(&v2.0) {
- Ordering::Equal => v1.1.cmp(&v2.1),
- other => other,
- }
+ flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
+ Ordering::Equal => v1.1.cmp(&v2.1),
+ other => other,
});
Ok(flags)
}
+/// Create flag info file
+pub fn create_flag_info(
+ package_map: &str,
+ flag_map: &str,
+ flag_info_out: &str,
+) -> Result<(), AconfigStorageError> {
+ let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
+ let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
+
+ if package_table.header.container != flag_table.header.container {
+ return Err(FileCreationFail(anyhow!(
+ "container for package map {} and flag map {} does not match",
+ package_table.header.container,
+ flag_table.header.container,
+ )));
+ }
+
+ let mut package_offsets = vec![0; package_table.header.num_packages as usize];
+ for node in package_table.nodes.iter() {
+ package_offsets[node.package_id as usize] = node.boolean_offset;
+ }
+
+ let mut is_flag_rw = vec![false; flag_table.header.num_flags as usize];
+ for node in flag_table.nodes.iter() {
+ let flag_offset = package_offsets[node.package_id as usize] + node.flag_id as u32;
+ is_flag_rw[flag_offset as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean;
+ }
+
+ let mut list = FlagInfoList {
+ header: FlagInfoHeader {
+ version: FILE_VERSION,
+ container: flag_table.header.container,
+ file_type: StorageFileType::FlagInfo as u8,
+ file_size: 0,
+ num_flags: flag_table.header.num_flags,
+ boolean_flag_offset: 0,
+ },
+ nodes: is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect(),
+ };
+
+ list.header.boolean_flag_offset = list.header.into_bytes().len() as u32;
+ list.header.file_size = list.into_bytes().len() as u32;
+
+ let mut file = File::create(flag_info_out).map_err(|errmsg| {
+ FileCreationFail(anyhow!("fail to create file {}: {}", flag_info_out, errmsg))
+ })?;
+ file.write_all(&list.into_bytes()).map_err(|errmsg| {
+ FileCreationFail(anyhow!("fail to write to file {}: {}", flag_info_out, errmsg))
+ })?;
+
+ Ok(())
+}
+
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::{
- create_test_flag_table, create_test_flag_value_list, create_test_package_table,
- write_bytes_to_temp_file,
+ create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
+ create_test_package_table, write_bytes_to_temp_file,
};
+ use tempfile::NamedTempFile;
#[test]
// this test point locks down the flag list api
@@ -309,13 +367,13 @@
String::from("com.android.aconfig.storage.test_1"),
String::from("enabled_ro"),
StoredFlagType::ReadOnlyBoolean,
- true
+ true,
),
(
String::from("com.android.aconfig.storage.test_1"),
String::from("enabled_rw"),
StoredFlagType::ReadWriteBoolean,
- true
+ true,
),
(
String::from("com.android.aconfig.storage.test_2"),
@@ -333,7 +391,7 @@
String::from("com.android.aconfig.storage.test_2"),
String::from("enabled_ro"),
StoredFlagType::ReadOnlyBoolean,
- true
+ true,
),
(
String::from("com.android.aconfig.storage.test_4"),
@@ -345,9 +403,36 @@
String::from("com.android.aconfig.storage.test_4"),
String::from("enabled_ro"),
StoredFlagType::ReadOnlyBoolean,
- true
+ true,
),
];
assert_eq!(flags, expected);
}
+
+ fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
+ let file = NamedTempFile::new().map_err(|_| {
+ AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+ })?;
+ Ok(file)
+ }
+
+ #[test]
+ // this test point locks down the flag info creation
+ fn test_create_flag_info() {
+ let package_table =
+ write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
+ let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
+ let flag_info = create_empty_temp_file().unwrap();
+
+ let package_table_path = package_table.path().display().to_string();
+ let flag_table_path = flag_table.path().display().to_string();
+ let flag_info_path = flag_info.path().display().to_string();
+
+ assert!(create_flag_info(&package_table_path, &flag_table_path, &flag_info_path).is_ok());
+
+ let flag_info =
+ FlagInfoList::from_bytes(&read_file_to_bytes(&flag_info_path).unwrap()).unwrap();
+ let expected_flag_info = create_test_flag_info_list();
+ assert_eq!(flag_info, expected_flag_info);
+ }
}
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index 9198f93..c0f647a 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -133,14 +133,15 @@
pub fn create_test_flag_info_list() -> FlagInfoList {
let header = FlagInfoHeader {
- version: 1234,
+ version: 1,
container: String::from("mockup"),
file_type: StorageFileType::FlagInfo as u8,
file_size: 35,
num_flags: 8,
boolean_flag_offset: 27,
};
- let nodes: Vec<FlagInfoNode> = vec![FlagInfoNode { attributes: 0 }; 8];
+ let is_flag_rw = [true, false, true, false, false, false, false, false];
+ let nodes = is_flag_rw.iter().map(|&rw| FlagInfoNode::create(rw)).collect();
FlagInfoList { header, nodes }
}