From 2ac7a4c2bd98787d7f21650aaedba274681980aa Mon Sep 17 00:00:00 2001 From: Dennis Shen Date: Sun, 7 Apr 2024 19:23:09 +0000 Subject: [PATCH] aconfig: add flag info read rust api Bug: b/321077378 Test: atest aconfig_storage_read_api.test; atest aconfig_storage_read_api.test.rust Change-Id: I382ac0145c5d91827952b3ddb01cabefd1539854 --- .../aconfig_storage_file/src/flag_info.rs | 12 +- tools/aconfig/aconfig_storage_file/src/lib.rs | 2 +- .../aconfig_storage_read_api/Android.bp | 1 + .../src/flag_info_query.rs | 115 ++++++++++++++++++ .../src/flag_value_query.rs | 17 +-- .../aconfig_storage_read_api/src/lib.rs | 49 ++++++-- .../src/mapped_file.rs | 4 +- .../aconfig_storage_read_api/tests/Android.bp | 2 + .../aconfig_storage_read_api/tests/flag.info | Bin 0 -> 35 bytes .../tests/storage_read_api_test.rs | 62 ++++++++-- 10 files changed, 219 insertions(+), 45 deletions(-) create mode 100644 tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs create mode 100644 tools/aconfig/aconfig_storage_read_api/tests/flag.info diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs index 3fff2637fa..dc2a8d6d0d 100644 --- a/tools/aconfig/aconfig_storage_file/src/flag_info.rs +++ b/tools/aconfig/aconfig_storage_file/src/flag_info.rs @@ -91,9 +91,9 @@ impl FlagInfoHeader { /// bit field for flag info #[derive(Clone, Debug, PartialEq, Eq)] pub enum FlagInfoBit { - IsSticky = 0, - IsReadWrite = 1, - HasOverride = 2, + IsSticky = 1 << 0, + IsReadWrite = 1 << 1, + HasOverride = 1 << 2, } /// Flag info node struct @@ -108,9 +108,9 @@ impl fmt::Debug for FlagInfoNode { writeln!( f, "sticky: {}, readwrite: {}, override: {}", - self.attributes & (FlagInfoBit::IsSticky as u8), - self.attributes & (FlagInfoBit::IsReadWrite as u8), - self.attributes & (FlagInfoBit::HasOverride as u8), + self.attributes & (FlagInfoBit::IsSticky as u8) != 0, + self.attributes & (FlagInfoBit::IsReadWrite as u8) != 0, + self.attributes & (FlagInfoBit::HasOverride as u8) != 0, )?; Ok(()) } diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs index d14bab6edd..5b8758c9a7 100644 --- a/tools/aconfig/aconfig_storage_file/src/lib.rs +++ b/tools/aconfig/aconfig_storage_file/src/lib.rs @@ -46,7 +46,7 @@ use std::fs::File; use std::hash::{Hash, Hasher}; use std::io::Read; -pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode}; +pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagInfoBit}; pub use crate::flag_table::{FlagTable, FlagTableHeader, FlagTableNode}; pub use crate::flag_value::{FlagValueHeader, FlagValueList}; pub use crate::package_table::{PackageTable, PackageTableHeader, PackageTableNode}; diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp index b252e9df0b..3746d1731a 100644 --- a/tools/aconfig/aconfig_storage_read_api/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/Android.bp @@ -38,6 +38,7 @@ rust_test_host { "tests/package.map", "tests/flag.map", "tests/flag.val", + "tests/flag.info", ], } diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs new file mode 100644 index 0000000000..2f8c536005 --- /dev/null +++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs @@ -0,0 +1,115 @@ +/* + * 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. + */ + +//! flag value query module defines the flag value file read from mapped bytes + +use crate::{AconfigStorageError, FILE_VERSION}; +use aconfig_storage_file::{flag_info::FlagInfoHeader, read_u8_from_bytes}; +use anyhow::anyhow; + +/// Get flag attribute bitfield +pub fn find_boolean_flag_attribute( + buf: &[u8], + flag_offset: u32, +) -> Result { + let interpreted_header = FlagInfoHeader::from_bytes(buf)?; + if interpreted_header.version > crate::FILE_VERSION { + return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!( + "Cannot read storage file with a higher version of {} with lib version {}", + interpreted_header.version, + FILE_VERSION + ))); + } + + let mut head = (interpreted_header.boolean_flag_offset + flag_offset) as usize; + + if head >= interpreted_header.file_size as usize { + return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!( + "Flag info offset goes beyond the end of the file." + ))); + } + + let val = read_u8_from_bytes(buf, &mut head)?; + Ok(val) +} + +#[cfg(test)] +mod tests { + use super::*; + use aconfig_storage_file::{test_utils::create_test_flag_info_list, FlagInfoBit}; + + #[test] + // this test point locks down query if flag is sticky + fn test_is_flag_sticky() { + let flag_info_list = create_test_flag_info_list().into_bytes(); + for offset in 0..8 { + let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap(); + assert_eq!((attribute & FlagInfoBit::IsSticky as u8) != 0u8, false); + } + } + + #[test] + // this test point locks down query if flag is readwrite + fn test_is_flag_readwrite() { + let flag_info_list = create_test_flag_info_list().into_bytes(); + let baseline: Vec = vec![true, false, true, false, false, false, false, false]; + for offset in 0..8 { + let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap(); + assert_eq!( + (attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, + baseline[offset as usize] + ); + } + } + + #[test] + // this test point locks down query if flag has override + fn test_flag_has_override() { + let flag_info_list = create_test_flag_info_list().into_bytes(); + for offset in 0..8 { + let attribute = find_boolean_flag_attribute(&flag_info_list[..], offset).unwrap(); + assert_eq!((attribute & FlagInfoBit::HasOverride as u8) != 0u8, false); + } + } + + #[test] + // this test point locks down query beyond the end of boolean section + fn test_boolean_out_of_range() { + let flag_info_list = create_test_flag_info_list().into_bytes(); + let error = find_boolean_flag_attribute(&flag_info_list[..], 8).unwrap_err(); + assert_eq!( + format!("{:?}", error), + "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)" + ); + } + + #[test] + // this test point locks down query error when file has a higher version + fn test_higher_version_storage_file() { + let mut info_list = create_test_flag_info_list(); + info_list.header.version = crate::FILE_VERSION + 1; + let flag_info = info_list.into_bytes(); + let error = find_boolean_flag_attribute(&flag_info[..], 4).unwrap_err(); + assert_eq!( + format!("{:?}", error), + format!( + "HigherStorageFileVersion(Cannot read storage file with a higher version of {} with lib version {})", + crate::FILE_VERSION + 1, + crate::FILE_VERSION + ) + ); + } +} diff --git a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs index 964cd69159..7324f438d0 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/flag_value_query.rs @@ -48,26 +48,13 @@ pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result FlagValueList { - let header = FlagValueHeader { - version: crate::FILE_VERSION, - container: String::from("system"), - file_type: StorageFileType::FlagVal as u8, - file_size: 35, - num_flags: 8, - boolean_value_offset: 27, - }; - let booleans: Vec = vec![false, true, false, false, true, true, false, true]; - FlagValueList { header, booleans } - } + use aconfig_storage_file::test_utils::create_test_flag_value_list; #[test] // this test point locks down flag value query fn test_flag_value_query() { let flag_value_list = create_test_flag_value_list().into_bytes(); - let baseline: Vec = vec![false, true, false, false, true, true, false, true]; + let baseline: Vec = vec![false, true, true, false, true, true, true, true]; for (offset, expected_value) in baseline.into_iter().enumerate() { let flag_value = find_boolean_flag_value(&flag_value_list[..], offset as u32).unwrap(); assert_eq!(flag_value, expected_value); diff --git a/tools/aconfig/aconfig_storage_read_api/src/lib.rs b/tools/aconfig/aconfig_storage_read_api/src/lib.rs index da64cb7e1e..0e0d3ac07d 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs @@ -34,6 +34,7 @@ //! apis. DO NOT DIRECTLY USE THESE APIS IN YOUR SOURCE CODE. For auto generated flag apis //! please refer to the g3doc go/android-flags +pub mod flag_info_query; pub mod flag_table_query; pub mod flag_value_query; pub mod mapped_file; @@ -47,6 +48,7 @@ pub use flag_table_query::FlagOffset; pub use package_table_query::PackageOffset; use aconfig_storage_file::{read_u32_from_bytes, FILE_VERSION}; +use flag_info_query::find_boolean_flag_attribute; use flag_table_query::find_flag_offset; use flag_value_query::find_boolean_flag_value; use package_table_query::find_package_offset; @@ -145,6 +147,18 @@ pub fn get_storage_file_version(file_path: &str) -> Result Result { + find_boolean_flag_attribute(file, offset) +} + // *************************************** // // CC INTERLOP // *************************************** // @@ -315,12 +329,14 @@ mod tests { use crate::mapped_file::get_mapped_file; use crate::test_utils::copy_to_temp_file; use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file; + use aconfig_storage_file::FlagInfoBit; use tempfile::NamedTempFile; - fn create_test_storage_files() -> [NamedTempFile; 4] { + fn create_test_storage_files() -> [NamedTempFile; 5] { let package_map = copy_to_temp_file("./tests/package.map").unwrap(); let flag_map = copy_to_temp_file("./tests/flag.map").unwrap(); let flag_val = copy_to_temp_file("./tests/flag.val").unwrap(); + let flag_info = copy_to_temp_file("./tests/flag.info").unwrap(); let text_proto = format!( r#" @@ -330,21 +346,23 @@ files {{ package_map: "{}" flag_map: "{}" flag_val: "{}" + flag_info: "{}" timestamp: 12345 }} "#, package_map.path().display(), flag_map.path().display(), - flag_val.path().display() + flag_val.path().display(), + flag_info.path().display() ); let pb_file = write_proto_to_temp_file(&text_proto).unwrap(); - [package_map, flag_map, flag_val, pb_file] + [package_map, flag_map, flag_val, flag_info, pb_file] } #[test] // this test point locks down flag package offset query fn test_package_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); let package_mapped_file = unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap() @@ -375,7 +393,7 @@ files {{ #[test] // this test point locks down flag offset query fn test_flag_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); let flag_mapped_file = unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() }; @@ -398,9 +416,9 @@ files {{ } #[test] - // this test point locks down flag offset query + // this test point locks down flag value query fn test_flag_value_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); let flag_value_file = unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagVal).unwrap() }; @@ -411,11 +429,28 @@ files {{ } } + #[test] + // this test point locks donw flag info query + fn test_flag_info_query() { + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); + let pb_file_path = pb_file.path().display().to_string(); + let flag_info_file = + unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() }; + let is_rw: Vec = vec![true, false, true, false, false, false, false, false]; + for (offset, expected_value) in is_rw.into_iter().enumerate() { + let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap(); + assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8); + assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value); + assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8); + } + } + #[test] // this test point locks down flag storage file version number query api fn test_storage_version_query() { 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); + assert_eq!(get_storage_file_version("./tests/flag.info").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 51354db760..9cc90ee4e6 100644 --- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs +++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs @@ -91,9 +91,7 @@ pub unsafe fn get_mapped_file( StorageFileType::PackageMap => unsafe { map_file(files_location.package_map()) }, StorageFileType::FlagMap => unsafe { map_file(files_location.flag_map()) }, StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) }, - StorageFileType::FlagInfo => { - Err(MapFileFail(anyhow!("TODO: add support for flag info file"))) - } + StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) }, } } diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp index d9cf238ff7..6b05ca6fb1 100644 --- a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp +++ b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp @@ -14,6 +14,7 @@ rust_test { "package.map", "flag.map", "flag.val", + "flag.info", ], test_suites: ["general-tests"], } @@ -35,6 +36,7 @@ cc_test { "package.map", "flag.map", "flag.val", + "flag.info", ], test_suites: [ "device-tests", diff --git a/tools/aconfig/aconfig_storage_read_api/tests/flag.info b/tools/aconfig/aconfig_storage_read_api/tests/flag.info new file mode 100644 index 0000000000000000000000000000000000000000..820d8393633a9cc00b67edad843294b5c0826844 GIT binary patch literal 35 gcmZQ%U|?Va;@te??9u{eWgwFSh^2v;2?#*|07P~I [NamedTempFile; 4] { + fn create_test_storage_files() -> [NamedTempFile; 5] { let package_map = copy_to_temp_file("./package.map"); let flag_map = copy_to_temp_file("./flag.map"); let flag_val = copy_to_temp_file("./flag.val"); + let flag_info = copy_to_temp_file("./flag.info"); let text_proto = format!( r#" @@ -28,20 +29,22 @@ files {{ package_map: "{}" flag_map: "{}" flag_val: "{}" + flag_info: "{}" timestamp: 12345 }} "#, package_map.path().display(), flag_map.path().display(), - flag_val.path().display() + flag_val.path().display(), + flag_info.path().display() ); let pb_file = write_proto_to_temp_file(&text_proto).unwrap(); - [package_map, flag_map, flag_val, pb_file] + [package_map, flag_map, flag_val, flag_info, pb_file] } #[test] fn test_unavailable_stoarge() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -56,7 +59,7 @@ files {{ #[test] fn test_package_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -88,7 +91,7 @@ files {{ #[test] fn test_none_exist_package_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -103,7 +106,7 @@ files {{ #[test] fn test_flag_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -129,7 +132,7 @@ files {{ #[test] fn test_none_exist_flag_offset_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -144,7 +147,7 @@ files {{ #[test] fn test_boolean_flag_value_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -159,7 +162,7 @@ files {{ #[test] fn test_invalid_boolean_flag_value_query() { - let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files(); + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); let pb_file_path = pb_file.path().display().to_string(); // SAFETY: // The safety here is ensured as the test process will not write to temp storage file @@ -172,10 +175,43 @@ files {{ ); } + #[test] + fn test_flag_info_query() { + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); + let pb_file_path = pb_file.path().display().to_string(); + // SAFETY: + // The safety here is ensured as the test process will not write to temp storage file + let flag_info_file = + unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() }; + let is_rw: Vec = vec![true, false, true, false, false, false, false, false]; + for (offset, expected_value) in is_rw.into_iter().enumerate() { + let attribute = get_boolean_flag_attribute(&flag_info_file, offset as u32).unwrap(); + assert!((attribute & FlagInfoBit::IsSticky as u8) == 0u8); + assert_eq!((attribute & FlagInfoBit::IsReadWrite as u8) != 0u8, expected_value); + assert!((attribute & FlagInfoBit::HasOverride as u8) == 0u8); + } + } + + #[test] + fn test_invalid_boolean_flag_info_query() { + let [_package_map, _flag_map, _flag_val, _flag_info, pb_file] = create_test_storage_files(); + let pb_file_path = pb_file.path().display().to_string(); + // SAFETY: + // The safety here is ensured as the test process will not write to temp storage file + let flag_info_file = + unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() }; + let err = get_boolean_flag_attribute(&flag_info_file, 8u32).unwrap_err(); + assert_eq!( + format!("{:?}", err), + "InvalidStorageFileOffset(Flag info offset goes beyond the end of the file.)" + ); + } + #[test] fn test_storage_version_query() { assert_eq!(get_storage_file_version("./package.map").unwrap(), 1); assert_eq!(get_storage_file_version("./flag.map").unwrap(), 1); assert_eq!(get_storage_file_version("./flag.val").unwrap(), 1); + assert_eq!(get_storage_file_version("./flag.info").unwrap(), 1); } }