Merge "Merge Android 14 QPR2 to AOSP main" into main
diff --git a/tools/aconfig/Cargo.toml b/tools/aconfig/Cargo.toml
index 7112fd4..6bd0d06 100644
--- a/tools/aconfig/Cargo.toml
+++ b/tools/aconfig/Cargo.toml
@@ -5,6 +5,7 @@
"aconfig_protos",
"aconfig_storage_file",
"aconfig_storage_read_api",
+ "aconfig_storage_write_api",
"aflags",
"printflags"
]
diff --git a/tools/aconfig/aconfig_storage_file/Android.bp b/tools/aconfig/aconfig_storage_file/Android.bp
index 6be3c19..c089d54 100644
--- a/tools/aconfig/aconfig_storage_file/Android.bp
+++ b/tools/aconfig/aconfig_storage_file/Android.bp
@@ -10,6 +10,10 @@
rustlibs: [
"libanyhow",
"libthiserror",
+ "libtempfile",
+ "libprotobuf",
+ "libclap",
+ "libaconfig_storage_protos",
],
}
@@ -25,3 +29,25 @@
test_suites: ["general-tests"],
defaults: ["aconfig_storage_file.defaults"],
}
+
+rust_protobuf {
+ name: "libaconfig_storage_protos",
+ protos: ["protos/aconfig_storage_metadata.proto"],
+ crate_name: "aconfig_storage_protos",
+ source_stem: "aconfig_storage_protos",
+ host_supported: true,
+}
+
+cc_library_static {
+ name: "libaconfig_storage_protos_cc",
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["protos/aconfig_storage_metadata.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "//apex_available:anyapex",
+ ],
+ host_supported: true,
+}
diff --git a/tools/aconfig/aconfig_storage_file/Cargo.toml b/tools/aconfig/aconfig_storage_file/Cargo.toml
index c4e2670..9b9a615 100644
--- a/tools/aconfig/aconfig_storage_file/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_file/Cargo.toml
@@ -9,12 +9,10 @@
[dependencies]
anyhow = "1.0.69"
-memmap2 = "0.8.0"
protobuf = "3.2.0"
-once_cell = "1.19.0"
tempfile = "3.9.0"
-cxx = "1.0"
thiserror = "1.0.56"
+clap = { version = "4.1.8", features = ["derive"] }
[build-dependencies]
protobuf-codegen = "3.2.0"
diff --git a/tools/aconfig/aconfig_storage_file/build.rs b/tools/aconfig/aconfig_storage_file/build.rs
new file mode 100644
index 0000000..1feeb60
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/build.rs
@@ -0,0 +1,17 @@
+use protobuf_codegen::Codegen;
+
+fn main() {
+ let proto_files = vec!["protos/aconfig_storage_metadata.proto"];
+
+ // tell cargo to only re-run the build script if any of the proto files has changed
+ for path in &proto_files {
+ println!("cargo:rerun-if-changed={}", path);
+ }
+
+ Codegen::new()
+ .pure()
+ .include("protos")
+ .inputs(proto_files)
+ .cargo_out_dir("aconfig_storage_protos")
+ .run_from_script();
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/protos/aconfig_storage_metadata.proto b/tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
similarity index 100%
rename from tools/aconfig/aconfig_storage_read_api/protos/aconfig_storage_metadata.proto
rename to tools/aconfig/aconfig_storage_file/protos/aconfig_storage_metadata.proto
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index e1791d1..d6e5c62 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -20,9 +20,10 @@
use crate::AconfigStorageError::{self, BytesParseFail};
use crate::{get_bucket_index, read_str_from_bytes, read_u16_from_bytes, read_u32_from_bytes};
use anyhow::anyhow;
+use std::fmt;
/// Flag table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagTableHeader {
pub version: u32,
pub container: String,
@@ -32,6 +33,23 @@
pub node_offset: u32,
}
+/// Implement debug print trait for header
+impl fmt::Debug for FlagTableHeader {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Version: {}, Container: {}, File Size: {}",
+ self.version, self.container, self.file_size
+ )?;
+ writeln!(
+ f,
+ "Num of Flags: {}, Bucket Offset:{}, Node Offset: {}",
+ self.num_flags, self.bucket_offset, self.node_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagTableHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -62,7 +80,7 @@
}
/// Flag table node struct
-#[derive(PartialEq, Debug, Clone)]
+#[derive(PartialEq, Clone)]
pub struct FlagTableNode {
pub package_id: u32,
pub flag_name: String,
@@ -71,6 +89,18 @@
pub next_offset: Option<u32>,
}
+/// Implement debug print trait for node
+impl fmt::Debug for FlagTableNode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Package Id: {}, Flag: {}, Type: {}, Offset: {}, Next: {:?}",
+ self.package_id, self.flag_name, self.flag_type, self.flag_id, self.next_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagTableNode {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -108,13 +138,28 @@
}
}
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagTable {
pub header: FlagTableHeader,
pub buckets: Vec<Option<u32>>,
pub nodes: Vec<FlagTableNode>,
}
+/// Implement debug print trait for flag table
+impl fmt::Debug for FlagTable {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Buckets:")?;
+ writeln!(f, "{:?}", self.buckets)?;
+ writeln!(f, "Nodes:")?;
+ for node in self.nodes.iter() {
+ write!(f, "{:?}", node)?;
+ }
+ Ok(())
+ }
+}
+
/// Flag table struct
impl FlagTable {
/// Serialize to bytes
@@ -156,60 +201,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- impl FlagTableNode {
- // create test baseline, syntactic sugar
- fn new_expected(
- package_id: u32,
- flag_name: &str,
- flag_type: u16,
- flag_id: u16,
- next_offset: Option<u32>,
- ) -> Self {
- Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
- }
- }
-
- pub fn create_test_flag_table() -> FlagTable {
- let header = FlagTableHeader {
- version: crate::FILE_VERSION,
- container: String::from("system"),
- file_size: 320,
- num_flags: 8,
- bucket_offset: 30,
- node_offset: 98,
- };
- let buckets: Vec<Option<u32>> = vec![
- Some(98),
- Some(124),
- None,
- None,
- None,
- Some(177),
- None,
- Some(203),
- None,
- Some(261),
- None,
- None,
- None,
- None,
- None,
- Some(293),
- None,
- ];
- let nodes = vec![
- FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
- FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
- FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
- FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
- FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
- FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
- FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
- FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
- ];
- FlagTable { header, buckets, nodes }
- }
+ use crate::test_utils::create_test_flag_table;
#[test]
// this test point locks down the table serialization
@@ -231,4 +223,15 @@
assert!(reinterpreted_table.is_ok());
assert_eq!(&flag_table, &reinterpreted_table.unwrap());
}
+
+ #[test]
+ // this test point locks down that version number should be at the top of serialized
+ // bytes
+ fn test_version_number() {
+ let flag_table = create_test_flag_table();
+ let bytes = &flag_table.as_bytes();
+ let mut head = 0;
+ let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+ assert_eq!(version, 1234)
+ }
}
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_value.rs b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
index 8356847..021546c 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_value.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_value.rs
@@ -19,9 +19,10 @@
use crate::AconfigStorageError;
use crate::{read_str_from_bytes, read_u32_from_bytes, read_u8_from_bytes};
+use std::fmt;
/// Flag value header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagValueHeader {
pub version: u32,
pub container: String,
@@ -30,6 +31,23 @@
pub boolean_value_offset: u32,
}
+/// Implement debug print trait for header
+impl fmt::Debug for FlagValueHeader {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Version: {}, Container: {}, File Size: {}",
+ self.version, self.container, self.file_size
+ )?;
+ writeln!(
+ f,
+ "Num of Flags: {}, Value Offset:{}",
+ self.num_flags, self.boolean_value_offset
+ )?;
+ Ok(())
+ }
+}
+
impl FlagValueHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -58,12 +76,23 @@
}
/// Flag value list struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct FlagValueList {
pub header: FlagValueHeader,
pub booleans: Vec<bool>,
}
+/// Implement debug print trait for flag value
+impl fmt::Debug for FlagValueList {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Values:")?;
+ writeln!(f, "{:?}", self.booleans)?;
+ Ok(())
+ }
+}
+
impl FlagValueList {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -89,18 +118,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- pub fn create_test_flag_value_list() -> FlagValueList {
- let header = FlagValueHeader {
- version: crate::FILE_VERSION,
- container: String::from("system"),
- file_size: 34,
- num_flags: 8,
- boolean_value_offset: 26,
- };
- let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
- FlagValueList { header, booleans }
- }
+ use crate::test_utils::create_test_flag_value_list;
#[test]
// this test point locks down the value list serialization
@@ -116,4 +134,15 @@
assert!(reinterpreted_value_list.is_ok());
assert_eq!(&flag_value_list, &reinterpreted_value_list.unwrap());
}
+
+ #[test]
+ // this test point locks down that version number should be at the top of serialized
+ // bytes
+ fn test_version_number() {
+ let flag_value_list = create_test_flag_value_list();
+ let bytes = &flag_value_list.as_bytes();
+ let mut head = 0;
+ let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+ assert_eq!(version, 1234)
+ }
}
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index e06e149..ec41a4e 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -35,10 +35,16 @@
pub mod flag_table;
pub mod flag_value;
pub mod package_table;
+pub mod protos;
+
+#[cfg(test)]
+mod test_utils;
use anyhow::anyhow;
use std::collections::hash_map::DefaultHasher;
+use std::fs::File;
use std::hash::{Hash, Hasher};
+use std::io::Read;
pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
pub use crate::flag_value::{FlagValueHeader, FlagValueList};
@@ -166,4 +172,88 @@
#[error("invalid storage file byte offset")]
InvalidStorageFileOffset(#[source] anyhow::Error),
+
+ #[error("failed to create file")]
+ FileCreationFail(#[source] anyhow::Error),
+}
+
+/// Read in storage file as bytes
+pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageError> {
+ let mut file = File::open(file_path).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+ })?;
+ let mut buffer = Vec::new();
+ file.read_to_end(&mut buffer).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!(
+ "Failed to read 4 bytes from file {}: {}",
+ file_path,
+ errmsg
+ ))
+ })?;
+ Ok(buffer)
+}
+
+/// List flag values from storage files
+pub fn list_flags(
+ package_map: &str,
+ flag_map: &str,
+ flag_val: &str,
+) -> Result<Vec<(String, bool)>, 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)?)?;
+ let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
+
+ let mut package_info = vec![("", 0); package_table.header.num_packages as usize];
+ for node in package_table.nodes.iter() {
+ package_info[node.package_id as usize] = (&node.package_name, node.boolean_offset);
+ }
+
+ let mut flags = Vec::new();
+ for node in flag_table.nodes.iter() {
+ let (package_name, package_offset) = package_info[node.package_id as usize];
+ let full_flag_name = String::from(package_name) + "/" + &node.flag_name;
+ let flag_offset = package_offset + node.flag_id as u32;
+ let flag_value = flag_value_list.booleans[flag_offset as usize];
+ flags.push((full_flag_name, flag_value));
+ }
+
+ flags.sort_by(|v1, v2| v1.0.cmp(&v2.0));
+ Ok(flags)
+}
+
+#[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,
+ };
+
+ #[test]
+ // this test point locks down the flag list api
+ fn test_list_flag() {
+ let package_table =
+ write_bytes_to_temp_file(&create_test_package_table().as_bytes()).unwrap();
+ let flag_table = write_bytes_to_temp_file(&create_test_flag_table().as_bytes()).unwrap();
+ let flag_value_list =
+ write_bytes_to_temp_file(&create_test_flag_value_list().as_bytes()).unwrap();
+
+ let package_table_path = package_table.path().display().to_string();
+ let flag_table_path = flag_table.path().display().to_string();
+ let flag_value_list_path = flag_value_list.path().display().to_string();
+
+ let flags =
+ list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
+ let expected = [
+ (String::from("com.android.aconfig.storage.test_1/disabled_rw"), false),
+ (String::from("com.android.aconfig.storage.test_1/enabled_ro"), true),
+ (String::from("com.android.aconfig.storage.test_1/enabled_rw"), false),
+ (String::from("com.android.aconfig.storage.test_2/disabled_ro"), false),
+ (String::from("com.android.aconfig.storage.test_2/enabled_fixed_ro"), true),
+ (String::from("com.android.aconfig.storage.test_2/enabled_ro"), true),
+ (String::from("com.android.aconfig.storage.test_4/enabled_fixed_ro"), false),
+ (String::from("com.android.aconfig.storage.test_4/enabled_ro"), true),
+ ];
+ assert_eq!(flags, expected);
+ }
}
diff --git a/tools/aconfig/aconfig_storage_file/src/main.rs b/tools/aconfig/aconfig_storage_file/src/main.rs
new file mode 100644
index 0000000..2c7b87c
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/main.rs
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+//! `aconfig_storage` is a debugging tool to parse storage files
+
+use aconfig_storage_file::{
+ list_flags, read_file_to_bytes, AconfigStorageError, FlagTable, FlagValueList, PackageTable,
+ StorageFileSelection,
+};
+
+use clap::{builder::ArgAction, Arg, Command};
+
+fn cli() -> Command {
+ Command::new("aconfig_storage_file")
+ .subcommand_required(true)
+ .subcommand(
+ Command::new("print")
+ .arg(Arg::new("file").long("file").required(true).action(ArgAction::Set))
+ .arg(
+ Arg::new("type")
+ .long("type")
+ .required(true)
+ .value_parser(|s: &str| StorageFileSelection::try_from(s)),
+ ),
+ )
+ .subcommand(
+ Command::new("list")
+ .arg(
+ Arg::new("package_map")
+ .long("package_map")
+ .required(true)
+ .action(ArgAction::Set),
+ )
+ .arg(Arg::new("flag_map").long("flag_map").required(true).action(ArgAction::Set))
+ .arg(Arg::new("flag_val").long("flag_val").required(true).action(ArgAction::Set)),
+ )
+}
+
+fn print_storage_file(
+ file_path: &str,
+ file_type: &StorageFileSelection,
+) -> Result<(), AconfigStorageError> {
+ let bytes = read_file_to_bytes(file_path)?;
+ match file_type {
+ StorageFileSelection::PackageMap => {
+ let package_table = PackageTable::from_bytes(&bytes)?;
+ println!("{:?}", package_table);
+ }
+ StorageFileSelection::FlagMap => {
+ let flag_table = FlagTable::from_bytes(&bytes)?;
+ println!("{:?}", flag_table);
+ }
+ StorageFileSelection::FlagVal => {
+ let flag_value = FlagValueList::from_bytes(&bytes)?;
+ println!("{:?}", flag_value);
+ }
+ }
+ Ok(())
+}
+
+fn main() -> Result<(), AconfigStorageError> {
+ let matches = cli().get_matches();
+ match matches.subcommand() {
+ Some(("print", sub_matches)) => {
+ let file_path = sub_matches.get_one::<String>("file").unwrap();
+ let file_type = sub_matches.get_one::<StorageFileSelection>("type").unwrap();
+ print_storage_file(file_path, file_type)?
+ }
+ Some(("list", sub_matches)) => {
+ let package_map = sub_matches.get_one::<String>("package_map").unwrap();
+ let flag_map = sub_matches.get_one::<String>("flag_map").unwrap();
+ let flag_val = sub_matches.get_one::<String>("flag_val").unwrap();
+ let flags = list_flags(package_map, flag_map, flag_val)?;
+ for flag in flags.iter() {
+ println!("{}: {}", flag.0, flag.1);
+ }
+ }
+ _ => unreachable!(),
+ }
+ Ok(())
+}
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 28310a8..f7435b0 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -20,9 +20,10 @@
use crate::AconfigStorageError::{self, BytesParseFail};
use crate::{get_bucket_index, read_str_from_bytes, read_u32_from_bytes};
use anyhow::anyhow;
+use std::fmt;
/// Package table header struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTableHeader {
pub version: u32,
pub container: String,
@@ -32,6 +33,23 @@
pub node_offset: u32,
}
+/// Implement debug print trait for header
+impl fmt::Debug for PackageTableHeader {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Version: {}, Container: {}, File Size: {}",
+ self.version, self.container, self.file_size
+ )?;
+ writeln!(
+ f,
+ "Num of Packages: {}, Bucket Offset:{}, Node Offset: {}",
+ self.num_packages, self.bucket_offset, self.node_offset
+ )?;
+ Ok(())
+ }
+}
+
impl PackageTableHeader {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -62,7 +80,7 @@
}
/// Package table node struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTableNode {
pub package_name: String,
pub package_id: u32,
@@ -72,6 +90,18 @@
pub next_offset: Option<u32>,
}
+/// Implement debug print trait for node
+impl fmt::Debug for PackageTableNode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(
+ f,
+ "Package: {}, Id: {}, Offset: {}, Next: {:?}",
+ self.package_name, self.package_id, self.boolean_offset, self.next_offset
+ )?;
+ Ok(())
+ }
+}
+
impl PackageTableNode {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -109,13 +139,28 @@
}
/// Package table struct
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq)]
pub struct PackageTable {
pub header: PackageTableHeader,
pub buckets: Vec<Option<u32>>,
pub nodes: Vec<PackageTableNode>,
}
+/// Implement debug print trait for package table
+impl fmt::Debug for PackageTable {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ writeln!(f, "Header:")?;
+ write!(f, "{:?}", self.header)?;
+ writeln!(f, "Buckets:")?;
+ writeln!(f, "{:?}", self.buckets)?;
+ writeln!(f, "Nodes:")?;
+ for node in self.nodes.iter() {
+ write!(f, "{:?}", node)?;
+ }
+ Ok(())
+ }
+}
+
impl PackageTable {
/// Serialize to bytes
pub fn as_bytes(&self) -> Vec<u8> {
@@ -156,38 +201,7 @@
#[cfg(test)]
mod tests {
use super::*;
-
- pub fn create_test_package_table() -> PackageTable {
- let header = PackageTableHeader {
- version: crate::FILE_VERSION,
- container: String::from("system"),
- file_size: 208,
- num_packages: 3,
- bucket_offset: 30,
- node_offset: 58,
- };
- let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
- let first_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_2"),
- package_id: 1,
- boolean_offset: 3,
- next_offset: None,
- };
- let second_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_1"),
- package_id: 0,
- boolean_offset: 0,
- next_offset: Some(158),
- };
- let third_node = PackageTableNode {
- package_name: String::from("com.android.aconfig.storage.test_4"),
- package_id: 2,
- boolean_offset: 6,
- next_offset: None,
- };
- let nodes = vec![first_node, second_node, third_node];
- PackageTable { header, buckets, nodes }
- }
+ use crate::test_utils::create_test_package_table;
#[test]
// this test point locks down the table serialization
@@ -208,4 +222,15 @@
assert!(reinterpreted_table.is_ok());
assert_eq!(&package_table, &reinterpreted_table.unwrap());
}
+
+ #[test]
+ // this test point locks down that version number should be at the top of serialized
+ // bytes
+ fn test_version_number() {
+ let package_table = create_test_package_table();
+ let bytes = &package_table.as_bytes();
+ let mut head = 0;
+ let version = read_u32_from_bytes(bytes, &mut head).unwrap();
+ assert_eq!(version, 1234)
+ }
}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/protos.rs b/tools/aconfig/aconfig_storage_file/src/protos.rs
similarity index 78%
rename from tools/aconfig/aconfig_storage_read_api/src/protos.rs
rename to tools/aconfig/aconfig_storage_file/src/protos.rs
index 37df3e1..8b86205 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/protos.rs
+++ b/tools/aconfig/aconfig_storage_file/src/protos.rs
@@ -49,8 +49,11 @@
pub use auto_generated::*;
use anyhow::Result;
+use protobuf::Message;
+use std::io::Write;
+use tempfile::NamedTempFile;
-pub mod storage_files {
+pub mod storage_record_pb {
use super::*;
use anyhow::ensure;
@@ -80,15 +83,28 @@
}
Ok(())
}
+
+ pub fn get_binary_proto_from_text_proto(text_proto: &str) -> Result<Vec<u8>> {
+ let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
+ let mut binary_proto = Vec::new();
+ storage_files.write_to_vec(&mut binary_proto)?;
+ Ok(binary_proto)
+ }
+
+ pub fn write_proto_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
+ let bytes = get_binary_proto_from_text_proto(text_proto).unwrap();
+ let mut file = NamedTempFile::new()?;
+ let _ = file.write_all(&bytes);
+ Ok(file)
+ }
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::test_utils::get_binary_storage_proto_bytes;
#[test]
- fn test_parse_storage_files() {
+ fn test_parse_storage_record_pb() {
let text_proto = r#"
files {
version: 0
@@ -107,8 +123,9 @@
timestamp: 54321
}
"#;
- let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
- let storage_files = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap();
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let storage_files = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap();
assert_eq!(storage_files.files.len(), 2);
let system_file = &storage_files.files[0];
assert_eq!(system_file.version(), 0);
@@ -127,7 +144,7 @@
}
#[test]
- fn test_parse_invalid_storage_files() {
+ fn test_parse_invalid_storage_record_pb() {
let text_proto = r#"
files {
version: 0
@@ -138,8 +155,9 @@
timestamp: 12345
}
"#;
- let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
- let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
assert_eq!(
format!("{:?}", err),
"invalid storage file record: missing package map file for container system"
@@ -155,8 +173,9 @@
timestamp: 12345
}
"#;
- let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
- let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
assert_eq!(
format!("{:?}", err),
"invalid storage file record: missing flag map file for container system"
@@ -172,8 +191,9 @@
timestamp: 12345
}
"#;
- let binary_proto_bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
- let err = storage_files::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
+ let binary_proto_bytes =
+ storage_record_pb::get_binary_proto_from_text_proto(text_proto).unwrap();
+ let err = storage_record_pb::try_from_binary_proto(&binary_proto_bytes).unwrap_err();
assert_eq!(
format!("{:?}", err),
"invalid storage file record: missing flag val file for container system"
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
new file mode 100644
index 0000000..7780044
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -0,0 +1,130 @@
+/*
+ * 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::flag_table::{FlagTable, FlagTableHeader, FlagTableNode};
+use crate::flag_value::{FlagValueHeader, FlagValueList};
+use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode};
+use crate::AconfigStorageError;
+
+use anyhow::anyhow;
+use std::io::Write;
+use tempfile::NamedTempFile;
+
+pub(crate) fn create_test_package_table() -> PackageTable {
+ let header = PackageTableHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 208,
+ num_packages: 3,
+ bucket_offset: 30,
+ node_offset: 58,
+ };
+ let buckets: Vec<Option<u32>> = vec![Some(58), None, None, Some(108), None, None, None];
+ let first_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_2"),
+ package_id: 1,
+ boolean_offset: 3,
+ next_offset: None,
+ };
+ let second_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_1"),
+ package_id: 0,
+ boolean_offset: 0,
+ next_offset: Some(158),
+ };
+ let third_node = PackageTableNode {
+ package_name: String::from("com.android.aconfig.storage.test_4"),
+ package_id: 2,
+ boolean_offset: 6,
+ next_offset: None,
+ };
+ let nodes = vec![first_node, second_node, third_node];
+ PackageTable { header, buckets, nodes }
+}
+
+impl FlagTableNode {
+ // create test baseline, syntactic sugar
+ fn new_expected(
+ package_id: u32,
+ flag_name: &str,
+ flag_type: u16,
+ flag_id: u16,
+ next_offset: Option<u32>,
+ ) -> Self {
+ Self { package_id, flag_name: flag_name.to_string(), flag_type, flag_id, next_offset }
+ }
+}
+
+pub(crate) fn create_test_flag_table() -> FlagTable {
+ let header = FlagTableHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 320,
+ num_flags: 8,
+ bucket_offset: 30,
+ node_offset: 98,
+ };
+ let buckets: Vec<Option<u32>> = vec![
+ Some(98),
+ Some(124),
+ None,
+ None,
+ None,
+ Some(177),
+ None,
+ Some(203),
+ None,
+ Some(261),
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some(293),
+ None,
+ ];
+ let nodes = vec![
+ FlagTableNode::new_expected(0, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(0, "enabled_rw", 1, 2, Some(150)),
+ FlagTableNode::new_expected(1, "disabled_ro", 1, 0, None),
+ FlagTableNode::new_expected(2, "enabled_ro", 1, 1, None),
+ FlagTableNode::new_expected(1, "enabled_fixed_ro", 1, 1, Some(235)),
+ FlagTableNode::new_expected(1, "enabled_ro", 1, 2, None),
+ FlagTableNode::new_expected(2, "enabled_fixed_ro", 1, 0, None),
+ FlagTableNode::new_expected(0, "disabled_rw", 1, 0, None),
+ ];
+ FlagTable { header, buckets, nodes }
+}
+
+pub(crate) fn create_test_flag_value_list() -> FlagValueList {
+ let header = FlagValueHeader {
+ version: 1234,
+ container: String::from("system"),
+ file_size: 34,
+ num_flags: 8,
+ boolean_value_offset: 26,
+ };
+ let booleans: Vec<bool> = vec![false, true, false, false, true, true, false, true];
+ FlagValueList { header, booleans }
+}
+
+pub(crate) fn write_bytes_to_temp_file(bytes: &[u8]) -> Result<NamedTempFile, AconfigStorageError> {
+ let mut file = NamedTempFile::new().map_err(|_| {
+ AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
+ })?;
+ let _ = file.write_all(&bytes);
+ Ok(file)
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index 43697b3..721f9a5 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -9,9 +9,7 @@
srcs: ["src/lib.rs"],
rustlibs: [
"libanyhow",
- "libaconfig_storage_protos",
"libonce_cell",
- "libprotobuf",
"libtempfile",
"libmemmap2",
"libcxx",
@@ -38,49 +36,6 @@
],
}
-rust_protobuf {
- name: "libaconfig_storage_protos",
- protos: ["protos/aconfig_storage_metadata.proto"],
- crate_name: "aconfig_storage_protos",
- source_stem: "aconfig_storage_protos",
- host_supported: true,
-}
-
-cc_library_static {
- name: "libaconfig_storage_protos_cc",
- proto: {
- export_proto_headers: true,
- type: "lite",
- },
- srcs: ["protos/aconfig_storage_metadata.proto"],
- apex_available: [
- "//apex_available:platform",
- "//apex_available:anyapex",
- ],
- host_supported: true,
-}
-
-genrule {
- name: "ro.package.map",
- out: ["tests/tmp.ro.package.map"],
- srcs: ["tests/package.map"],
- cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
-}
-
-genrule {
- name: "ro.flag.map",
- out: ["tests/tmp.ro.flag.map"],
- srcs: ["tests/flag.map"],
- cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
-}
-
-genrule {
- name: "ro.flag.val",
- out: ["tests/tmp.ro.flag.val"],
- srcs: ["tests/flag.val"],
- cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
-}
-
// cxx source codegen from rust api
genrule {
name: "libcxx_aconfig_storage_read_api_bridge_code",
@@ -119,3 +74,24 @@
whole_static_libs: ["libaconfig_storage_read_api_cxx_bridge"],
export_include_dirs: ["include"],
}
+
+genrule {
+ name: "ro.package.map",
+ out: ["tests/tmp.ro.package.map"],
+ srcs: ["tests/package.map"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
+
+genrule {
+ name: "ro.flag.map",
+ out: ["tests/tmp.ro.flag.map"],
+ srcs: ["tests/flag.map"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
+
+genrule {
+ name: "ro.flag.val",
+ out: ["tests/tmp.ro.flag.val"],
+ srcs: ["tests/flag.val"],
+ cmd: "rm -f $(out);cp -f $(in) $(out);chmod -w $(out)",
+}
diff --git a/tools/aconfig/aconfig_storage_read_api/Cargo.toml b/tools/aconfig/aconfig_storage_read_api/Cargo.toml
index 4c65d40..30a4298 100644
--- a/tools/aconfig/aconfig_storage_read_api/Cargo.toml
+++ b/tools/aconfig/aconfig_storage_read_api/Cargo.toml
@@ -10,7 +10,6 @@
[dependencies]
anyhow = "1.0.69"
memmap2 = "0.8.0"
-protobuf = "3.2.0"
once_cell = "1.19.0"
tempfile = "3.9.0"
cxx = "1.0"
diff --git a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
index 7cf8e38..c2da5ce 100644
--- a/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/aconfig_storage_read_api.cpp
@@ -4,12 +4,23 @@
#include "aconfig_storage/lib.rs.h"
namespace aconfig_storage {
+/// Get storage file version number
+VersionNumberQuery get_storage_file_version(
+ std::string const& file_path) {
+ auto version_cxx = get_storage_file_version_cxx(
+ rust::Str(file_path.c_str()));
+ auto version = VersionNumberQuery();
+ version.query_success = version_cxx.query_success;
+ version.error_message = std::string(version_cxx.error_message.c_str());
+ version.version_number = version_cxx.version_number;
+ return version;
+}
/// Get package offset
PackageOffsetQuery get_package_offset(
std::string const& container,
std::string const& package) {
- auto offset_cxx = get_package_offset_cxx(
+ auto offset_cxx = get_package_offset_cxx(
rust::Str(container.c_str()),
rust::Str(package.c_str()));
auto offset = PackageOffsetQuery();
@@ -26,7 +37,7 @@
std::string const& container,
uint32_t package_id,
std::string const& flag_name) {
- auto offset_cxx = get_flag_offset_cxx(
+ auto offset_cxx = get_flag_offset_cxx(
rust::Str(container.c_str()),
package_id,
rust::Str(flag_name.c_str()));
@@ -42,7 +53,7 @@
BooleanFlagValueQuery get_boolean_flag_value(
std::string const& container,
uint32_t offset) {
- auto value_cxx = get_boolean_flag_value_cxx(
+ auto value_cxx = get_boolean_flag_value_cxx(
rust::Str(container.c_str()),
offset);
auto value = BooleanFlagValueQuery();
diff --git a/tools/aconfig/aconfig_storage_read_api/build.rs b/tools/aconfig/aconfig_storage_read_api/build.rs
index 894b71c..7b1aa53 100644
--- a/tools/aconfig/aconfig_storage_read_api/build.rs
+++ b/tools/aconfig/aconfig_storage_read_api/build.rs
@@ -1,20 +1,4 @@
-use protobuf_codegen::Codegen;
-
fn main() {
- let proto_files = vec!["protos/aconfig_storage_metadata.proto"];
-
- // tell cargo to only re-run the build script if any of the proto files has changed
- for path in &proto_files {
- println!("cargo:rerun-if-changed={}", path);
- }
-
- Codegen::new()
- .pure()
- .include("protos")
- .inputs(proto_files)
- .cargo_out_dir("aconfig_storage_protos")
- .run_from_script();
-
let _ = cxx_build::bridge("src/lib.rs");
println!("cargo:rerun-if-changed=src/lib.rs");
}
diff --git a/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
index 636fb7e..3741dc2 100644
--- a/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
+++ b/tools/aconfig/aconfig_storage_read_api/include/aconfig_storage/aconfig_storage_read_api.hpp
@@ -5,6 +5,13 @@
namespace aconfig_storage {
+/// Storage version number query result
+struct VersionNumberQuery {
+ bool query_success;
+ std::string error_message;
+ uint32_t version_number;
+};
+
/// Package offset query result
struct PackageOffsetQuery {
bool query_success;
@@ -29,6 +36,12 @@
bool flag_value;
};
+/// Get storage file version number
+/// \input file_path: the path to the storage file
+/// \returns a VersionNumberQuery
+VersionNumberQuery get_storage_file_version(
+ std::string const& file_path);
+
/// Get package offset
/// \input container: the flag container name
/// \input package: the flag package name
diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
index 996256b..3958b2e 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -15,8 +15,7 @@
*/
//! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage
-//! files. It provides three apis to
-//! interface with storage files:
+//! files. It provides four apis to interface with storage files:
//!
//! 1, function to get package flag value start offset
//! pub fn get_package_offset(container: &str, package: &str) -> `Result<Option<PackageOffset>>>`
@@ -28,6 +27,9 @@
//! flag offset).
//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
//!
+//! 4, function to get storage file version without mmapping the file.
+//! pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError>
+//!
//! Note these are low level apis that are expected to be only used in auto generated flag
//! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis
//! please refer to the g3doc go/android-flags
@@ -36,13 +38,14 @@
pub mod flag_value_query;
pub mod mapped_file;
pub mod package_table_query;
-pub mod protos;
#[cfg(test)]
mod test_utils;
-pub use crate::protos::ProtoStorageFiles;
-pub use aconfig_storage_file::{AconfigStorageError, StorageFileSelection, FILE_VERSION};
+pub use aconfig_storage_file::{
+ protos::ProtoStorageFiles, read_u32_from_bytes, AconfigStorageError, StorageFileSelection,
+ FILE_VERSION,
+};
pub use flag_table_query::FlagOffset;
pub use package_table_query::PackageOffset;
@@ -51,6 +54,10 @@
use mapped_file::get_mapped_file;
use package_table_query::find_package_offset;
+use anyhow::anyhow;
+use std::fs::File;
+use std::io::Read;
+
/// Storage file location pb file
pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/available_storage_file_records.pb";
@@ -85,6 +92,28 @@
find_boolean_flag_value(&mapped_file, offset)
}
+/// Get storage file version number
+///
+/// This function would read the first four bytes of the file and interpret it as the
+/// version number of the file. There are unit tests in aconfig_storage_file crate to
+/// lock down that for all storage files, the first four bytes will be the version
+/// number of the storage file
+pub fn get_storage_file_version(file_path: &str) -> Result<u32, AconfigStorageError> {
+ let mut file = File::open(file_path).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+ })?;
+ let mut buffer = [0; 4];
+ file.read(&mut buffer).map_err(|errmsg| {
+ AconfigStorageError::FileReadFail(anyhow!(
+ "Failed to read 4 bytes from file {}: {}",
+ file_path,
+ errmsg
+ ))
+ })?;
+ let mut head = 0;
+ read_u32_from_bytes(&buffer, &mut head)
+}
+
/// Get package start offset for flags given the container and package name.
///
/// This function would map the corresponding package map file if has not been mapped yet,
@@ -127,8 +156,20 @@
get_boolean_flag_value_impl(STORAGE_LOCATION_FILE, container, offset)
}
+// *************************************** //
+// CC INTERLOP
+// *************************************** //
+
+// Exported rust data structure and methods, c++ code will be generated
#[cxx::bridge]
mod ffi {
+ // Storage file version query return for cc interlop
+ pub struct VersionNumberQueryCXX {
+ pub query_success: bool,
+ pub error_message: String,
+ pub version_number: u32,
+ }
+
// Package table query return for cc interlop
pub struct PackageOffsetQueryCXX {
pub query_success: bool,
@@ -174,6 +215,8 @@
offset: u32,
) -> BooleanFlagValueQueryCXX;
+ pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;
+
pub fn get_package_offset_cxx(container: &str, package: &str) -> PackageOffsetQueryCXX;
pub fn get_flag_offset_cxx(
@@ -187,6 +230,100 @@
}
}
+/// Implement the package offset interlop return type, create from actual package offset api return type
+impl ffi::PackageOffsetQueryCXX {
+ pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
+ match offset_result {
+ Ok(offset_opt) => match offset_opt {
+ Some(offset) => Self {
+ query_success: true,
+ error_message: String::from(""),
+ package_exists: true,
+ package_id: offset.package_id,
+ boolean_offset: offset.boolean_offset,
+ },
+ None => Self {
+ query_success: true,
+ error_message: String::from(""),
+ package_exists: false,
+ package_id: 0,
+ boolean_offset: 0,
+ },
+ },
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ package_exists: false,
+ package_id: 0,
+ boolean_offset: 0,
+ },
+ }
+ }
+}
+
+/// Implement the flag offset interlop return type, create from actual flag offset api return type
+impl ffi::FlagOffsetQueryCXX {
+ pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
+ match offset_result {
+ Ok(offset_opt) => match offset_opt {
+ Some(offset) => Self {
+ query_success: true,
+ error_message: String::from(""),
+ flag_exists: true,
+ flag_offset: offset,
+ },
+ None => Self {
+ query_success: true,
+ error_message: String::from(""),
+ flag_exists: false,
+ flag_offset: 0,
+ },
+ },
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ flag_exists: false,
+ flag_offset: 0,
+ },
+ }
+ }
+}
+
+/// Implement the flag value interlop return type, create from actual flag value api return type
+impl ffi::BooleanFlagValueQueryCXX {
+ pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
+ match value_result {
+ Ok(value) => {
+ Self { query_success: true, error_message: String::from(""), flag_value: value }
+ }
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ flag_value: false,
+ },
+ }
+ }
+}
+
+/// Implement the storage version number interlop return type, create from actual version number
+/// api return type
+impl ffi::VersionNumberQueryCXX {
+ pub(crate) fn new(version_result: Result<u32, AconfigStorageError>) -> Self {
+ match version_result {
+ Ok(version) => Self {
+ query_success: true,
+ error_message: String::from(""),
+ version_number: version,
+ },
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ version_number: 0,
+ },
+ }
+ }
+}
+
/// Get package start offset impl cc interlop
pub fn get_package_offset_cxx_impl(
pb_file: &str,
@@ -215,6 +352,11 @@
ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value_impl(pb_file, container, offset))
}
+/// Get storage version number cc interlop
+pub fn get_storage_file_version_cxx(file_path: &str) -> ffi::VersionNumberQueryCXX {
+ ffi::VersionNumberQueryCXX::new(get_storage_file_version(file_path))
+}
+
/// Get package start offset cc interlop
pub fn get_package_offset_cxx(container: &str, package: &str) -> ffi::PackageOffsetQueryCXX {
ffi::PackageOffsetQueryCXX::new(get_package_offset(container, package))
@@ -234,82 +376,11 @@
ffi::BooleanFlagValueQueryCXX::new(get_boolean_flag_value(container, offset))
}
-impl ffi::PackageOffsetQueryCXX {
- pub(crate) fn new(offset_result: Result<Option<PackageOffset>, AconfigStorageError>) -> Self {
- match offset_result {
- Ok(offset_opt) => match offset_opt {
- Some(offset) => Self {
- query_success: true,
- error_message: String::from(""),
- package_exists: true,
- package_id: offset.package_id,
- boolean_offset: offset.boolean_offset,
- },
- None => Self {
- query_success: true,
- error_message: String::from(""),
- package_exists: false,
- package_id: 0,
- boolean_offset: 0,
- },
- },
- Err(errmsg) => Self {
- query_success: false,
- error_message: format!("{:?}", errmsg),
- package_exists: false,
- package_id: 0,
- boolean_offset: 0,
- },
- }
- }
-}
-
-impl ffi::FlagOffsetQueryCXX {
- pub(crate) fn new(offset_result: Result<Option<FlagOffset>, AconfigStorageError>) -> Self {
- match offset_result {
- Ok(offset_opt) => match offset_opt {
- Some(offset) => Self {
- query_success: true,
- error_message: String::from(""),
- flag_exists: true,
- flag_offset: offset,
- },
- None => Self {
- query_success: true,
- error_message: String::from(""),
- flag_exists: false,
- flag_offset: 0,
- },
- },
- Err(errmsg) => Self {
- query_success: false,
- error_message: format!("{:?}", errmsg),
- flag_exists: false,
- flag_offset: 0,
- },
- }
- }
-}
-
-impl ffi::BooleanFlagValueQueryCXX {
- pub(crate) fn new(value_result: Result<bool, AconfigStorageError>) -> Self {
- match value_result {
- Ok(value) => {
- Self { query_success: true, error_message: String::from(""), flag_value: value }
- }
- Err(errmsg) => Self {
- query_success: false,
- error_message: format!("{:?}", errmsg),
- flag_value: false,
- },
- }
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
- use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
+ use crate::test_utils::TestStorageFileSet;
+ use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
fn create_test_storage_files(read_only: bool) -> TestStorageFileSet {
TestStorageFileSet::new(
@@ -339,7 +410,7 @@
ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let package_offset = get_package_offset_impl(
&file_full_path,
@@ -390,7 +461,7 @@
ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let baseline = vec![
(0, "enabled_ro", 1u16),
@@ -429,7 +500,7 @@
ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let baseline: Vec<bool> = vec![false; 8];
for (offset, expected_value) in baseline.into_iter().enumerate() {
@@ -438,4 +509,13 @@
assert_eq!(flag_value, expected_value);
}
}
+
+ #[test]
+ // this test point locks down flag storage file version number query api
+ fn test_storage_version_query() {
+ let _ro_files = create_test_storage_files(true);
+ assert_eq!(get_storage_file_version("./tests/package.map").unwrap(), 1);
+ assert_eq!(get_storage_file_version("./tests/flag.map").unwrap(), 1);
+ assert_eq!(get_storage_file_version("./tests/flag.val").unwrap(), 1);
+ }
}
diff --git a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
index d8f2570..e94c56f 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -23,13 +23,13 @@
use memmap2::Mmap;
use once_cell::sync::Lazy;
-use crate::protos::{
- storage_files::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
-};
use crate::AconfigStorageError::{
self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
};
use crate::StorageFileSelection;
+use aconfig_storage_file::protos::{
+ storage_record_pb::try_from_binary_proto, ProtoStorageFileInfo, ProtoStorageFiles,
+};
/// Cache for already mapped files
static ALL_MAPPED_FILES: Lazy<Mutex<HashMap<String, MappedStorageFileSet>>> = Lazy::new(|| {
@@ -148,7 +148,8 @@
#[cfg(test)]
mod tests {
use super::*;
- use crate::test_utils::{write_storage_text_to_temp_file, TestStorageFileSet};
+ use crate::test_utils::TestStorageFileSet;
+ use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
#[test]
fn test_find_storage_file_location() {
@@ -170,7 +171,7 @@
timestamp: 54321
}
"#;
- let file = write_storage_text_to_temp_file(text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let file_info = find_container_storage_location(&file_full_path, "system").unwrap();
assert_eq!(file_info.version(), 0);
@@ -235,7 +236,7 @@
ro_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
map_and_verify(
&file_full_path,
@@ -264,7 +265,7 @@
rw_files.package_map.name, ro_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
assert_eq!(
@@ -289,7 +290,7 @@
ro_files.package_map.name, rw_files.flag_map.name, ro_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
assert_eq!(
@@ -314,7 +315,7 @@
ro_files.package_map.name, ro_files.flag_map.name, rw_files.flag_val.name
);
- let file = write_storage_text_to_temp_file(&text_proto).unwrap();
+ let file = write_proto_to_temp_file(&text_proto).unwrap();
let file_full_path = file.path().display().to_string();
let error = map_container_storage_files(&file_full_path, "system").unwrap_err();
assert_eq!(
diff --git a/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
index 7905d51..cc5938d 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/test_utils.rs
@@ -14,27 +14,10 @@
* limitations under the License.
*/
-use crate::protos::ProtoStorageFiles;
use anyhow::Result;
-use protobuf::Message;
use std::fs;
-use std::io::Write;
use tempfile::NamedTempFile;
-pub(crate) fn get_binary_storage_proto_bytes(text_proto: &str) -> Result<Vec<u8>> {
- let storage_files: ProtoStorageFiles = protobuf::text_format::parse_from_str(text_proto)?;
- let mut binary_proto = Vec::new();
- storage_files.write_to_vec(&mut binary_proto)?;
- Ok(binary_proto)
-}
-
-pub(crate) fn write_storage_text_to_temp_file(text_proto: &str) -> Result<NamedTempFile> {
- let bytes = get_binary_storage_proto_bytes(text_proto).unwrap();
- let mut file = NamedTempFile::new()?;
- let _ = file.write_all(&bytes);
- Ok(file)
-}
-
fn set_file_read_only(file: &NamedTempFile) {
let mut perms = fs::metadata(file.path()).unwrap().permissions();
if !perms.readonly() {
@@ -51,6 +34,7 @@
}
}
+#[allow(dead_code)]
pub(crate) struct TestStorageFile {
pub file: NamedTempFile,
pub name: String,
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
index b605646..6fecd08 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.cpp
@@ -27,6 +27,7 @@
using ::aconfig_storage::test_only_api::get_package_offset_impl;
using ::aconfig_storage::test_only_api::get_flag_offset_impl;
using ::aconfig_storage::test_only_api::get_boolean_flag_value_impl;
+using ::aconfig_storage::get_storage_file_version;
void write_storage_location_pb_to_file(std::string const& file_path) {
auto const test_dir = android::base::GetExecutableDirectory();
@@ -45,6 +46,22 @@
<< "Failed to write a file: " << file_path;
}
+TEST(AconfigStorageTest, test_storage_version_query) {
+ auto const test_dir = android::base::GetExecutableDirectory();
+ auto query = get_storage_file_version(test_dir + "/tests/tmp.ro.package.map");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_EQ(query.version_number, 1);
+ query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.map");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_EQ(query.version_number, 1);
+ query = get_storage_file_version(test_dir + "/tests/tmp.ro.flag.val");
+ ASSERT_EQ(query.error_message, std::string());
+ ASSERT_TRUE(query.query_success);
+ ASSERT_EQ(query.version_number, 1);
+}
+
TEST(AconfigStorageTest, test_package_offset_query) {
auto pb_file = std::string("/tmp/test_package_offset_query.pb");
write_storage_location_pb_to_file(pb_file);
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
index 9b23ec4..4a65876 100644
--- a/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
+++ b/tools/aconfig/aconfig_storage_read_api/tests/storage_read_api_test.rs
@@ -1,8 +1,8 @@
#[cfg(not(feature = "cargo"))]
mod aconfig_storage_rust_test {
use aconfig_storage_read_api::{
- get_boolean_flag_value_impl, get_flag_offset_impl, get_package_offset_impl, PackageOffset,
- ProtoStorageFiles,
+ get_boolean_flag_value_impl, get_flag_offset_impl, get_package_offset_impl,
+ get_storage_file_version, PackageOffset, ProtoStorageFiles,
};
use protobuf::Message;
use std::io::Write;
@@ -171,4 +171,11 @@
"InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"
);
}
+
+ #[test]
+ fn test_storage_version_query() {
+ assert_eq!(get_storage_file_version("./tests/tmp.ro.package.map").unwrap(), 1);
+ assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.map").unwrap(), 1);
+ assert_eq!(get_storage_file_version("./tests/tmp.ro.flag.val").unwrap(), 1);
+ }
}