Merge "Revert "Expose RELEASE_AVF_SUPPORT_CUSTOM_VM_WITH_PARAVIRTUALIZED_DEVICES"" into main
diff --git a/core/Makefile b/core/Makefile
index 9d77ec1..b245d32 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1469,8 +1469,13 @@
boototapackage_4k: $(BUILT_BOOT_OTA_PACKAGE_4K)
.PHONY: boototapackage_4k
+ifeq ($(BOARD_16K_OTA_MOVE_VENDOR),true)
+$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_4k.zip))
+$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT_VENDOR)/boot_otas/boot_ota_16k.zip))
+else
$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_4K),$(TARGET_OUT)/boot_otas/boot_ota_4k.zip))
$(eval $(call copy-one-file,$(BUILT_BOOT_OTA_PACKAGE_16K),$(TARGET_OUT)/boot_otas/boot_ota_16k.zip))
+endif # BOARD_16K_OTA_MOVE_VENDOR == true
ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_4k.zip
ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT)/boot_otas/boot_ota_16k.zip
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 29fb7aa..70b3744 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -44,28 +44,9 @@
BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := false
endif
-# ART does not provide linux_bionic variants needed for products that
-# set HOST_CROSS_OS=linux_bionic.
-ifeq (linux_bionic,${HOST_CROSS_OS})
- BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
-# ART does not provide host side arm64 variants needed for products that
-# set HOST_CROSS_ARCH=arm64.
-ifeq (arm64,${HOST_CROSS_ARCH})
- BRANCH_DEFAULT_MODULE_BUILD_FROM_SOURCE := true
-endif
-
ifneq (,$(MODULE_BUILD_FROM_SOURCE))
# Keep an explicit setting.
-else ifeq (,$(filter docs sdk win_sdk sdk_addon,$(MAKECMDGOALS))$(findstring com.google.android.conscrypt,$(PRODUCT_PACKAGES))$(findstring com.google.android.go.conscrypt,$(PRODUCT_PACKAGES)))
- # Prebuilt module SDKs require prebuilt modules to work, and currently
- # prebuilt modules are only provided for com.google.android(.go)?.xxx. If we can't
- # find one of them in PRODUCT_PACKAGES then assume com.android.xxx are in use,
- # and disable prebuilt SDKs. In particular this applies to AOSP builds.
- #
- # However, docs/sdk/win_sdk/sdk_addon builds might not include com.google.android.xxx
- # packages, so for those we respect the default behavior.
+else ifeq (,$(filter docs sdk win_sdk sdk_addon,$(MAKECMDGOALS)))
MODULE_BUILD_FROM_SOURCE := true
else ifneq (,$(PRODUCT_MODULE_BUILD_FROM_SOURCE))
# Let products override the branch default.
@@ -163,6 +144,8 @@
$(call add_soong_config_var_value,ANDROID,release_binder_death_recipient_weak_from_jni,$(RELEASE_BINDER_DEATH_RECIPIENT_WEAK_FROM_JNI))
+$(call add_soong_config_var_value,ANDROID,release_package_libandroid_runtime_punch_holes,$(RELEASE_PACKAGE_LIBANDROID_RUNTIME_PUNCH_HOLES))
+
$(call add_soong_config_var_value,ANDROID,release_selinux_data_data_ignore,$(RELEASE_SELINUX_DATA_DATA_IGNORE))
# Enable system_server optimizations by default unless explicitly set or if
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 8546828..aa695eb 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -39,7 +39,7 @@
$(call write-optional-json-list, "srcjars", $(sort $(ALL_MODULES.$(m).SRCJARS))) \
$(call write-optional-json-list, "classes_jar", $(sort $(ALL_MODULES.$(m).CLASSES_JAR))) \
$(call write-optional-json-list, "test_mainline_modules", $(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES))) \
- $(call write-optional-json-bool, $(ALL_MODULES.$(m).IS_UNIT_TEST)) \
+ $(call write-optional-json-bool, "is_unit_test", $(ALL_MODULES.$(m).IS_UNIT_TEST)) \
$(call write-optional-json-list, "test_options_tags", $(sort $(ALL_MODULES.$(m).TEST_OPTIONS_TAGS))) \
$(call write-optional-json-list, "data", $(sort $(ALL_MODULES.$(m).TEST_DATA))) \
$(call write-optional-json-list, "runtime_dependencies", $(sort $(ALL_MODULES.$(m).LOCAL_RUNTIME_LIBRARIES))) \
diff --git a/teams/Android.bp b/teams/Android.bp
index 8f83e71..a02a573 100644
--- a/teams/Android.bp
+++ b/teams/Android.bp
@@ -732,7 +732,7 @@
}
team {
- name: "trendy_team_deprecated_systemui_gfx",
+ name: "trendy_team_ailabs",
// go/trendy/manage/engineers/6673470538285056
trendy_team_id: "6673470538285056",
diff --git a/tools/aconfig/aconfig/src/storage/flag_table.rs b/tools/aconfig/aconfig/src/storage/flag_table.rs
index b339821..a971211 100644
--- a/tools/aconfig/aconfig/src/storage/flag_table.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_table.rs
@@ -48,7 +48,7 @@
package_id: u32,
flag_name: &str,
flag_type: StoredFlagType,
- flag_id: u16,
+ flag_index: u16,
num_buckets: u32,
) -> Self {
let bucket_index = FlagTableNode::find_bucket_index(package_id, flag_name, num_buckets);
@@ -56,7 +56,7 @@
package_id,
flag_name: flag_name.to_string(),
flag_type,
- flag_id,
+ flag_index,
next_offset: None,
};
Self { node, bucket_index }
diff --git a/tools/aconfig/aconfig/src/storage/flag_value.rs b/tools/aconfig/aconfig/src/storage/flag_value.rs
index a37ad9f..c15ba54 100644
--- a/tools/aconfig/aconfig/src/storage/flag_value.rs
+++ b/tools/aconfig/aconfig/src/storage/flag_value.rs
@@ -41,14 +41,14 @@
};
for pkg in packages.iter() {
- let start_offset = pkg.boolean_offset as usize;
+ 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())))?;
- list.booleans[start_offset + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
+ list.booleans[start_index + (*fid as usize)] = pf.state() == ProtoFlagState::ENABLED;
}
}
diff --git a/tools/aconfig/aconfig/src/storage/mod.rs b/tools/aconfig/aconfig/src/storage/mod.rs
index 30517de..855ed02 100644
--- a/tools/aconfig/aconfig/src/storage/mod.rs
+++ b/tools/aconfig/aconfig/src/storage/mod.rs
@@ -33,9 +33,9 @@
pub package_id: u32,
pub flag_names: HashSet<&'a str>,
pub boolean_flags: Vec<&'a ProtoParsedFlag>,
- // offset of the first boolean flag in this flag package with respect to the start of
- // boolean flag value array in the flag value file
- pub boolean_offset: u32,
+ // The index of the first boolean flag in this aconfig package among all boolean
+ // flags in this container.
+ pub boolean_start_index: u32,
}
impl<'a> FlagPackage<'a> {
@@ -45,7 +45,7 @@
package_id,
flag_names: HashSet::new(),
boolean_flags: vec![],
- boolean_offset: 0,
+ boolean_start_index: 0,
}
}
@@ -73,12 +73,11 @@
}
}
- // calculate package flag value start offset, in flag value file, each boolean
- // is stored as a single byte
- let mut boolean_offset = 0;
+ // cacluate boolean flag start index for each package
+ let mut boolean_start_index = 0;
for p in packages.iter_mut() {
- p.boolean_offset = boolean_offset;
- boolean_offset += p.boolean_flags.len() as u32;
+ p.boolean_start_index = boolean_start_index;
+ boolean_start_index += p.boolean_flags.len() as u32;
}
packages
@@ -184,7 +183,7 @@
assert!(packages[0].flag_names.contains("enabled_rw"));
assert!(packages[0].flag_names.contains("disabled_rw"));
assert!(packages[0].flag_names.contains("enabled_ro"));
- assert_eq!(packages[0].boolean_offset, 0);
+ assert_eq!(packages[0].boolean_start_index, 0);
assert_eq!(packages[1].package_name, "com.android.aconfig.storage.test_2");
assert_eq!(packages[1].package_id, 1);
@@ -192,13 +191,13 @@
assert!(packages[1].flag_names.contains("enabled_ro"));
assert!(packages[1].flag_names.contains("disabled_ro"));
assert!(packages[1].flag_names.contains("enabled_fixed_ro"));
- assert_eq!(packages[1].boolean_offset, 3);
+ assert_eq!(packages[1].boolean_start_index, 3);
assert_eq!(packages[2].package_name, "com.android.aconfig.storage.test_4");
assert_eq!(packages[2].package_id, 2);
assert_eq!(packages[2].flag_names.len(), 2);
assert!(packages[2].flag_names.contains("enabled_ro"));
assert!(packages[2].flag_names.contains("enabled_fixed_ro"));
- assert_eq!(packages[2].boolean_offset, 6);
+ assert_eq!(packages[2].boolean_start_index, 6);
}
}
diff --git a/tools/aconfig/aconfig/src/storage/package_table.rs b/tools/aconfig/aconfig/src/storage/package_table.rs
index 0a3df77..c53602f 100644
--- a/tools/aconfig/aconfig/src/storage/package_table.rs
+++ b/tools/aconfig/aconfig/src/storage/package_table.rs
@@ -48,7 +48,7 @@
let node = PackageTableNode {
package_name: String::from(package.package_name),
package_id: package.package_id,
- boolean_offset: package.boolean_offset,
+ boolean_start_index: package.boolean_start_index,
next_offset: None,
};
let bucket_index = PackageTableNode::find_bucket_index(package.package_name, num_buckets);
diff --git a/tools/aconfig/aconfig_storage_file/src/flag_info.rs b/tools/aconfig/aconfig_storage_file/src/flag_info.rs
index 3fff263..dc2a8d6 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 @@
/// 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 @@
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/flag_table.rs b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
index f41f4ce..64b90ea 100644
--- a/tools/aconfig/aconfig_storage_file/src/flag_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/flag_table.rs
@@ -100,7 +100,8 @@
pub package_id: u32,
pub flag_name: String,
pub flag_type: StoredFlagType,
- pub flag_id: u16,
+ // within package flag index of this flag type
+ pub flag_index: u16,
pub next_offset: Option<u32>,
}
@@ -109,8 +110,8 @@
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
+ "Package Id: {}, Flag: {}, Type: {:?}, Index: {}, Next: {:?}",
+ self.package_id, self.flag_name, self.flag_type, self.flag_index, self.next_offset
)?;
Ok(())
}
@@ -125,7 +126,7 @@
result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
result.extend_from_slice(name_bytes);
result.extend_from_slice(&(self.flag_type as u16).to_le_bytes());
- result.extend_from_slice(&self.flag_id.to_le_bytes());
+ result.extend_from_slice(&self.flag_index.to_le_bytes());
result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
result
}
@@ -137,7 +138,7 @@
package_id: read_u32_from_bytes(bytes, &mut head)?,
flag_name: read_str_from_bytes(bytes, &mut head)?,
flag_type: StoredFlagType::try_from(read_u16_from_bytes(bytes, &mut head)?)?,
- flag_id: read_u16_from_bytes(bytes, &mut head)?,
+ flag_index: read_u16_from_bytes(bytes, &mut head)?,
next_offset: match read_u32_from_bytes(bytes, &mut head)? {
0 => None,
val => Some(val),
diff --git a/tools/aconfig/aconfig_storage_file/src/lib.rs b/tools/aconfig/aconfig_storage_file/src/lib.rs
index d14bab6..070a3cf 100644
--- a/tools/aconfig/aconfig_storage_file/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_file/src/lib.rs
@@ -46,12 +46,14 @@
use std::hash::{Hash, Hasher};
use std::io::Read;
-pub use crate::flag_info::{FlagInfoHeader, FlagInfoList, FlagInfoNode};
+pub use crate::flag_info::{FlagInfoBit, 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, HashTableSizeLimit, InvalidFlagValueType, InvalidStoredFlagType,
+};
/// Storage file version
pub const FILE_VERSION: u32 = 1;
@@ -103,6 +105,7 @@
}
/// Flag type enum as stored by storage file
+/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StoredFlagType {
ReadWriteBoolean = 0,
@@ -123,6 +126,36 @@
}
}
+/// Flag value type enum, one FlagValueType maps to many StoredFlagType
+/// ONLY APPEND, NEVER REMOVE FOR BACKWARD COMPATIBILITY. THE MAX IS U16
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum FlagValueType {
+ Boolean = 0,
+}
+
+impl TryFrom<StoredFlagType> for FlagValueType {
+ type Error = AconfigStorageError;
+
+ fn try_from(value: StoredFlagType) -> Result<Self, Self::Error> {
+ match value {
+ StoredFlagType::ReadWriteBoolean => Ok(Self::Boolean),
+ StoredFlagType::ReadOnlyBoolean => Ok(Self::Boolean),
+ StoredFlagType::FixedReadOnlyBoolean => Ok(Self::Boolean),
+ }
+ }
+}
+
+impl TryFrom<u16> for FlagValueType {
+ type Error = AconfigStorageError;
+
+ fn try_from(value: u16) -> Result<Self, Self::Error> {
+ match value {
+ x if x == Self::Boolean as u16 => Ok(Self::Boolean),
+ _ => Err(InvalidFlagValueType(anyhow!("Invalid flag value type"))),
+ }
+ }
+}
+
/// Storage query api error
#[non_exhaustive]
#[derive(thiserror::Error, Debug)]
@@ -162,6 +195,9 @@
#[error("invalid stored flag type")]
InvalidStoredFlagType(#[source] anyhow::Error),
+
+ #[error("invalid flag value type")]
+ InvalidFlagValueType(#[source] anyhow::Error),
}
/// Get the right hash table size given number of entries in the table. Use a
@@ -254,13 +290,13 @@
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);
+ package_info[node.package_id as usize] = (&node.package_name, node.boolean_start_index);
}
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 flag_offset = package_offset + node.flag_id as u32;
+ let flag_offset = package_offset + node.flag_index as u32;
let flag_value = flag_value_list.booleans[flag_offset as usize];
flags.push((
String::from(package_name),
diff --git a/tools/aconfig/aconfig_storage_file/src/package_table.rs b/tools/aconfig/aconfig_storage_file/src/package_table.rs
index 36b0493..b734972 100644
--- a/tools/aconfig/aconfig_storage_file/src/package_table.rs
+++ b/tools/aconfig/aconfig_storage_file/src/package_table.rs
@@ -96,9 +96,9 @@
pub struct PackageTableNode {
pub package_name: String,
pub package_id: u32,
- // offset of the first boolean flag in this flag package with respect to the start of
- // boolean flag value array in the flag value file
- pub boolean_offset: u32,
+ // The index of the first boolean flag in this aconfig package among all boolean
+ // flags in this container.
+ pub boolean_start_index: u32,
pub next_offset: Option<u32>,
}
@@ -107,8 +107,8 @@
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
+ "Package: {}, Id: {}, Boolean flag start index: {}, Next: {:?}",
+ self.package_name, self.package_id, self.boolean_start_index, self.next_offset
)?;
Ok(())
}
@@ -122,7 +122,7 @@
result.extend_from_slice(&(name_bytes.len() as u32).to_le_bytes());
result.extend_from_slice(name_bytes);
result.extend_from_slice(&self.package_id.to_le_bytes());
- result.extend_from_slice(&self.boolean_offset.to_le_bytes());
+ result.extend_from_slice(&self.boolean_start_index.to_le_bytes());
result.extend_from_slice(&self.next_offset.unwrap_or(0).to_le_bytes());
result
}
@@ -133,7 +133,7 @@
let node = Self {
package_name: read_str_from_bytes(bytes, &mut head)?,
package_id: read_u32_from_bytes(bytes, &mut head)?,
- boolean_offset: read_u32_from_bytes(bytes, &mut head)?,
+ boolean_start_index: read_u32_from_bytes(bytes, &mut head)?,
next_offset: match read_u32_from_bytes(bytes, &mut head)? {
0 => None,
val => Some(val),
diff --git a/tools/aconfig/aconfig_storage_file/src/test_utils.rs b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
index c0f647a..608563c 100644
--- a/tools/aconfig/aconfig_storage_file/src/test_utils.rs
+++ b/tools/aconfig/aconfig_storage_file/src/test_utils.rs
@@ -38,19 +38,19 @@
let first_node = PackageTableNode {
package_name: String::from("com.android.aconfig.storage.test_2"),
package_id: 1,
- boolean_offset: 3,
+ boolean_start_index: 3,
next_offset: None,
};
let second_node = PackageTableNode {
package_name: String::from("com.android.aconfig.storage.test_1"),
package_id: 0,
- boolean_offset: 0,
+ boolean_start_index: 0,
next_offset: Some(159),
};
let third_node = PackageTableNode {
package_name: String::from("com.android.aconfig.storage.test_4"),
package_id: 2,
- boolean_offset: 6,
+ boolean_start_index: 6,
next_offset: None,
};
let nodes = vec![first_node, second_node, third_node];
@@ -63,14 +63,14 @@
package_id: u32,
flag_name: &str,
flag_type: u16,
- flag_id: u16,
+ flag_index: u16,
next_offset: Option<u32>,
) -> Self {
Self {
package_id,
flag_name: flag_name.to_string(),
flag_type: StoredFlagType::try_from(flag_type).unwrap(),
- flag_id,
+ flag_index,
next_offset,
}
}
diff --git a/tools/aconfig/aconfig_storage_read_api/Android.bp b/tools/aconfig/aconfig_storage_read_api/Android.bp
index b252e9d..3746d17 100644
--- a/tools/aconfig/aconfig_storage_read_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_read_api/Android.bp
@@ -38,6 +38,7 @@
"tests/package.map",
"tests/flag.map",
"tests/flag.val",
+ "tests/flag.info",
],
}
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 2213831..39f1840 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
@@ -54,6 +54,8 @@
return entry.flag_map();
case StorageFileType::flag_val:
return entry.flag_val();
+ case StorageFileType::flag_info:
+ return entry.flag_info();
default:
return Error() << "Invalid file type " << file_type;
}
@@ -124,49 +126,50 @@
}
}
-/// Get package offset
-Result<PackageOffset> get_package_offset(
+/// Get package context
+Result<PackageReadContext> get_package_read_context(
MappedStorageFile const& file,
std::string const& package) {
auto content = rust::Slice<const uint8_t>(
static_cast<uint8_t*>(file.file_ptr), file.file_size);
- auto offset_cxx = get_package_offset_cxx(content, rust::Str(package.c_str()));
- if (offset_cxx.query_success) {
- auto offset = PackageOffset();
- offset.package_exists = offset_cxx.package_exists;
- offset.package_id = offset_cxx.package_id;
- offset.boolean_offset = offset_cxx.boolean_offset;
- return offset;
+ auto context_cxx = get_package_read_context_cxx(content, rust::Str(package.c_str()));
+ if (context_cxx.query_success) {
+ auto context = PackageReadContext();
+ context.package_exists = context_cxx.package_exists;
+ context.package_id = context_cxx.package_id;
+ context.boolean_start_index = context_cxx.boolean_start_index;
+ return context;
} else {
- return Error() << offset_cxx.error_message;
+ return Error() << context_cxx.error_message;
}
}
-/// Get flag offset
-Result<FlagOffset> get_flag_offset(
+/// Get flag read context
+Result<FlagReadContext> get_flag_read_context(
MappedStorageFile const& file,
uint32_t package_id,
std::string const& flag_name){
auto content = rust::Slice<const uint8_t>(
static_cast<uint8_t*>(file.file_ptr), file.file_size);
- auto offset_cxx = get_flag_offset_cxx(content, package_id, rust::Str(flag_name.c_str()));
- if (offset_cxx.query_success) {
- auto offset = FlagOffset();
- offset.flag_exists = offset_cxx.flag_exists;
- offset.flag_offset = offset_cxx.flag_offset;
- return offset;
+ auto context_cxx = get_flag_read_context_cxx(content, package_id, rust::Str(flag_name.c_str()));
+ if (context_cxx.query_success) {
+ auto context = FlagReadContext();
+ context.flag_exists = context_cxx.flag_exists;
+ context.flag_type = static_cast<StoredFlagType>(context_cxx.flag_type);
+ context.flag_index = context_cxx.flag_index;
+ return context;
} else {
- return Error() << offset_cxx.error_message;
+ return Error() << context_cxx.error_message;
}
}
/// Get boolean flag value
Result<bool> get_boolean_flag_value(
MappedStorageFile const& file,
- uint32_t offset) {
+ uint32_t index) {
auto content = rust::Slice<const uint8_t>(
static_cast<uint8_t*>(file.file_ptr), file.file_size);
- auto value_cxx = get_boolean_flag_value_cxx(content, offset);
+ auto value_cxx = get_boolean_flag_value_cxx(content, index);
if (value_cxx.query_success) {
return value_cxx.flag_value;
} else {
@@ -174,4 +177,17 @@
}
}
+/// Get boolean flag attribute
+Result<uint8_t> get_boolean_flag_attribute(
+ MappedStorageFile const& file,
+ uint32_t index) {
+ auto content = rust::Slice<const uint8_t>(
+ static_cast<uint8_t*>(file.file_ptr), file.file_size);
+ auto info_cxx = get_boolean_flag_attribute_cxx(content, index);
+ if (info_cxx.query_success) {
+ return info_cxx.flag_attribute;
+ } else {
+ return Error() << info_cxx.error_message;
+ }
+}
} // namespace aconfig_storage
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 aa90f47..4555207 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
@@ -6,30 +6,56 @@
namespace aconfig_storage {
-/// Storage file type enum
+/// Storage file type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
enum StorageFileType {
package_map,
flag_map,
- flag_val
+ flag_val,
+ flag_info
};
+/// Flag type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
+enum StoredFlagType {
+ ReadWriteBoolean = 0,
+ ReadOnlyBoolean = 1,
+ FixedReadOnlyBoolean = 2,
+};
+
+/// Flag value type enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/lib.rs
+enum FlagValueType {
+ Boolean = 0,
+};
+
+/// Flag info enum, to be consistent with the one defined in
+/// aconfig_storage_file/src/flag_info.rs
+enum FlagInfoBit {
+ IsSticky = 1<<0,
+ IsReadWrite = 1<<1,
+ HasOverride = 1<<2,
+};
+
+
/// Mapped storage file
struct MappedStorageFile {
void* file_ptr;
size_t file_size;
};
-/// Package offset query result
-struct PackageOffset {
+/// Package read context query result
+struct PackageReadContext {
bool package_exists;
uint32_t package_id;
- uint32_t boolean_offset;
+ uint32_t boolean_start_index;
};
-/// Flag offset query result
-struct FlagOffset {
+/// Flag read context query result
+struct FlagReadContext {
bool flag_exists;
- uint16_t flag_offset;
+ StoredFlagType flag_type;
+ uint16_t flag_index;
};
/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
@@ -56,30 +82,37 @@
android::base::Result<uint32_t> get_storage_file_version(
std::string const& file_path);
-/// Get package offset
+/// Get package read context
/// \input file: mapped storage file
/// \input package: the flag package name
-/// \returns a package offset
-android::base::Result<PackageOffset> get_package_offset(
+/// \returns a package read context
+android::base::Result<PackageReadContext> get_package_read_context(
MappedStorageFile const& file,
std::string const& package);
-/// Get flag offset
+/// Get flag read context
/// \input file: mapped storage file
/// \input package_id: the flag package id obtained from package offset query
/// \input flag_name: flag name
-/// \returns the flag offset
-android::base::Result<FlagOffset> get_flag_offset(
+/// \returns the flag read context
+android::base::Result<FlagReadContext> get_flag_read_context(
MappedStorageFile const& file,
uint32_t package_id,
std::string const& flag_name);
/// Get boolean flag value
/// \input file: mapped storage file
-/// \input offset: the boolean flag value byte offset in the file
+/// \input index: the boolean flag index in the file
/// \returns the boolean flag value
android::base::Result<bool> get_boolean_flag_value(
MappedStorageFile const& file,
- uint32_t offset);
+ uint32_t index);
+/// Get boolean flag attribute
+/// \input file: mapped storage file
+/// \input index: the boolean flag index in the file
+/// \returns the boolean flag attribute
+android::base::Result<uint8_t> get_boolean_flag_attribute(
+ MappedStorageFile const& file,
+ uint32_t index);
} // namespace aconfig_storage
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 0000000..807da97
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_info_query.rs
@@ -0,0 +1,112 @@
+/*
+ * 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_index: u32) -> Result<u8, AconfigStorageError> {
+ 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
+ )));
+ }
+
+ // Find the byte offset to the flag info, each flag info now takes one byte
+ let mut head = (interpreted_header.boolean_flag_offset + flag_index) 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<bool> = 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_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
index a251b41..55fdcb7 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/flag_table_query.rs
@@ -18,18 +18,23 @@
use crate::{AconfigStorageError, FILE_VERSION};
use aconfig_storage_file::{
- flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes,
+ flag_table::FlagTableHeader, flag_table::FlagTableNode, read_u32_from_bytes, StoredFlagType,
};
use anyhow::anyhow;
-pub type FlagOffset = u16;
+/// Flag table query return
+#[derive(PartialEq, Debug)]
+pub struct FlagReadContext {
+ pub flag_type: StoredFlagType,
+ pub flag_index: u16,
+}
-/// Query flag within package offset
-pub fn find_flag_offset(
+/// Query flag read context: flag type and within package flag index
+pub fn find_flag_read_context(
buf: &[u8],
package_id: u32,
flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
+) -> Result<Option<FlagReadContext>, AconfigStorageError> {
let interpreted_header = FlagTableHeader::from_bytes(buf)?;
if interpreted_header.version > crate::FILE_VERSION {
return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -53,7 +58,10 @@
loop {
let interpreted_node = FlagTableNode::from_bytes(&buf[flag_node_offset..])?;
if interpreted_node.package_id == package_id && interpreted_node.flag_name == flag {
- return Ok(Some(interpreted_node.flag_id));
+ return Ok(Some(FlagReadContext {
+ flag_type: interpreted_node.flag_type,
+ flag_index: interpreted_node.flag_index,
+ }));
}
match interpreted_node.next_offset {
Some(offset) => flag_node_offset = offset as usize,
@@ -72,19 +80,20 @@
fn test_flag_query() {
let flag_table = create_test_flag_table().into_bytes();
let baseline = vec![
- (0, "enabled_ro", 1u16),
- (0, "enabled_rw", 2u16),
- (1, "disabled_ro", 0u16),
- (2, "enabled_ro", 1u16),
- (1, "enabled_fixed_ro", 1u16),
- (1, "enabled_ro", 2u16),
- (2, "enabled_fixed_ro", 0u16),
- (0, "disabled_rw", 0u16),
+ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+ (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+ (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+ (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+ (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+ (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
];
- for (package_id, flag_name, expected_offset) in baseline.into_iter() {
- let flag_offset =
- find_flag_offset(&flag_table[..], package_id, flag_name).unwrap().unwrap();
- assert_eq!(flag_offset, expected_offset);
+ for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+ let flag_context =
+ find_flag_read_context(&flag_table[..], package_id, flag_name).unwrap().unwrap();
+ assert_eq!(flag_context.flag_type, flag_type);
+ assert_eq!(flag_context.flag_index, flag_index);
}
}
@@ -92,10 +101,10 @@
// this test point locks down table query of a non exist flag
fn test_not_existed_flag_query() {
let flag_table = create_test_flag_table().into_bytes();
- let flag_offset = find_flag_offset(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
- assert_eq!(flag_offset, None);
- let flag_offset = find_flag_offset(&flag_table[..], 2, "disabled_rw").unwrap();
- assert_eq!(flag_offset, None);
+ let flag_context = find_flag_read_context(&flag_table[..], 1, "disabled_fixed_ro").unwrap();
+ assert_eq!(flag_context, None);
+ let flag_context = find_flag_read_context(&flag_table[..], 2, "disabled_rw").unwrap();
+ assert_eq!(flag_context, None);
}
#[test]
@@ -104,7 +113,7 @@
let mut table = create_test_flag_table();
table.header.version = crate::FILE_VERSION + 1;
let flag_table = table.into_bytes();
- let error = find_flag_offset(&flag_table[..], 0, "enabled_ro").unwrap_err();
+ let error = find_flag_read_context(&flag_table[..], 0, "enabled_ro").unwrap_err();
assert_eq!(
format!("{:?}", error),
format!(
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 964cd69..9d32a16 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
@@ -21,7 +21,7 @@
use anyhow::anyhow;
/// Query flag value
-pub fn find_boolean_flag_value(buf: &[u8], flag_offset: u32) -> Result<bool, AconfigStorageError> {
+pub fn find_boolean_flag_value(buf: &[u8], flag_index: u32) -> Result<bool, AconfigStorageError> {
let interpreted_header = FlagValueHeader::from_bytes(buf)?;
if interpreted_header.version > crate::FILE_VERSION {
return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -31,10 +31,8 @@
)));
}
- let mut head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
-
- // TODO: right now, there is only boolean flags, with more flag value types added
- // later, the end of boolean flag value section should be updated (b/322826265).
+ // Find byte offset to the flag value, each boolean flag cost one byte to store
+ let mut head = (interpreted_header.boolean_value_offset + flag_index) as usize;
if head >= interpreted_header.file_size as usize {
return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
"Flag value offset goes beyond the end of the file."
@@ -48,26 +46,13 @@
#[cfg(test)]
mod tests {
use super::*;
- use aconfig_storage_file::{FlagValueList, StorageFileType};
-
- pub fn create_test_flag_value_list() -> 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<bool> = 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<bool> = vec![false, true, false, false, true, true, false, true];
+ let baseline: Vec<bool> = 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 da64cb7..aa0145f 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/lib.rs
@@ -17,14 +17,16 @@
//! `aconfig_storage_read_api` is a crate that defines read apis to read flags from storage
//! 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>>>`
+//! 1, function to get package read context
+//! pub fn get_packager_read_context(container: &str, package: &str)
+//! -> `Result<Option<PackageReadContext>>>`
//!
-//! 2, function to get flag offset within a specific package
-//! pub fn get_flag_offset(container: &str, package_id: u32, flag: &str) -> `Result<Option<u16>>>`
+//! 2, function to get flag read context
+//! pub fn get_flag_read_context(container: &str, package_id: u32, flag: &str)
+//! -> `Result<Option<FlagReadContext>>>`
//!
-//! 3, function to get the actual flag value given the global offset (combined package and
-//! flag offset).
+//! 3, function to get the actual flag value given the global index (combined package and
+//! flag index).
//! pub fn get_boolean_flag_value(container: &str, offset: u32) -> `Result<bool>`
//!
//! 4, function to get storage file version without mmapping the file.
@@ -34,6 +36,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;
@@ -43,13 +46,14 @@
mod test_utils;
pub use aconfig_storage_file::{AconfigStorageError, StorageFileType};
-pub use flag_table_query::FlagOffset;
-pub use package_table_query::PackageOffset;
+pub use flag_table_query::FlagReadContext;
+pub use package_table_query::PackageReadContext;
use aconfig_storage_file::{read_u32_from_bytes, FILE_VERSION};
-use flag_table_query::find_flag_offset;
+use flag_info_query::find_boolean_flag_attribute;
+use flag_table_query::find_flag_read_context;
use flag_value_query::find_boolean_flag_value;
-use package_table_query::find_package_offset;
+use package_table_query::find_package_read_context;
use anyhow::anyhow;
use memmap2::Mmap;
@@ -77,50 +81,50 @@
unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
}
-/// Get package start offset for flags.
+/// Get package read context for a specific package.
///
/// \input file: mapped package file
/// \input package: package name
///
/// \return
-/// If a package is found, it returns Ok(Some(PackageOffset))
+/// If a package is found, it returns Ok(Some(PackageReadContext))
/// If a package is not found, it returns Ok(None)
/// If errors out, it returns an Err(errmsg)
-pub fn get_package_offset(
+pub fn get_package_read_context(
file: &Mmap,
package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
- find_package_offset(file, package)
+) -> Result<Option<PackageReadContext>, AconfigStorageError> {
+ find_package_read_context(file, package)
}
-/// Get flag offset within a package given.
+/// Get flag read context for a specific flag.
///
/// \input file: mapped flag file
/// \input package_id: package id obtained from package mapping file
/// \input flag: flag name
///
/// \return
-/// If a flag is found, it returns Ok(Some(u16))
+/// If a flag is found, it returns Ok(Some(FlagReadContext))
/// If a flag is not found, it returns Ok(None)
/// If errors out, it returns an Err(errmsg)
-pub fn get_flag_offset(
+pub fn get_flag_read_context(
file: &Mmap,
package_id: u32,
flag: &str,
-) -> Result<Option<FlagOffset>, AconfigStorageError> {
- find_flag_offset(file, package_id, flag)
+) -> Result<Option<FlagReadContext>, AconfigStorageError> {
+ find_flag_read_context(file, package_id, flag)
}
/// Get the boolean flag value.
///
/// \input file: mapped flag file
-/// \input offset: flag value offset
+/// \input index: boolean flag offset
///
/// \return
/// If the provide offset is valid, it returns the boolean flag value, otherwise it
/// returns the error message.
-pub fn get_boolean_flag_value(file: &Mmap, offset: u32) -> Result<bool, AconfigStorageError> {
- find_boolean_flag_value(file, offset)
+pub fn get_boolean_flag_value(file: &Mmap, index: u32) -> Result<bool, AconfigStorageError> {
+ find_boolean_flag_value(file, index)
}
/// Get storage file version number
@@ -145,6 +149,18 @@
read_u32_from_bytes(&buffer, &mut head)
}
+/// Get the boolean flag attribute.
+///
+/// \input file: mapped flag info file
+/// \input index: boolean flag index
+///
+/// \return
+/// If the provide offset is valid, it returns the boolean flag attribute bitfiled, otherwise it
+/// returns the error message.
+pub fn get_boolean_flag_attribute(file: &Mmap, index: u32) -> Result<u8, AconfigStorageError> {
+ find_boolean_flag_attribute(file, index)
+}
+
// *************************************** //
// CC INTERLOP
// *************************************** //
@@ -160,20 +176,21 @@
}
// Package table query return for cc interlop
- pub struct PackageOffsetQueryCXX {
+ pub struct PackageReadContextQueryCXX {
pub query_success: bool,
pub error_message: String,
pub package_exists: bool,
pub package_id: u32,
- pub boolean_offset: u32,
+ pub boolean_start_index: u32,
}
// Flag table query return for cc interlop
- pub struct FlagOffsetQueryCXX {
+ pub struct FlagReadContextQueryCXX {
pub query_success: bool,
pub error_message: String,
pub flag_exists: bool,
- pub flag_offset: u16,
+ pub flag_type: u16,
+ pub flag_index: u16,
}
// Flag value query return for cc interlop
@@ -183,21 +200,42 @@
pub flag_value: bool,
}
+ // Flag info query return for cc interlop
+ pub struct BooleanFlagAttributeQueryCXX {
+ pub query_success: bool,
+ pub error_message: String,
+ pub flag_attribute: u8,
+ }
+
// Rust export to c++
extern "Rust" {
pub fn get_storage_file_version_cxx(file_path: &str) -> VersionNumberQueryCXX;
- pub fn get_package_offset_cxx(file: &[u8], package: &str) -> PackageOffsetQueryCXX;
+ pub fn get_package_read_context_cxx(
+ file: &[u8],
+ package: &str,
+ ) -> PackageReadContextQueryCXX;
- pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> FlagOffsetQueryCXX;
+ pub fn get_flag_read_context_cxx(
+ file: &[u8],
+ package_id: u32,
+ flag: &str,
+ ) -> FlagReadContextQueryCXX;
pub fn get_boolean_flag_value_cxx(file: &[u8], offset: u32) -> BooleanFlagValueQueryCXX;
+
+ pub fn get_boolean_flag_attribute_cxx(
+ file: &[u8],
+ offset: u32,
+ ) -> BooleanFlagAttributeQueryCXX;
}
}
/// 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 {
+impl ffi::PackageReadContextQueryCXX {
+ pub(crate) fn new(
+ offset_result: Result<Option<PackageReadContext>, AconfigStorageError>,
+ ) -> Self {
match offset_result {
Ok(offset_opt) => match offset_opt {
Some(offset) => Self {
@@ -205,14 +243,14 @@
error_message: String::from(""),
package_exists: true,
package_id: offset.package_id,
- boolean_offset: offset.boolean_offset,
+ boolean_start_index: offset.boolean_start_index,
},
None => Self {
query_success: true,
error_message: String::from(""),
package_exists: false,
package_id: 0,
- boolean_offset: 0,
+ boolean_start_index: 0,
},
},
Err(errmsg) => Self {
@@ -220,35 +258,38 @@
error_message: format!("{:?}", errmsg),
package_exists: false,
package_id: 0,
- boolean_offset: 0,
+ boolean_start_index: 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 {
+impl ffi::FlagReadContextQueryCXX {
+ pub(crate) fn new(offset_result: Result<Option<FlagReadContext>, 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,
+ flag_type: offset.flag_type as u16,
+ flag_index: offset.flag_index,
},
None => Self {
query_success: true,
error_message: String::from(""),
flag_exists: false,
- flag_offset: 0,
+ flag_type: 0u16,
+ flag_index: 0u16,
},
},
Err(errmsg) => Self {
query_success: false,
error_message: format!("{:?}", errmsg),
flag_exists: false,
- flag_offset: 0,
+ flag_type: 0u16,
+ flag_index: 0u16,
},
}
}
@@ -270,6 +311,22 @@
}
}
+/// Implement the flag info interlop return type, create from actual flag info api return type
+impl ffi::BooleanFlagAttributeQueryCXX {
+ pub(crate) fn new(info_result: Result<u8, AconfigStorageError>) -> Self {
+ match info_result {
+ Ok(info) => {
+ Self { query_success: true, error_message: String::from(""), flag_attribute: info }
+ }
+ Err(errmsg) => Self {
+ query_success: false,
+ error_message: format!("{:?}", errmsg),
+ flag_attribute: 0u8,
+ },
+ }
+ }
+}
+
/// Implement the storage version number interlop return type, create from actual version number
/// api return type
impl ffi::VersionNumberQueryCXX {
@@ -289,14 +346,18 @@
}
}
-/// Get package start offset cc interlop
-pub fn get_package_offset_cxx(file: &[u8], package: &str) -> ffi::PackageOffsetQueryCXX {
- ffi::PackageOffsetQueryCXX::new(find_package_offset(file, package))
+/// Get package read context cc interlop
+pub fn get_package_read_context_cxx(file: &[u8], package: &str) -> ffi::PackageReadContextQueryCXX {
+ ffi::PackageReadContextQueryCXX::new(find_package_read_context(file, package))
}
-/// Get flag start offset cc interlop
-pub fn get_flag_offset_cxx(file: &[u8], package_id: u32, flag: &str) -> ffi::FlagOffsetQueryCXX {
- ffi::FlagOffsetQueryCXX::new(find_flag_offset(file, package_id, flag))
+/// Get flag read context cc interlop
+pub fn get_flag_read_context_cxx(
+ file: &[u8],
+ package_id: u32,
+ flag: &str,
+) -> ffi::FlagReadContextQueryCXX {
+ ffi::FlagReadContextQueryCXX::new(find_flag_read_context(file, package_id, flag))
}
/// Get boolean flag value cc interlop
@@ -304,6 +365,14 @@
ffi::BooleanFlagValueQueryCXX::new(find_boolean_flag_value(file, offset))
}
+/// Get boolean flag attribute cc interlop
+pub fn get_boolean_flag_attribute_cxx(
+ file: &[u8],
+ offset: u32,
+) -> ffi::BooleanFlagAttributeQueryCXX {
+ ffi::BooleanFlagAttributeQueryCXX::new(find_boolean_flag_attribute(file, 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))
@@ -315,12 +384,14 @@
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, StoredFlagType};
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,77 +401,80 @@
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();
+ // this test point locks down flag package read context query
+ fn test_package_context_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 package_mapped_file = unsafe {
get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
};
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+ assert_eq!(package_context, expected_package_context);
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+ assert_eq!(package_context, expected_package_context);
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+ assert_eq!(package_context, expected_package_context);
}
#[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();
+ // this test point locks down flag read context query
+ fn test_flag_context_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_mapped_file =
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
let baseline = vec![
- (0, "enabled_ro", 1u16),
- (0, "enabled_rw", 2u16),
- (1, "disabled_ro", 0u16),
- (2, "enabled_ro", 1u16),
- (1, "enabled_fixed_ro", 1u16),
- (1, "enabled_ro", 2u16),
- (2, "enabled_fixed_ro", 0u16),
- (0, "disabled_rw", 0u16),
+ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+ (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+ (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+ (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+ (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+ (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
];
- for (package_id, flag_name, expected_offset) in baseline.into_iter() {
- let flag_offset =
- get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
- assert_eq!(flag_offset, expected_offset);
+ for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+ let flag_context =
+ get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+ assert_eq!(flag_context.flag_type, flag_type);
+ assert_eq!(flag_context.flag_index, flag_index);
}
}
#[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() };
@@ -412,10 +486,27 @@
}
#[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<bool> = 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 51354db..3786443 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/mapped_file.rs
@@ -29,7 +29,7 @@
};
/// Find where storage files are stored for a particular container
-fn find_container_storage_location(
+pub fn find_container_storage_location(
location_pb_file: &str,
container: &str,
) -> Result<ProtoStorageFileInfo, AconfigStorageError> {
@@ -91,9 +91,7 @@
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/src/package_table_query.rs b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
index d83844e..2cb854b 100644
--- a/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
+++ b/tools/aconfig/aconfig_storage_read_api/src/package_table_query.rs
@@ -24,16 +24,16 @@
/// Package table query return
#[derive(PartialEq, Debug)]
-pub struct PackageOffset {
+pub struct PackageReadContext {
pub package_id: u32,
- pub boolean_offset: u32,
+ pub boolean_start_index: u32,
}
-/// Query package id and start offset
-pub fn find_package_offset(
+/// Query package read context: package id and start index
+pub fn find_package_read_context(
buf: &[u8],
package: &str,
-) -> Result<Option<PackageOffset>, AconfigStorageError> {
+) -> Result<Option<PackageReadContext>, AconfigStorageError> {
let interpreted_header = PackageTableHeader::from_bytes(buf)?;
if interpreted_header.version > FILE_VERSION {
return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
@@ -57,9 +57,9 @@
loop {
let interpreted_node = PackageTableNode::from_bytes(&buf[package_node_offset..])?;
if interpreted_node.package_name == package {
- return Ok(Some(PackageOffset {
+ return Ok(Some(PackageReadContext {
package_id: interpreted_node.package_id,
- boolean_offset: interpreted_node.boolean_offset,
+ boolean_start_index: interpreted_node.boolean_start_index,
}));
}
match interpreted_node.next_offset {
@@ -78,24 +78,24 @@
// this test point locks down table query
fn test_package_query() {
let package_table = create_test_package_table().into_bytes();
- let package_offset =
- find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
+ let package_context =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
- assert_eq!(package_offset, expected_package_offset);
- let package_offset =
- find_package_offset(&package_table[..], "com.android.aconfig.storage.test_2")
+ let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+ assert_eq!(package_context, expected_package_context);
+ let package_context =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_2")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
- assert_eq!(package_offset, expected_package_offset);
- let package_offset =
- find_package_offset(&package_table[..], "com.android.aconfig.storage.test_4")
+ let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+ assert_eq!(package_context, expected_package_context);
+ let package_context =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_4")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+ assert_eq!(package_context, expected_package_context);
}
#[test]
@@ -103,13 +103,15 @@
fn test_not_existed_package_query() {
// this will land at an empty bucket
let package_table = create_test_package_table().into_bytes();
- let package_offset =
- find_package_offset(&package_table[..], "com.android.aconfig.storage.test_3").unwrap();
- assert_eq!(package_offset, None);
+ let package_context =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_3")
+ .unwrap();
+ assert_eq!(package_context, None);
// this will land at the end of a linked list
- let package_offset =
- find_package_offset(&package_table[..], "com.android.aconfig.storage.test_5").unwrap();
- assert_eq!(package_offset, None);
+ let package_context =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_5")
+ .unwrap();
+ assert_eq!(package_context, None);
}
#[test]
@@ -118,8 +120,9 @@
let mut table = create_test_package_table();
table.header.version = crate::FILE_VERSION + 1;
let package_table = table.into_bytes();
- let error = find_package_offset(&package_table[..], "com.android.aconfig.storage.test_1")
- .unwrap_err();
+ let error =
+ find_package_read_context(&package_table[..], "com.android.aconfig.storage.test_1")
+ .unwrap_err();
assert_eq!(
format!("{:?}", error),
format!(
diff --git a/tools/aconfig/aconfig_storage_read_api/tests/Android.bp b/tools/aconfig/aconfig_storage_read_api/tests/Android.bp
index d9cf238..6b05ca6 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 @@
"package.map",
"flag.map",
"flag.val",
+ "flag.info",
],
test_suites: ["general-tests"],
}
@@ -35,6 +36,7 @@
"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 0000000..820d839
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_read_api/tests/flag.info
Binary files differ
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 539474b..8ed3756 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
@@ -47,7 +47,8 @@
Result<std::string> write_storage_location_pb_file(std::string const& package_map,
std::string const& flag_map,
- std::string const& flag_val) {
+ std::string const& flag_val,
+ std::string const& flag_info) {
auto temp_file = std::tmpnam(nullptr);
auto proto = storage_files();
auto* info = proto.add_files();
@@ -56,6 +57,7 @@
info->set_package_map(package_map);
info->set_flag_map(flag_map);
info->set_flag_val(flag_val);
+ info->set_flag_info(flag_info);
info->set_timestamp(12345);
auto content = std::string();
@@ -71,20 +73,23 @@
package_map = *copy_to_temp_file(test_dir + "/package.map");
flag_map = *copy_to_temp_file(test_dir + "/flag.map");
flag_val = *copy_to_temp_file(test_dir + "/flag.val");
+ flag_info = *copy_to_temp_file(test_dir + "/flag.info");
storage_record_pb = *write_storage_location_pb_file(
- package_map, flag_map, flag_val);
+ package_map, flag_map, flag_val, flag_info);
}
void TearDown() override {
std::remove(package_map.c_str());
std::remove(flag_map.c_str());
std::remove(flag_val.c_str());
+ std::remove(flag_info.c_str());
std::remove(storage_record_pb.c_str());
}
std::string package_map;
std::string flag_map;
std::string flag_val;
+ std::string flag_info;
std::string storage_record_pb;
};
@@ -99,6 +104,9 @@
version = api::get_storage_file_version(flag_val);
ASSERT_TRUE(version.ok());
ASSERT_EQ(*version, 1);
+ version = api::get_storage_file_version(flag_info);
+ ASSERT_TRUE(version.ok());
+ ASSERT_EQ(*version, 1);
}
/// Negative test to lock down the error when mapping none exist storage files
@@ -110,83 +118,84 @@
"Unable to find storage files for container vendor");
}
-/// Test to lock down storage package offset query api
-TEST_F(AconfigStorageTest, test_package_offset_query) {
+/// Test to lock down storage package context query api
+TEST_F(AconfigStorageTest, test_package_context_query) {
auto mapped_file = private_api::get_mapped_file_impl(
storage_record_pb, "mockup", api::StorageFileType::package_map);
ASSERT_TRUE(mapped_file.ok());
- auto offset = api::get_package_offset(
+ auto context = api::get_package_read_context(
*mapped_file, "com.android.aconfig.storage.test_1");
- ASSERT_TRUE(offset.ok());
- ASSERT_TRUE(offset->package_exists);
- ASSERT_EQ(offset->package_id, 0);
- ASSERT_EQ(offset->boolean_offset, 0);
+ ASSERT_TRUE(context.ok());
+ ASSERT_TRUE(context->package_exists);
+ ASSERT_EQ(context->package_id, 0);
+ ASSERT_EQ(context->boolean_start_index, 0);
- offset = api::get_package_offset(
+ context = api::get_package_read_context(
*mapped_file, "com.android.aconfig.storage.test_2");
- ASSERT_TRUE(offset.ok());
- ASSERT_TRUE(offset->package_exists);
- ASSERT_EQ(offset->package_id, 1);
- ASSERT_EQ(offset->boolean_offset, 3);
+ ASSERT_TRUE(context.ok());
+ ASSERT_TRUE(context->package_exists);
+ ASSERT_EQ(context->package_id, 1);
+ ASSERT_EQ(context->boolean_start_index, 3);
- offset = api::get_package_offset(
+ context = api::get_package_read_context(
*mapped_file, "com.android.aconfig.storage.test_4");
- ASSERT_TRUE(offset.ok());
- ASSERT_TRUE(offset->package_exists);
- ASSERT_EQ(offset->package_id, 2);
- ASSERT_EQ(offset->boolean_offset, 6);
+ ASSERT_TRUE(context.ok());
+ ASSERT_TRUE(context->package_exists);
+ ASSERT_EQ(context->package_id, 2);
+ ASSERT_EQ(context->boolean_start_index, 6);
}
/// Test to lock down when querying none exist package
-TEST_F(AconfigStorageTest, test_none_existent_package_offset_query) {
+TEST_F(AconfigStorageTest, test_none_existent_package_context_query) {
auto mapped_file = private_api::get_mapped_file_impl(
storage_record_pb, "mockup", api::StorageFileType::package_map);
ASSERT_TRUE(mapped_file.ok());
- auto offset = api::get_package_offset(
+ auto context = api::get_package_read_context(
*mapped_file, "com.android.aconfig.storage.test_3");
- ASSERT_TRUE(offset.ok());
- ASSERT_FALSE(offset->package_exists);
+ ASSERT_TRUE(context.ok());
+ ASSERT_FALSE(context->package_exists);
}
-/// Test to lock down storage flag offset query api
-TEST_F(AconfigStorageTest, test_flag_offset_query) {
+/// Test to lock down storage flag context query api
+TEST_F(AconfigStorageTest, test_flag_context_query) {
auto mapped_file = private_api::get_mapped_file_impl(
storage_record_pb, "mockup", api::StorageFileType::flag_map);
ASSERT_TRUE(mapped_file.ok());
- auto baseline = std::vector<std::tuple<int, std::string, int>>{
- {0, "enabled_ro", 1},
- {0, "enabled_rw", 2},
- {1, "disabled_ro", 0},
- {2, "enabled_ro", 1},
- {1, "enabled_fixed_ro", 1},
- {1, "enabled_ro", 2},
- {2, "enabled_fixed_ro", 0},
- {0, "disabled_rw", 0},
+ auto baseline = std::vector<std::tuple<int, std::string, api::StoredFlagType, int>>{
+ {0, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
+ {0, "enabled_rw", api::StoredFlagType::ReadWriteBoolean, 2},
+ {1, "disabled_ro", api::StoredFlagType::ReadOnlyBoolean, 0},
+ {2, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 1},
+ {1, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 1},
+ {1, "enabled_ro", api::StoredFlagType::ReadOnlyBoolean, 2},
+ {2, "enabled_fixed_ro", api::StoredFlagType::FixedReadOnlyBoolean, 0},
+ {0, "disabled_rw", api::StoredFlagType::ReadWriteBoolean, 0},
};
- for (auto const&[package_id, flag_name, expected_offset] : baseline) {
- auto offset = api::get_flag_offset(*mapped_file, package_id, flag_name);
- ASSERT_TRUE(offset.ok());
- ASSERT_TRUE(offset->flag_exists);
- ASSERT_EQ(offset->flag_offset, expected_offset);
+ for (auto const&[package_id, flag_name, flag_type, flag_index] : baseline) {
+ auto context = api::get_flag_read_context(*mapped_file, package_id, flag_name);
+ ASSERT_TRUE(context.ok());
+ ASSERT_TRUE(context->flag_exists);
+ ASSERT_EQ(context->flag_type, flag_type);
+ ASSERT_EQ(context->flag_index, flag_index);
}
}
/// Test to lock down when querying none exist flag
-TEST_F(AconfigStorageTest, test_none_existent_flag_offset_query) {
+TEST_F(AconfigStorageTest, test_none_existent_flag_context_query) {
auto mapped_file = private_api::get_mapped_file_impl(
storage_record_pb, "mockup", api::StorageFileType::flag_map);
ASSERT_TRUE(mapped_file.ok());
- auto offset = api::get_flag_offset(*mapped_file, 0, "none_exist");
- ASSERT_TRUE(offset.ok());
- ASSERT_FALSE(offset->flag_exists);
+ auto context = api::get_flag_read_context(*mapped_file, 0, "none_exist");
+ ASSERT_TRUE(context.ok());
+ ASSERT_FALSE(context->flag_exists);
- offset = api::get_flag_offset(*mapped_file, 3, "enabled_ro");
- ASSERT_TRUE(offset.ok());
- ASSERT_FALSE(offset->flag_exists);
+ context = api::get_flag_read_context(*mapped_file, 3, "enabled_ro");
+ ASSERT_TRUE(context.ok());
+ ASSERT_FALSE(context->flag_exists);
}
/// Test to lock down storage flag value query api
@@ -197,10 +206,10 @@
auto expected_value = std::vector<bool>{
false, true, true, false, true, true, true, true};
- for (int offset = 0; offset < 8; ++offset) {
- auto value = api::get_boolean_flag_value(*mapped_file, offset);
+ for (int index = 0; index < 8; ++index) {
+ auto value = api::get_boolean_flag_value(*mapped_file, index);
ASSERT_TRUE(value.ok());
- ASSERT_EQ(*value, expected_value[offset]);
+ ASSERT_EQ(*value, expected_value[index]);
}
}
@@ -215,3 +224,33 @@
ASSERT_EQ(value.error().message(),
std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
}
+
+/// Test to lock down storage flag info query api
+TEST_F(AconfigStorageTest, test_boolean_flag_info_query) {
+ auto mapped_file = private_api::get_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ ASSERT_TRUE(mapped_file.ok());
+
+ auto expected_value = std::vector<bool>{
+ true, false, true, false, false, false, false, false};
+ for (int index = 0; index < 8; ++index) {
+ auto attribute = api::get_boolean_flag_attribute(*mapped_file, index);
+ ASSERT_TRUE(attribute.ok());
+ ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsSticky), 0);
+ ASSERT_EQ((*attribute & static_cast<uint8_t>(api::FlagInfoBit::IsReadWrite)) != 0,
+ expected_value[index]);
+ ASSERT_EQ(*attribute & static_cast<uint8_t>(api::FlagInfoBit::HasOverride), 0);
+ }
+}
+
+/// Negative test to lock down the error when querying flag info out of range
+TEST_F(AconfigStorageTest, test_invalid_boolean_flag_info_query) {
+ auto mapped_file = private_api::get_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ ASSERT_TRUE(mapped_file.ok());
+
+ auto attribute = api::get_boolean_flag_attribute(*mapped_file, 8);
+ ASSERT_FALSE(attribute.ok());
+ ASSERT_EQ(attribute.error().message(),
+ std::string("InvalidStorageFileOffset(Flag info offset goes beyond the end of the 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 7687d0f..bddb396 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,10 +1,11 @@
#[cfg(not(feature = "cargo"))]
mod aconfig_storage_rust_test {
use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
- use aconfig_storage_file::StorageFileType;
+ use aconfig_storage_file::{FlagInfoBit, StorageFileType, StoredFlagType};
use aconfig_storage_read_api::{
- get_boolean_flag_value, get_flag_offset, get_package_offset, get_storage_file_version,
- mapped_file::get_mapped_file, PackageOffset,
+ get_boolean_flag_attribute, get_boolean_flag_value, get_flag_read_context,
+ get_package_read_context, get_storage_file_version, mapped_file::get_mapped_file,
+ PackageReadContext,
};
use std::fs;
use tempfile::NamedTempFile;
@@ -15,10 +16,11 @@
file
}
- fn create_test_storage_files() -> [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 +30,22 @@
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
@@ -55,8 +59,8 @@
}
#[test]
- fn test_package_offset_query() {
- let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+ fn test_package_context_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
@@ -64,31 +68,31 @@
get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
};
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_1")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_1")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 0, boolean_offset: 0 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 0, boolean_start_index: 0 };
+ assert_eq!(package_context, expected_package_context);
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_2")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_2")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 1, boolean_offset: 3 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 1, boolean_start_index: 3 };
+ assert_eq!(package_context, expected_package_context);
- let package_offset =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_4")
+ let package_context =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_4")
.unwrap()
.unwrap();
- let expected_package_offset = PackageOffset { package_id: 2, boolean_offset: 6 };
- assert_eq!(package_offset, expected_package_offset);
+ let expected_package_context = PackageReadContext { package_id: 2, boolean_start_index: 6 };
+ assert_eq!(package_context, expected_package_context);
}
#[test]
- fn test_none_exist_package_offset_query() {
- let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+ fn test_none_exist_package_context_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
@@ -96,14 +100,15 @@
get_mapped_file(&pb_file_path, "mockup", StorageFileType::PackageMap).unwrap()
};
- let package_offset_option =
- get_package_offset(&package_mapped_file, "com.android.aconfig.storage.test_3").unwrap();
- assert_eq!(package_offset_option, None);
+ let package_context_option =
+ get_package_read_context(&package_mapped_file, "com.android.aconfig.storage.test_3")
+ .unwrap();
+ assert_eq!(package_context_option, None);
}
#[test]
- fn test_flag_offset_query() {
- let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+ fn test_flag_context_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
@@ -111,40 +116,43 @@
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
let baseline = vec![
- (0, "enabled_ro", 1u16),
- (0, "enabled_rw", 2u16),
- (1, "disabled_ro", 0u16),
- (2, "enabled_ro", 1u16),
- (1, "enabled_fixed_ro", 1u16),
- (1, "enabled_ro", 2u16),
- (2, "enabled_fixed_ro", 0u16),
- (0, "disabled_rw", 0u16),
+ (0, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (0, "enabled_rw", StoredFlagType::ReadWriteBoolean, 2u16),
+ (1, "disabled_ro", StoredFlagType::ReadOnlyBoolean, 0u16),
+ (2, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 1u16),
+ (1, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 1u16),
+ (1, "enabled_ro", StoredFlagType::ReadOnlyBoolean, 2u16),
+ (2, "enabled_fixed_ro", StoredFlagType::FixedReadOnlyBoolean, 0u16),
+ (0, "disabled_rw", StoredFlagType::ReadWriteBoolean, 0u16),
];
- for (package_id, flag_name, expected_offset) in baseline.into_iter() {
- let flag_offset =
- get_flag_offset(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
- assert_eq!(flag_offset, expected_offset);
+ for (package_id, flag_name, flag_type, flag_index) in baseline.into_iter() {
+ let flag_context =
+ get_flag_read_context(&flag_mapped_file, package_id, flag_name).unwrap().unwrap();
+ assert_eq!(flag_context.flag_type, flag_type);
+ assert_eq!(flag_context.flag_index, flag_index);
}
}
#[test]
- fn test_none_exist_flag_offset_query() {
- let [_package_map, _flag_map, _flag_val, pb_file] = create_test_storage_files();
+ fn test_none_exist_flag_context_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_mapped_file =
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagMap).unwrap() };
- let flag_offset_option = get_flag_offset(&flag_mapped_file, 0, "none_exist").unwrap();
- assert_eq!(flag_offset_option, None);
+ let flag_context_option =
+ get_flag_read_context(&flag_mapped_file, 0, "none_exist").unwrap();
+ assert_eq!(flag_context_option, None);
- let flag_offset_option = get_flag_offset(&flag_mapped_file, 3, "enabled_ro").unwrap();
- assert_eq!(flag_offset_option, None);
+ let flag_context_option =
+ get_flag_read_context(&flag_mapped_file, 3, "enabled_ro").unwrap();
+ assert_eq!(flag_context_option, None);
}
#[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 +167,7 @@
#[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
@@ -173,9 +181,42 @@
}
#[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<bool> = 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);
}
}
diff --git a/tools/aconfig/aconfig_storage_write_api/Android.bp b/tools/aconfig/aconfig_storage_write_api/Android.bp
index 0f15b9c..4dbdbbf 100644
--- a/tools/aconfig/aconfig_storage_write_api/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/Android.bp
@@ -14,6 +14,7 @@
"libcxx",
"libthiserror",
"libaconfig_storage_file",
+ "libaconfig_storage_read_api",
],
}
@@ -30,6 +31,7 @@
defaults: ["aconfig_storage_write_api.defaults"],
data: [
"tests/flag.val",
+ "tests/flag.info",
],
rustlibs: [
"libaconfig_storage_read_api",
@@ -68,12 +70,13 @@
srcs: ["aconfig_storage_write_api.cpp"],
generated_headers: [
"cxx-bridge-header",
- "libcxx_aconfig_storage_write_api_bridge_header"
+ "libcxx_aconfig_storage_write_api_bridge_header",
],
generated_sources: ["libcxx_aconfig_storage_write_api_bridge_code"],
whole_static_libs: ["libaconfig_storage_write_api_cxx_bridge"],
export_include_dirs: ["include"],
static_libs: [
+ "libaconfig_storage_read_api_cc",
"libaconfig_storage_protos_cc",
"libprotobuf-cpp-lite",
"libbase",
diff --git a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
index ea88f05..01785e1 100644
--- a/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/aconfig_storage_write_api.cpp
@@ -38,7 +38,8 @@
/// Get storage file path
static Result<std::string> find_storage_file(
std::string const& pb_file,
- std::string const& container) {
+ std::string const& container,
+ StorageFileType file_type) {
auto records_pb = read_storage_records_pb(pb_file);
if (!records_pb.ok()) {
return Error() << "Unable to read storage records from " << pb_file
@@ -47,15 +48,26 @@
for (auto& entry : records_pb->files()) {
if (entry.container() == container) {
- return entry.flag_val();
+ switch(file_type) {
+ case StorageFileType::package_map:
+ return entry.package_map();
+ case StorageFileType::flag_map:
+ return entry.flag_map();
+ case StorageFileType::flag_val:
+ return entry.flag_val();
+ case StorageFileType::flag_info:
+ return entry.flag_info();
+ default:
+ return Error() << "Invalid file type " << file_type;
+ }
}
}
- return Error() << "Unable to find storage files for container " << container;;
+ return Error() << "Unable to find storage files for container " << container;
}
/// Map a storage file
-static Result<MappedFlagValueFile> map_storage_file(std::string const& file) {
+static Result<MutableMappedStorageFile> map_storage_file(std::string const& file) {
struct stat file_stat;
if (stat(file.c_str(), &file_stat) < 0) {
return ErrnoError() << "stat failed";
@@ -78,7 +90,7 @@
return ErrnoError() << "mmap failed";
}
- auto mapped_file = MappedFlagValueFile();
+ auto mapped_file = MutableMappedStorageFile();
mapped_file.file_ptr = map_result;
mapped_file.file_size = file_size;
@@ -87,34 +99,37 @@
namespace private_internal_api {
-/// Get mapped file implementation.
-Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+/// Get mutable mapped file implementation.
+Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
std::string const& pb_file,
- std::string const& container) {
- auto file_result = find_storage_file(pb_file, container);
+ std::string const& container,
+ StorageFileType file_type) {
+ if (file_type != StorageFileType::flag_val &&
+ file_type != StorageFileType::flag_info) {
+ return Error() << "Cannot create mutable mapped file for this file type";
+ }
+
+ auto file_result = find_storage_file(pb_file, container, file_type);
if (!file_result.ok()) {
return Error() << file_result.error();
}
- auto mapped_result = map_storage_file(*file_result);
- if (!mapped_result.ok()) {
- return Error() << "failed to map " << *file_result << ": "
- << mapped_result.error();
- }
- return *mapped_result;
+
+ return map_storage_file(*file_result);
}
} // namespace private internal api
-/// Get mapped writeable flag value file
-Result<MappedFlagValueFile> get_mapped_flag_value_file(
- std::string const& container) {
- return private_internal_api::get_mapped_flag_value_file_impl(
- kPersistStorageRecordsPb, container);
+/// Get mutable mapped file
+Result<MutableMappedStorageFile> get_mutable_mapped_file(
+ std::string const& container,
+ StorageFileType file_type) {
+ return private_internal_api::get_mutable_mapped_file_impl(
+ kPersistStorageRecordsPb, container, file_type);
}
/// Set boolean flag value
Result<void> set_boolean_flag_value(
- const MappedFlagValueFile& file,
+ const MutableMappedStorageFile& file,
uint32_t offset,
bool value) {
auto content = rust::Slice<uint8_t>(
@@ -126,6 +141,38 @@
return {};
}
+/// Set if flag is sticky
+Result<void> set_flag_is_sticky(
+ const MutableMappedStorageFile& file,
+ FlagValueType value_type,
+ uint32_t offset,
+ bool value) {
+ auto content = rust::Slice<uint8_t>(
+ static_cast<uint8_t*>(file.file_ptr), file.file_size);
+ auto update_cxx = update_flag_is_sticky_cxx(
+ content, static_cast<uint16_t>(value_type), offset, value);
+ if (!update_cxx.update_success) {
+ return Error() << std::string(update_cxx.error_message.c_str());
+ }
+ return {};
+}
+
+/// Set if flag has override
+Result<void> set_flag_has_override(
+ const MutableMappedStorageFile& file,
+ FlagValueType value_type,
+ uint32_t offset,
+ bool value) {
+ auto content = rust::Slice<uint8_t>(
+ static_cast<uint8_t*>(file.file_ptr), file.file_size);
+ auto update_cxx = update_flag_has_override_cxx(
+ content, static_cast<uint16_t>(value_type), offset, value);
+ if (!update_cxx.update_success) {
+ return Error() << std::string(update_cxx.error_message.c_str());
+ }
+ return {};
+}
+
Result<void> create_flag_info(
std::string const& package_map,
std::string const& flag_map,
diff --git a/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
index b652510..8699b88 100644
--- a/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
+++ b/tools/aconfig/aconfig_storage_write_api/include/aconfig_storage/aconfig_storage_write_api.hpp
@@ -4,13 +4,14 @@
#include <string>
#include <android-base/result.h>
+#include <aconfig_storage/aconfig_storage_read_api.hpp>
using namespace android::base;
namespace aconfig_storage {
/// Mapped flag value file
-struct MappedFlagValueFile{
+struct MutableMappedStorageFile{
void* file_ptr;
size_t file_size;
};
@@ -18,19 +19,35 @@
/// DO NOT USE APIS IN THE FOLLOWING NAMESPACE DIRECTLY
namespace private_internal_api {
-Result<MappedFlagValueFile> get_mapped_flag_value_file_impl(
+Result<MutableMappedStorageFile> get_mutable_mapped_file_impl(
std::string const& pb_file,
- std::string const& container);
+ std::string const& container,
+ StorageFileType file_type);
} // namespace private_internal_api
-/// Get mapped writeable flag value file
-Result<MappedFlagValueFile> get_mapped_flag_value_file(
- std::string const& container);
+/// Get mapped writeable storage file
+Result<MutableMappedStorageFile> get_mutable_mapped_file(
+ std::string const& container,
+ StorageFileType file_type);
/// Set boolean flag value
Result<void> set_boolean_flag_value(
- const MappedFlagValueFile& file,
+ const MutableMappedStorageFile& file,
+ uint32_t offset,
+ bool value);
+
+/// Set if flag is sticky
+Result<void> set_flag_is_sticky(
+ const MutableMappedStorageFile& file,
+ FlagValueType value_type,
+ uint32_t offset,
+ bool value);
+
+/// Set if flag has override
+Result<void> set_flag_has_override(
+ const MutableMappedStorageFile& file,
+ FlagValueType value_type,
uint32_t offset,
bool value);
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
new file mode 100644
index 0000000..56e1253
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_info_update.rs
@@ -0,0 +1,129 @@
+/*
+ * 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 info update module defines the flag info file write to mapped bytes
+
+use aconfig_storage_file::{
+ read_u8_from_bytes, AconfigStorageError, FlagInfoBit, FlagInfoHeader, FlagValueType,
+ FILE_VERSION,
+};
+use anyhow::anyhow;
+
+fn get_flag_info_offset(
+ buf: &mut [u8],
+ flag_type: FlagValueType,
+ flag_index: u32,
+) -> Result<usize, AconfigStorageError> {
+ let interpreted_header = FlagInfoHeader::from_bytes(buf)?;
+ if interpreted_header.version > FILE_VERSION {
+ return Err(AconfigStorageError::HigherStorageFileVersion(anyhow!(
+ "Cannot write to storage file with a higher version of {} with lib version {}",
+ interpreted_header.version,
+ FILE_VERSION
+ )));
+ }
+
+ // get byte offset to the flag info
+ let head = match flag_type {
+ FlagValueType::Boolean => (interpreted_header.boolean_flag_offset + flag_index) as usize,
+ };
+
+ if head >= interpreted_header.file_size as usize {
+ return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
+ "Flag value offset goes beyond the end of the file."
+ )));
+ }
+
+ Ok(head)
+}
+
+fn get_flag_attribute_and_offset(
+ buf: &mut [u8],
+ flag_type: FlagValueType,
+ flag_index: u32,
+) -> Result<(u8, usize), AconfigStorageError> {
+ let head = get_flag_info_offset(buf, flag_type, flag_index)?;
+ let mut pos = head;
+ let attribute = read_u8_from_bytes(buf, &mut pos)?;
+ Ok((attribute, head))
+}
+
+/// Set if flag is sticky
+pub fn update_flag_is_sticky(
+ buf: &mut [u8],
+ flag_type: FlagValueType,
+ flag_index: u32,
+ value: bool,
+) -> Result<(), AconfigStorageError> {
+ let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
+ let is_sticky = (attribute & (FlagInfoBit::IsSticky as u8)) != 0;
+ if is_sticky != value {
+ buf[head] = (attribute ^ FlagInfoBit::IsSticky as u8).to_le_bytes()[0];
+ }
+ Ok(())
+}
+
+/// Set if flag has override
+pub fn update_flag_has_override(
+ buf: &mut [u8],
+ flag_type: FlagValueType,
+ flag_index: u32,
+ value: bool,
+) -> Result<(), AconfigStorageError> {
+ let (attribute, head) = get_flag_attribute_and_offset(buf, flag_type, flag_index)?;
+ let has_override = (attribute & (FlagInfoBit::HasOverride as u8)) != 0;
+ if has_override != value {
+ buf[head] = (attribute ^ FlagInfoBit::HasOverride as u8).to_le_bytes()[0];
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use aconfig_storage_file::test_utils::create_test_flag_info_list;
+ use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
+
+ #[test]
+ // this test point locks down is sticky update
+ fn test_update_flag_is_sticky() {
+ let flag_info_list = create_test_flag_info_list();
+ let mut buf = flag_info_list.into_bytes();
+ for i in 0..flag_info_list.header.num_flags {
+ update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+ update_flag_is_sticky(&mut buf, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+ }
+ }
+
+ #[test]
+ // this test point locks down has override update
+ fn test_update_flag_has_override() {
+ let flag_info_list = create_test_flag_info_list();
+ let mut buf = flag_info_list.into_bytes();
+ for i in 0..flag_info_list.header.num_flags {
+ update_flag_has_override(&mut buf, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+ update_flag_has_override(&mut buf, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = find_boolean_flag_attribute(&buf, i).unwrap();
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+ }
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
index 4cb7939..0938715 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/flag_value_update.rs
@@ -22,7 +22,7 @@
/// Set flag value
pub fn update_boolean_flag_value(
buf: &mut [u8],
- flag_offset: u32,
+ flag_index: u32,
flag_value: bool,
) -> Result<(), AconfigStorageError> {
let interpreted_header = FlagValueHeader::from_bytes(buf)?;
@@ -34,10 +34,8 @@
)));
}
- let head = (interpreted_header.boolean_value_offset + flag_offset) as usize;
-
- // TODO: right now, there is only boolean flags, with more flag value types added
- // later, the end of boolean flag value section should be updated (b/322826265).
+ // get byte offset to the flag
+ let head = (interpreted_header.boolean_value_offset + flag_index) as usize;
if head >= interpreted_header.file_size as usize {
return Err(AconfigStorageError::InvalidStorageFileOffset(anyhow!(
"Flag value offset goes beyond the end of the file."
@@ -51,20 +49,7 @@
#[cfg(test)]
mod tests {
use super::*;
- use aconfig_storage_file::{FlagValueList, StorageFileType};
-
- pub fn create_test_flag_value_list() -> FlagValueList {
- let header = FlagValueHeader {
- version: 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<bool> = vec![false; 8];
- FlagValueList { header, booleans }
- }
+ use aconfig_storage_file::test_utils::create_test_flag_value_list;
#[test]
// this test point locks down flag value update
diff --git a/tools/aconfig/aconfig_storage_write_api/src/lib.rs b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
index 678bbd5..ee08d16 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/lib.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/lib.rs
@@ -17,6 +17,7 @@
//! `aconfig_storage_write_api` is a crate that defines write apis to update flag value
//! in storage file. It provides one api to interface with storage files.
+pub mod flag_info_update;
pub mod flag_value_update;
pub mod mapped_file;
@@ -24,8 +25,8 @@
mod test_utils;
use aconfig_storage_file::{
- AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, PackageTable,
- StorageFileType, StoredFlagType, FILE_VERSION,
+ AconfigStorageError, FlagInfoHeader, FlagInfoList, FlagInfoNode, FlagTable, FlagValueType,
+ PackageTable, StorageFileType, StoredFlagType, FILE_VERSION,
};
use anyhow::anyhow;
@@ -36,10 +37,11 @@
/// Storage file location pb file
pub const STORAGE_LOCATION_FILE: &str = "/metadata/aconfig/persistent_storage_file_records.pb";
-/// Get mmaped flag value file given the container name
+/// Get read write mapped storage files.
///
/// \input container: the flag package container
-/// \return a result of mapped file
+/// \input file_type: storage file type enum
+/// \return a result of read write mapped file
///
///
/// # Safety
@@ -48,23 +50,64 @@
/// file not thru this memory mapped file or there are concurrent writes to this
/// memory mapped file. Ensure all writes to the underlying file are thru this memory
/// mapped file and there are no concurrent writes.
-pub unsafe fn get_mapped_flag_value_file(container: &str) -> Result<MmapMut, AconfigStorageError> {
- unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container) }
+pub unsafe fn get_mapped_storage_file(
+ container: &str,
+ file_type: StorageFileType,
+) -> Result<MmapMut, AconfigStorageError> {
+ unsafe { crate::mapped_file::get_mapped_file(STORAGE_LOCATION_FILE, container, file_type) }
}
/// Set boolean flag value thru mapped file and flush the change to file
///
/// \input mapped_file: the mapped flag value file
-/// \input offset: flag value offset
+/// \input index: flag index
/// \input value: updated flag value
/// \return a result of ()
///
pub fn set_boolean_flag_value(
file: &mut MmapMut,
- offset: u32,
+ index: u32,
value: bool,
) -> Result<(), AconfigStorageError> {
- crate::flag_value_update::update_boolean_flag_value(file, offset, value)?;
+ crate::flag_value_update::update_boolean_flag_value(file, index, value)?;
+ file.flush().map_err(|errmsg| {
+ AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
+ })
+}
+
+/// Set if flag is sticky thru mapped file and flush the change to file
+///
+/// \input mapped_file: the mapped flag info file
+/// \input index: flag index
+/// \input value: updated flag sticky value
+/// \return a result of ()
+///
+pub fn set_flag_is_sticky(
+ file: &mut MmapMut,
+ flag_type: FlagValueType,
+ index: u32,
+ value: bool,
+) -> Result<(), AconfigStorageError> {
+ crate::flag_info_update::update_flag_is_sticky(file, flag_type, index, value)?;
+ file.flush().map_err(|errmsg| {
+ AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
+ })
+}
+
+/// Set if flag has override thru mapped file and flush the change to file
+///
+/// \input mapped_file: the mapped flag info file
+/// \input index: flag index
+/// \input value: updated flag has override value
+/// \return a result of ()
+///
+pub fn set_flag_has_override(
+ file: &mut MmapMut,
+ flag_type: FlagValueType,
+ index: u32,
+ value: bool,
+) -> Result<(), AconfigStorageError> {
+ crate::flag_info_update::update_flag_has_override(file, flag_type, index, value)?;
file.flush().map_err(|errmsg| {
AconfigStorageError::MapFlushFail(anyhow!("fail to flush storage file: {}", errmsg))
})
@@ -106,15 +149,15 @@
)));
}
- let mut package_offsets = vec![0; package_table.header.num_packages as usize];
+ let mut package_start_index = 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;
+ package_start_index[node.package_id as usize] = node.boolean_start_index;
}
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 flag_index = package_start_index[node.package_id as usize] + node.flag_index as u32;
+ is_flag_rw[flag_index as usize] = node.flag_type == StoredFlagType::ReadWriteBoolean;
}
let mut list = FlagInfoList {
@@ -163,6 +206,18 @@
pub error_message: String,
}
+ // Flag is sticky update return for cc interlop
+ pub struct FlagIsStickyUpdateCXX {
+ pub update_success: bool,
+ pub error_message: String,
+ }
+
+ // Flag has override update return for cc interlop
+ pub struct FlagHasOverrideUpdateCXX {
+ pub update_success: bool,
+ pub error_message: String,
+ }
+
// Flag info file creation return for cc interlop
pub struct FlagInfoCreationCXX {
pub success: bool,
@@ -177,6 +232,20 @@
value: bool,
) -> BooleanFlagValueUpdateCXX;
+ pub fn update_flag_is_sticky_cxx(
+ file: &mut [u8],
+ flag_type: u16,
+ offset: u32,
+ value: bool,
+ ) -> FlagIsStickyUpdateCXX;
+
+ pub fn update_flag_has_override_cxx(
+ file: &mut [u8],
+ flag_type: u16,
+ offset: u32,
+ value: bool,
+ ) -> FlagHasOverrideUpdateCXX;
+
pub fn create_flag_info_cxx(
package_map: &str,
flag_map: &str,
@@ -201,6 +270,58 @@
}
}
+pub(crate) fn update_flag_is_sticky_cxx(
+ file: &mut [u8],
+ flag_type: u16,
+ offset: u32,
+ value: bool,
+) -> ffi::FlagIsStickyUpdateCXX {
+ match FlagValueType::try_from(flag_type) {
+ Ok(value_type) => {
+ match crate::flag_info_update::update_flag_is_sticky(file, value_type, offset, value) {
+ Ok(()) => ffi::FlagIsStickyUpdateCXX {
+ update_success: true,
+ error_message: String::from("")
+ },
+ Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
+ update_success: false,
+ error_message: format!("{:?}", errmsg),
+ },
+ }
+ }
+ Err(errmsg) => ffi::FlagIsStickyUpdateCXX {
+ update_success: false,
+ error_message: format!("{:?}", errmsg),
+ }
+ }
+}
+
+pub(crate) fn update_flag_has_override_cxx(
+ file: &mut [u8],
+ flag_type: u16,
+ offset: u32,
+ value: bool,
+) -> ffi::FlagHasOverrideUpdateCXX {
+ match FlagValueType::try_from(flag_type) {
+ Ok(value_type) => {
+ match crate::flag_info_update::update_flag_has_override(file, value_type, offset, value) {
+ Ok(()) => ffi::FlagHasOverrideUpdateCXX {
+ update_success: true,
+ error_message: String::from("")
+ },
+ Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
+ update_success: false,
+ error_message: format!("{:?}", errmsg),
+ },
+ }
+ }
+ Err(errmsg) => ffi::FlagHasOverrideUpdateCXX {
+ update_success: false,
+ error_message: format!("{:?}", errmsg),
+ }
+ }
+}
+
/// Create flag info file cc interlop
pub(crate) fn create_flag_info_cxx(
package_map: &str,
@@ -224,6 +345,8 @@
create_test_flag_info_list, create_test_flag_table, create_test_package_table,
write_bytes_to_temp_file,
};
+ use aconfig_storage_file::FlagInfoBit;
+ use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
use std::fs::File;
use std::io::Read;
@@ -248,6 +371,7 @@
package_map: "some_package.map"
flag_map: "some_flag.map"
flag_val: "{}"
+ flag_info: "some_flag.info"
timestamp: 12345
}}
"#,
@@ -260,7 +384,12 @@
// The safety here is guaranteed as only this single threaded test process will
// write to this file
unsafe {
- let mut file = crate::mapped_file::get_mapped_file(&record_pb_path, "system").unwrap();
+ let mut file = crate::mapped_file::get_mapped_file(
+ &record_pb_path,
+ "system",
+ StorageFileType::FlagVal,
+ )
+ .unwrap();
for i in 0..8 {
set_boolean_flag_value(&mut file, i, true).unwrap();
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
@@ -273,6 +402,97 @@
}
}
+ fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
+ let mut f = File::open(&file).unwrap();
+ let mut bytes = Vec::new();
+ f.read_to_end(&mut bytes).unwrap();
+ find_boolean_flag_attribute(&bytes, offset).unwrap()
+ }
+
+ #[test]
+ fn test_set_flag_is_sticky() {
+ let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
+ let flag_info_path = flag_info_file.path().display().to_string();
+ let text_proto = format!(
+ r#"
+ files {{
+ version: 0
+ container: "system"
+ package_map: "some_package.map"
+ flag_map: "some_flag.map"
+ flag_val: "some_flag.val"
+ flag_info: "{}"
+ timestamp: 12345
+ }}
+ "#,
+ flag_info_path
+ );
+ let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+ let record_pb_path = record_pb_file.path().display().to_string();
+
+ // SAFETY:
+ // The safety here is guaranteed as only this single threaded test process will
+ // write to this file
+ unsafe {
+ let mut file = crate::mapped_file::get_mapped_file(
+ &record_pb_path,
+ "system",
+ StorageFileType::FlagInfo,
+ )
+ .unwrap();
+ for i in 0..8 {
+ set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+ set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+ }
+ }
+ }
+
+ #[test]
+ fn test_set_flag_has_override() {
+ let flag_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
+ let flag_info_path = flag_info_file.path().display().to_string();
+ let text_proto = format!(
+ r#"
+ files {{
+ version: 0
+ container: "system"
+ package_map: "some_package.map"
+ flag_map: "some_flag.map"
+ flag_val: "some_flag.val"
+ flag_info: "{}"
+ timestamp: 12345
+ }}
+ "#,
+ flag_info_path
+ );
+ let record_pb_file = write_proto_to_temp_file(&text_proto).unwrap();
+ let record_pb_path = record_pb_file.path().display().to_string();
+
+ // SAFETY:
+ // The safety here is guaranteed as only this single threaded test process will
+ // write to this file
+ unsafe {
+ let mut file = crate::mapped_file::get_mapped_file(
+ &record_pb_path,
+ "system",
+ StorageFileType::FlagInfo,
+ )
+ .unwrap();
+ for i in 0..8 {
+ set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+ set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+ }
+ }
+ }
+
fn create_empty_temp_file() -> Result<NamedTempFile, AconfigStorageError> {
let file = NamedTempFile::new().map_err(|_| {
AconfigStorageError::FileCreationFail(anyhow!("Failed to create temp file"))
diff --git a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
index 4c98be4..ea9ac19 100644
--- a/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
+++ b/tools/aconfig/aconfig_storage_write_api/src/mapped_file.rs
@@ -14,43 +14,41 @@
* limitations under the License.
*/
-use std::fs::{self, File, OpenOptions};
-use std::io::{BufReader, Read};
-
use anyhow::anyhow;
use memmap2::MmapMut;
+use std::fs::{self, OpenOptions};
+use std::io::Read;
-use aconfig_storage_file::protos::{storage_record_pb::try_from_binary_proto, ProtoStorageFiles};
-use aconfig_storage_file::AconfigStorageError::{
- self, FileReadFail, MapFileFail, ProtobufParseFail, StorageFileNotFound,
-};
+use aconfig_storage_file::AconfigStorageError::{self, FileReadFail, MapFileFail};
+use aconfig_storage_file::StorageFileType;
+use aconfig_storage_read_api::mapped_file::find_container_storage_location;
-/// Find where persistent storage value file is for a particular container
-fn find_persist_flag_value_file(
- location_pb_file: &str,
- container: &str,
-) -> Result<String, AconfigStorageError> {
- let file = File::open(location_pb_file).map_err(|errmsg| {
- FileReadFail(anyhow!("Failed to open file {}: {}", location_pb_file, errmsg))
- })?;
- let mut reader = BufReader::new(file);
- let mut bytes = Vec::new();
- reader.read_to_end(&mut bytes).map_err(|errmsg| {
- FileReadFail(anyhow!("Failed to read file {}: {}", location_pb_file, errmsg))
- })?;
- let storage_locations: ProtoStorageFiles = try_from_binary_proto(&bytes).map_err(|errmsg| {
- ProtobufParseFail(anyhow!(
- "Failed to parse storage location pb file {}: {}",
- location_pb_file,
- errmsg
- ))
- })?;
- for location_info in storage_locations.files.iter() {
- if location_info.container() == container {
- return Ok(location_info.flag_val().to_string());
- }
+/// Get the mutable memory mapping of a storage file
+///
+/// # Safety
+///
+/// The memory mapped file may have undefined behavior if there are writes to this
+/// file not thru this memory mapped file or there are concurrent writes to this
+/// memory mapped file. Ensure all writes to the underlying file are thru this memory
+/// mapped file and there are no concurrent writes.
+unsafe fn map_file(file_path: &str) -> Result<MmapMut, AconfigStorageError> {
+ // make sure file has read write permission
+ let perms = fs::metadata(file_path).unwrap().permissions();
+ if perms.readonly() {
+ return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path)));
}
- Err(StorageFileNotFound(anyhow!("Persistent flag value file does not exist for {}", container)))
+
+ let file =
+ OpenOptions::new().read(true).write(true).open(file_path).map_err(|errmsg| {
+ FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
+ })?;
+
+ unsafe {
+ let mapped_file = MmapMut::map_mut(&file).map_err(|errmsg| {
+ MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
+ })?;
+ Ok(mapped_file)
+ }
}
/// Get a mapped storage file given the container and file type
@@ -64,24 +62,16 @@
pub unsafe fn get_mapped_file(
location_pb_file: &str,
container: &str,
+ file_type: StorageFileType,
) -> Result<MmapMut, AconfigStorageError> {
- let file_path = find_persist_flag_value_file(location_pb_file, container)?;
-
- // make sure file has read write permission
- let perms = fs::metadata(&file_path).unwrap().permissions();
- if perms.readonly() {
- return Err(MapFileFail(anyhow!("fail to map non read write storage file {}", file_path)));
- }
-
- let file =
- OpenOptions::new().read(true).write(true).open(&file_path).map_err(|errmsg| {
- FileReadFail(anyhow!("Failed to open file {}: {}", file_path, errmsg))
- })?;
-
- unsafe {
- MmapMut::map_mut(&file).map_err(|errmsg| {
- MapFileFail(anyhow!("fail to map storage file {}: {}", file_path, errmsg))
- })
+ let files_location = find_container_storage_location(location_pb_file, container)?;
+ match file_type {
+ StorageFileType::FlagVal => unsafe { map_file(files_location.flag_val()) },
+ StorageFileType::FlagInfo => unsafe { map_file(files_location.flag_info()) },
+ _ => Err(MapFileFail(anyhow!(
+ "Cannot map file type {:?} as writeable memory mapped files.",
+ file_type
+ ))),
}
}
@@ -92,41 +82,9 @@
use aconfig_storage_file::protos::storage_record_pb::write_proto_to_temp_file;
#[test]
- fn test_find_persist_flag_value_file_location() {
- let text_proto = r#"
-files {
- version: 0
- container: "system"
- package_map: "/system/etc/package.map"
- flag_map: "/system/etc/flag.map"
- flag_val: "/metadata/aconfig/system.val"
- timestamp: 12345
-}
-files {
- version: 1
- container: "product"
- package_map: "/product/etc/package.map"
- flag_map: "/product/etc/flag.map"
- flag_val: "/metadata/aconfig/product.val"
- timestamp: 54321
-}
-"#;
- let file = write_proto_to_temp_file(&text_proto).unwrap();
- let file_full_path = file.path().display().to_string();
- let flag_value_file = find_persist_flag_value_file(&file_full_path, "system").unwrap();
- assert_eq!(flag_value_file, "/metadata/aconfig/system.val");
- let flag_value_file = find_persist_flag_value_file(&file_full_path, "product").unwrap();
- assert_eq!(flag_value_file, "/metadata/aconfig/product.val");
- let err = find_persist_flag_value_file(&file_full_path, "vendor").unwrap_err();
- assert_eq!(
- format!("{:?}", err),
- "StorageFileNotFound(Persistent flag value file does not exist for vendor)"
- );
- }
-
- #[test]
fn test_mapped_file_contents() {
- let mut rw_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
+ let mut rw_val_file = copy_to_temp_file("./tests/flag.val", false).unwrap();
+ let mut rw_info_file = copy_to_temp_file("./tests/flag.info", false).unwrap();
let text_proto = format!(
r#"
files {{
@@ -135,21 +93,37 @@
package_map: "some_package.map"
flag_map: "some_flag.map"
flag_val: "{}"
+ flag_info: "{}"
timestamp: 12345
}}
"#,
- rw_file.path().display().to_string()
+ rw_val_file.path().display().to_string(),
+ rw_info_file.path().display().to_string()
);
let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
let storage_record_file_path = storage_record_file.path().display().to_string();
let mut content = Vec::new();
- rw_file.read_to_end(&mut content).unwrap();
+ rw_val_file.read_to_end(&mut content).unwrap();
// SAFETY:
// The safety here is guaranteed here as no writes happens to this temp file
unsafe {
- let mmaped_file = get_mapped_file(&storage_record_file_path, "system").unwrap();
+ let mmaped_file =
+ get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
+ .unwrap();
+ assert_eq!(mmaped_file[..], content[..]);
+ }
+
+ let mut content = Vec::new();
+ rw_info_file.read_to_end(&mut content).unwrap();
+
+ // SAFETY:
+ // The safety here is guaranteed here as no writes happens to this temp file
+ unsafe {
+ let mmaped_file =
+ get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagInfo)
+ .unwrap();
assert_eq!(mmaped_file[..], content[..]);
}
}
@@ -165,6 +139,7 @@
package_map: "some_package.map"
flag_map: "some_flag.map"
flag_val: "{}"
+ flag_info: "some_flag.info"
timestamp: 12345
}}
"#,
@@ -176,7 +151,9 @@
// SAFETY:
// The safety here is guaranteed here as no writes happens to this temp file
unsafe {
- let error = get_mapped_file(&storage_record_file_path, "system").unwrap_err();
+ let error =
+ get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagVal)
+ .unwrap_err();
assert_eq!(
format!("{:?}", error),
format!(
@@ -186,4 +163,49 @@
);
}
}
+
+ #[test]
+ fn test_mapped_not_supported_file() {
+ let text_proto = format!(
+ r#"
+files {{
+ version: 0
+ container: "system"
+ package_map: "some_package.map"
+ flag_map: "some_flag.map"
+ flag_val: "some_flag.val"
+ flag_info: "some_flag.info"
+ timestamp: 12345
+}}
+"#,
+ );
+ let storage_record_file = write_proto_to_temp_file(&text_proto).unwrap();
+ let storage_record_file_path = storage_record_file.path().display().to_string();
+
+ // SAFETY:
+ // The safety here is guaranteed here as no writes happens to this temp file
+ unsafe {
+ let error =
+ get_mapped_file(&storage_record_file_path, "system", StorageFileType::PackageMap)
+ .unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
+ StorageFileType::PackageMap
+ )
+ );
+
+ let error =
+ get_mapped_file(&storage_record_file_path, "system", StorageFileType::FlagMap)
+ .unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ format!(
+ "MapFileFail(Cannot map file type {:?} as writeable memory mapped files.)",
+ StorageFileType::FlagMap
+ )
+ );
+ }
+ }
}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
index 5b23dbc..85568e0 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/Android.bp
@@ -1,8 +1,7 @@
-
rust_test {
name: "aconfig_storage_write_api.test.rust",
srcs: [
- "storage_write_api_test.rs"
+ "storage_write_api_test.rs",
],
rustlibs: [
"libanyhow",
@@ -14,6 +13,7 @@
],
data: [
"flag.val",
+ "flag.info",
],
test_suites: ["general-tests"],
}
@@ -34,6 +34,7 @@
],
data: [
"flag.val",
+ "flag.info",
],
test_suites: [
"device-tests",
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/flag.info b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
new file mode 100644
index 0000000..820d839
--- /dev/null
+++ b/tools/aconfig/aconfig_storage_write_api/tests/flag.info
Binary files differ
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
index 00b737c..77664e4 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.cpp
@@ -50,7 +50,8 @@
return temp_file;
}
- Result<std::string> write_storage_location_pb_file(std::string const& flag_val) {
+ Result<std::string> write_storage_location_pb_file(std::string const& flag_val,
+ std::string const& flag_info) {
auto temp_file = std::tmpnam(nullptr);
auto proto = storage_files();
auto* info = proto.add_files();
@@ -59,6 +60,7 @@
info->set_package_map("some_package.map");
info->set_flag_map("some_flag.map");
info->set_flag_val(flag_val);
+ info->set_flag_info(flag_info);
info->set_timestamp(12345);
auto content = std::string();
@@ -72,22 +74,25 @@
void SetUp() override {
auto const test_dir = android::base::GetExecutableDirectory();
flag_val = *copy_to_rw_temp_file(test_dir + "/flag.val");
- storage_record_pb = *write_storage_location_pb_file(flag_val);
+ flag_info = *copy_to_rw_temp_file(test_dir + "/flag.info");
+ storage_record_pb = *write_storage_location_pb_file(flag_val, flag_info);
}
void TearDown() override {
std::remove(flag_val.c_str());
+ std::remove(flag_info.c_str());
std::remove(storage_record_pb.c_str());
}
std::string flag_val;
+ std::string flag_info;
std::string storage_record_pb;
};
/// Negative test to lock down the error when mapping none exist storage files
TEST_F(AconfigStorageTest, test_none_exist_storage_file_mapping) {
- auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
- storage_record_pb, "vendor");
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "vendor", api::StorageFileType::flag_val);
ASSERT_FALSE(mapped_file_result.ok());
ASSERT_EQ(mapped_file_result.error().message(),
"Unable to find storage files for container vendor");
@@ -96,17 +101,34 @@
/// Negative test to lock down the error when mapping a non writeable storage file
TEST_F(AconfigStorageTest, test_non_writable_storage_file_mapping) {
ASSERT_TRUE(chmod(flag_val.c_str(), S_IRUSR | S_IRGRP | S_IROTH) != -1);
- auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
- storage_record_pb, "mockup");
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_val);
ASSERT_FALSE(mapped_file_result.ok());
auto it = mapped_file_result.error().message().find("cannot map nonwriteable file");
ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
}
+/// Negative test to lock down the error when mapping a file type that cannot be modified
+TEST_F(AconfigStorageTest, test_invalid_storage_file_type_mapping) {
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::package_map);
+ ASSERT_FALSE(mapped_file_result.ok());
+ auto it = mapped_file_result.error().message().find(
+ "Cannot create mutable mapped file for this file type");
+ ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
+
+ mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_map);
+ ASSERT_FALSE(mapped_file_result.ok());
+ it = mapped_file_result.error().message().find(
+ "Cannot create mutable mapped file for this file type");
+ ASSERT_TRUE(it != std::string::npos) << mapped_file_result.error().message();
+}
+
/// Test to lock down storage flag value update api
TEST_F(AconfigStorageTest, test_boolean_flag_value_update) {
- auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
- storage_record_pb, "mockup");
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_val);
ASSERT_TRUE(mapped_file_result.ok());
auto mapped_file = *mapped_file_result;
@@ -124,8 +146,8 @@
/// Negative test to lock down the error when querying flag value out of range
TEST_F(AconfigStorageTest, test_invalid_boolean_flag_value_update) {
- auto mapped_file_result = private_api::get_mapped_flag_value_file_impl(
- storage_record_pb, "mockup");
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_val);
ASSERT_TRUE(mapped_file_result.ok());
auto mapped_file = *mapped_file_result;
auto update_result = api::set_boolean_flag_value(mapped_file, 8, true);
@@ -133,3 +155,61 @@
ASSERT_EQ(update_result.error().message(),
std::string("InvalidStorageFileOffset(Flag value offset goes beyond the end of the file.)"));
}
+
+/// Test to lock down storage flag stickiness update api
+TEST_F(AconfigStorageTest, test_flag_is_sticky_update) {
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ ASSERT_TRUE(mapped_file_result.ok());
+ auto mapped_file = *mapped_file_result;
+
+ for (int offset = 0; offset < 8; ++offset) {
+ auto update_result = api::set_flag_is_sticky(
+ mapped_file, api::FlagValueType::Boolean, offset, true);
+ ASSERT_TRUE(update_result.ok());
+ auto ro_mapped_file = api::MappedStorageFile();
+ ro_mapped_file.file_ptr = mapped_file.file_ptr;
+ ro_mapped_file.file_size = mapped_file.file_size;
+ auto attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+ ASSERT_TRUE(attribute.ok());
+ ASSERT_TRUE(*attribute & api::FlagInfoBit::IsSticky);
+
+ update_result = api::set_flag_is_sticky(
+ mapped_file, api::FlagValueType::Boolean, offset, false);
+ ASSERT_TRUE(update_result.ok());
+ ro_mapped_file.file_ptr = mapped_file.file_ptr;
+ ro_mapped_file.file_size = mapped_file.file_size;
+ attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+ ASSERT_TRUE(attribute.ok());
+ ASSERT_FALSE(*attribute & api::FlagInfoBit::IsSticky);
+ }
+}
+
+/// Test to lock down storage flag has override update api
+TEST_F(AconfigStorageTest, test_flag_has_override_update) {
+ auto mapped_file_result = private_api::get_mutable_mapped_file_impl(
+ storage_record_pb, "mockup", api::StorageFileType::flag_info);
+ ASSERT_TRUE(mapped_file_result.ok());
+ auto mapped_file = *mapped_file_result;
+
+ for (int offset = 0; offset < 8; ++offset) {
+ auto update_result = api::set_flag_has_override(
+ mapped_file, api::FlagValueType::Boolean, offset, true);
+ ASSERT_TRUE(update_result.ok());
+ auto ro_mapped_file = api::MappedStorageFile();
+ ro_mapped_file.file_ptr = mapped_file.file_ptr;
+ ro_mapped_file.file_size = mapped_file.file_size;
+ auto attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+ ASSERT_TRUE(attribute.ok());
+ ASSERT_TRUE(*attribute & api::FlagInfoBit::HasOverride);
+
+ update_result = api::set_flag_has_override(
+ mapped_file, api::FlagValueType::Boolean, offset, false);
+ ASSERT_TRUE(update_result.ok());
+ ro_mapped_file.file_ptr = mapped_file.file_ptr;
+ ro_mapped_file.file_size = mapped_file.file_size;
+ attribute = api::get_boolean_flag_attribute(ro_mapped_file, offset);
+ ASSERT_TRUE(attribute.ok());
+ ASSERT_FALSE(*attribute & api::FlagInfoBit::HasOverride);
+ }
+}
diff --git a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
index 4bda54c..f6a9b280 100644
--- a/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
+++ b/tools/aconfig/aconfig_storage_write_api/tests/storage_write_api_test.rs
@@ -1,8 +1,13 @@
#[cfg(not(feature = "cargo"))]
mod aconfig_storage_write_api_test {
use aconfig_storage_file::protos::ProtoStorageFiles;
+ use aconfig_storage_file::{FlagInfoBit, FlagValueType, StorageFileType};
+ use aconfig_storage_read_api::flag_info_query::find_boolean_flag_attribute;
use aconfig_storage_read_api::flag_value_query::find_boolean_flag_value;
- use aconfig_storage_write_api::{mapped_file::get_mapped_file, set_boolean_flag_value};
+ use aconfig_storage_write_api::{
+ mapped_file::get_mapped_file, set_boolean_flag_value, set_flag_has_override,
+ set_flag_is_sticky,
+ };
use protobuf::Message;
use std::fs::{self, File};
@@ -10,7 +15,7 @@
use tempfile::NamedTempFile;
/// Write storage location record pb to a temp file
- fn write_storage_record_file(flag_val: &str) -> NamedTempFile {
+ fn write_storage_record_file(flag_val: &str, flag_info: &str) -> NamedTempFile {
let text_proto = format!(
r#"
files {{
@@ -19,10 +24,11 @@
package_map: "some_package_map"
flag_map: "some_flag_map"
flag_val: "{}"
+ flag_info: "{}"
timestamp: 12345
}}
"#,
- flag_val
+ flag_val, flag_info
);
let storage_files: ProtoStorageFiles =
protobuf::text_format::parse_from_str(&text_proto).unwrap();
@@ -48,18 +54,30 @@
find_boolean_flag_value(&bytes, offset).unwrap()
}
+ /// Get flag attribute at offset
+ fn get_flag_attribute_at_offset(file: &str, offset: u32) -> u8 {
+ let mut f = File::open(file).unwrap();
+ let mut bytes = Vec::new();
+ f.read_to_end(&mut bytes).unwrap();
+ find_boolean_flag_attribute(&bytes, offset).unwrap()
+ }
+
#[test]
/// Test to lock down flag value update api
fn test_boolean_flag_value_update() {
let flag_value_file = copy_to_temp_rw_file("./flag.val");
+ let flag_info_file = copy_to_temp_rw_file("./flag.info");
let flag_value_path = flag_value_file.path().display().to_string();
- let record_pb_file = write_storage_record_file(&flag_value_path);
+ let flag_info_path = flag_info_file.path().display().to_string();
+ let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
let record_pb_path = record_pb_file.path().display().to_string();
// SAFETY:
// The safety here is ensured as only this single threaded test process will
// write to this file
- let mut file = unsafe { get_mapped_file(&record_pb_path, "mockup").unwrap() };
+ let mut file = unsafe {
+ get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagVal).unwrap()
+ };
for i in 0..8 {
set_boolean_flag_value(&mut file, i, true).unwrap();
let value = get_boolean_flag_value_at_offset(&flag_value_path, i);
@@ -70,4 +88,56 @@
assert!(!value);
}
}
+
+ #[test]
+ /// Test to lock down flag is sticky update api
+ fn test_set_flag_is_sticky() {
+ let flag_value_file = copy_to_temp_rw_file("./flag.val");
+ let flag_info_file = copy_to_temp_rw_file("./flag.info");
+ let flag_value_path = flag_value_file.path().display().to_string();
+ let flag_info_path = flag_info_file.path().display().to_string();
+ let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
+ let record_pb_path = record_pb_file.path().display().to_string();
+
+ // SAFETY:
+ // The safety here is ensured as only this single threaded test process will
+ // write to this file
+ let mut file = unsafe {
+ get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
+ };
+ for i in 0..8 {
+ set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) != 0);
+ set_flag_is_sticky(&mut file, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::IsSticky as u8)) == 0);
+ }
+ }
+
+ #[test]
+ /// Test to lock down flag is sticky update api
+ fn test_set_flag_has_override() {
+ let flag_value_file = copy_to_temp_rw_file("./flag.val");
+ let flag_info_file = copy_to_temp_rw_file("./flag.info");
+ let flag_value_path = flag_value_file.path().display().to_string();
+ let flag_info_path = flag_info_file.path().display().to_string();
+ let record_pb_file = write_storage_record_file(&flag_value_path, &flag_info_path);
+ let record_pb_path = record_pb_file.path().display().to_string();
+
+ // SAFETY:
+ // The safety here is ensured as only this single threaded test process will
+ // write to this file
+ let mut file = unsafe {
+ get_mapped_file(&record_pb_path, "mockup", StorageFileType::FlagInfo).unwrap()
+ };
+ for i in 0..8 {
+ set_flag_has_override(&mut file, FlagValueType::Boolean, i, true).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) != 0);
+ set_flag_has_override(&mut file, FlagValueType::Boolean, i, false).unwrap();
+ let attribute = get_flag_attribute_at_offset(&flag_info_path, i);
+ assert!((attribute & (FlagInfoBit::HasOverride as u8)) == 0);
+ }
+ }
}
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 67438e6..2c5fe0d 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1288,7 +1288,7 @@
assert len(words) >= 1 and len(words) <= 2
OPTIONS.vabc_compression_param = a.lower()
if len(words) == 2:
- if not words[1].isdigit():
+ if not words[1].lstrip("-").isdigit():
raise ValueError("Cannot parse value %r for option $COMPRESSION_LEVEL - only "
"integers are allowed." % words[1])
elif o == "--security_patch_level":