Merge "aconfig: add a new aconfig storage file flag listing api" into main
This commit is contained in:
@@ -97,5 +97,9 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"postsubmit": [
|
"postsubmit": [
|
||||||
|
{
|
||||||
|
// aconfig_storage file cpp integration tests
|
||||||
|
"name": "aconfig_storage_file.test.cpp"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -12,6 +12,7 @@ rust_defaults {
|
|||||||
"libtempfile",
|
"libtempfile",
|
||||||
"libprotobuf",
|
"libprotobuf",
|
||||||
"libclap",
|
"libclap",
|
||||||
|
"libcxx",
|
||||||
"libaconfig_storage_protos",
|
"libaconfig_storage_protos",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@@ -77,3 +78,62 @@ cc_library {
|
|||||||
product_available: true,
|
product_available: true,
|
||||||
double_loadable: true,
|
double_loadable: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cxx source codegen from rust api
|
||||||
|
genrule {
|
||||||
|
name: "libcxx_aconfig_storage_file_bridge_code",
|
||||||
|
tools: ["cxxbridge"],
|
||||||
|
cmd: "$(location cxxbridge) $(in) > $(out)",
|
||||||
|
srcs: ["src/lib.rs"],
|
||||||
|
out: ["aconfig_storage/lib.rs.cc"],
|
||||||
|
}
|
||||||
|
|
||||||
|
// cxx header codegen from rust api
|
||||||
|
genrule {
|
||||||
|
name: "libcxx_aconfig_storage_file_bridge_header",
|
||||||
|
tools: ["cxxbridge"],
|
||||||
|
cmd: "$(location cxxbridge) $(in) --header > $(out)",
|
||||||
|
srcs: ["src/lib.rs"],
|
||||||
|
out: ["aconfig_storage/lib.rs.h"],
|
||||||
|
}
|
||||||
|
|
||||||
|
// a static cc lib based on generated code
|
||||||
|
rust_ffi_static {
|
||||||
|
name: "libaconfig_storage_file_cxx_bridge",
|
||||||
|
crate_name: "aconfig_storage_file_cxx_bridge",
|
||||||
|
host_supported: true,
|
||||||
|
vendor_available: true,
|
||||||
|
product_available: true,
|
||||||
|
srcs: ["src/lib.rs"],
|
||||||
|
defaults: ["aconfig_storage_file.defaults"],
|
||||||
|
apex_available: [
|
||||||
|
"//apex_available:platform",
|
||||||
|
"//apex_available:anyapex",
|
||||||
|
],
|
||||||
|
min_sdk_version: "29",
|
||||||
|
}
|
||||||
|
|
||||||
|
// storage file parse api cc interface
|
||||||
|
cc_library {
|
||||||
|
name: "libaconfig_storage_file_cc",
|
||||||
|
srcs: ["aconfig_storage_file.cpp"],
|
||||||
|
generated_headers: [
|
||||||
|
"cxx-bridge-header",
|
||||||
|
"libcxx_aconfig_storage_file_bridge_header",
|
||||||
|
],
|
||||||
|
generated_sources: ["libcxx_aconfig_storage_file_bridge_code"],
|
||||||
|
whole_static_libs: ["libaconfig_storage_file_cxx_bridge"],
|
||||||
|
export_include_dirs: ["include"],
|
||||||
|
host_supported: true,
|
||||||
|
vendor_available: true,
|
||||||
|
product_available: true,
|
||||||
|
shared_libs: [
|
||||||
|
"libbase",
|
||||||
|
],
|
||||||
|
apex_available: [
|
||||||
|
"//apex_available:platform",
|
||||||
|
"//apex_available:anyapex",
|
||||||
|
],
|
||||||
|
min_sdk_version: "29",
|
||||||
|
double_loadable: true,
|
||||||
|
}
|
||||||
|
@@ -13,6 +13,7 @@ protobuf = "3.2.0"
|
|||||||
tempfile = "3.9.0"
|
tempfile = "3.9.0"
|
||||||
thiserror = "1.0.56"
|
thiserror = "1.0.56"
|
||||||
clap = { version = "4.1.8", features = ["derive"] }
|
clap = { version = "4.1.8", features = ["derive"] }
|
||||||
|
cxx = "1.0"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "aconfig-storage"
|
name = "aconfig-storage"
|
||||||
|
38
tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
Normal file
38
tools/aconfig/aconfig_storage_file/aconfig_storage_file.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#include "rust/cxx.h"
|
||||||
|
#include "aconfig_storage/lib.rs.h"
|
||||||
|
|
||||||
|
#include "aconfig_storage/aconfig_storage_file.hpp"
|
||||||
|
|
||||||
|
using namespace android::base;
|
||||||
|
|
||||||
|
namespace aconfig_storage {
|
||||||
|
|
||||||
|
Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
|
||||||
|
const std::string& package_map,
|
||||||
|
const std::string& flag_map,
|
||||||
|
const std::string& flag_val,
|
||||||
|
const std::string& flag_info) {
|
||||||
|
auto flag_list_cxx = list_flags_with_info_cxx(rust::Str(package_map.c_str()),
|
||||||
|
rust::Str(flag_map.c_str()),
|
||||||
|
rust::Str(flag_val.c_str()),
|
||||||
|
rust::Str(flag_info.c_str()));
|
||||||
|
if (flag_list_cxx.query_success) {
|
||||||
|
auto flag_list = std::vector<FlagValueAndInfoSummary>();
|
||||||
|
for (const auto& flag_cxx : flag_list_cxx.flags) {
|
||||||
|
auto flag = FlagValueAndInfoSummary();
|
||||||
|
flag.package_name = std::string(flag_cxx.package_name);
|
||||||
|
flag.flag_name = std::string(flag_cxx.flag_name);
|
||||||
|
flag.flag_value = std::string(flag_cxx.flag_value);
|
||||||
|
flag.value_type = std::string(flag_cxx.value_type);
|
||||||
|
flag.is_readwrite = flag_cxx.is_readwrite;
|
||||||
|
flag.has_server_override = flag_cxx.has_server_override;
|
||||||
|
flag.has_local_override = flag_cxx.has_local_override;
|
||||||
|
flag_list.push_back(flag);
|
||||||
|
}
|
||||||
|
return flag_list;
|
||||||
|
} else {
|
||||||
|
return Error() << flag_list_cxx.error_message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aconfig_storage
|
@@ -14,4 +14,6 @@ fn main() {
|
|||||||
.inputs(proto_files)
|
.inputs(proto_files)
|
||||||
.cargo_out_dir("aconfig_storage_protos")
|
.cargo_out_dir("aconfig_storage_protos")
|
||||||
.run_from_script();
|
.run_from_script();
|
||||||
|
|
||||||
|
let _ = cxx_build::bridge("src/lib.rs");
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <android-base/result.h>
|
||||||
|
|
||||||
|
namespace aconfig_storage {
|
||||||
|
|
||||||
|
/// Flag value and info summary for a flag
|
||||||
|
struct FlagValueAndInfoSummary {
|
||||||
|
std::string package_name;
|
||||||
|
std::string flag_name;
|
||||||
|
std::string flag_value;
|
||||||
|
std::string value_type;
|
||||||
|
bool is_readwrite;
|
||||||
|
bool has_server_override;
|
||||||
|
bool has_local_override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// List all flag values with their flag info
|
||||||
|
/// \input package_map: package map file
|
||||||
|
/// \input flag_map: flag map file
|
||||||
|
/// \input flag_val: flag value file
|
||||||
|
/// \input flag_info: flag info file
|
||||||
|
android::base::Result<std::vector<FlagValueAndInfoSummary>> list_flags_with_info(
|
||||||
|
const std::string& package_map,
|
||||||
|
const std::string& flag_map,
|
||||||
|
const std::string& flag_val,
|
||||||
|
const std::string& flag_info);
|
||||||
|
|
||||||
|
}// namespace aconfig_storage
|
@@ -278,12 +278,21 @@ pub fn read_file_to_bytes(file_path: &str) -> Result<Vec<u8>, AconfigStorageErro
|
|||||||
Ok(buffer)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flag value summary
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct FlagValueSummary {
|
||||||
|
pub package_name: String,
|
||||||
|
pub flag_name: String,
|
||||||
|
pub flag_value: String,
|
||||||
|
pub value_type: StoredFlagType,
|
||||||
|
}
|
||||||
|
|
||||||
/// List flag values from storage files
|
/// List flag values from storage files
|
||||||
pub fn list_flags(
|
pub fn list_flags(
|
||||||
package_map: &str,
|
package_map: &str,
|
||||||
flag_map: &str,
|
flag_map: &str,
|
||||||
flag_val: &str,
|
flag_val: &str,
|
||||||
) -> Result<Vec<(String, String, StoredFlagType, bool)>, AconfigStorageError> {
|
) -> Result<Vec<FlagValueSummary>, AconfigStorageError> {
|
||||||
let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
|
let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
|
||||||
let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
|
let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
|
||||||
let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
|
let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
|
||||||
@@ -295,30 +304,155 @@ pub fn list_flags(
|
|||||||
|
|
||||||
let mut flags = Vec::new();
|
let mut flags = Vec::new();
|
||||||
for node in flag_table.nodes.iter() {
|
for node in flag_table.nodes.iter() {
|
||||||
let (package_name, package_offset) = package_info[node.package_id as usize];
|
let (package_name, boolean_start_index) = package_info[node.package_id as usize];
|
||||||
let flag_offset = package_offset + node.flag_index as u32;
|
let flag_index = boolean_start_index + node.flag_index as u32;
|
||||||
let flag_value = flag_value_list.booleans[flag_offset as usize];
|
let flag_value = flag_value_list.booleans[flag_index as usize];
|
||||||
flags.push((
|
flags.push(FlagValueSummary {
|
||||||
String::from(package_name),
|
package_name: String::from(package_name),
|
||||||
node.flag_name.clone(),
|
flag_name: node.flag_name.clone(),
|
||||||
node.flag_type,
|
flag_value: flag_value.to_string(),
|
||||||
flag_value,
|
value_type: node.flag_type,
|
||||||
));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
flags.sort_by(|v1, v2| match v1.0.cmp(&v2.0) {
|
flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
|
||||||
Ordering::Equal => v1.1.cmp(&v2.1),
|
Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
|
||||||
other => other,
|
other => other,
|
||||||
});
|
});
|
||||||
Ok(flags)
|
Ok(flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flag value and info summary
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub struct FlagValueAndInfoSummary {
|
||||||
|
pub package_name: String,
|
||||||
|
pub flag_name: String,
|
||||||
|
pub flag_value: String,
|
||||||
|
pub value_type: StoredFlagType,
|
||||||
|
pub is_readwrite: bool,
|
||||||
|
pub has_server_override: bool,
|
||||||
|
pub has_local_override: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List flag values and info from storage files
|
||||||
|
pub fn list_flags_with_info(
|
||||||
|
package_map: &str,
|
||||||
|
flag_map: &str,
|
||||||
|
flag_val: &str,
|
||||||
|
flag_info: &str,
|
||||||
|
) -> Result<Vec<FlagValueAndInfoSummary>, AconfigStorageError> {
|
||||||
|
let package_table = PackageTable::from_bytes(&read_file_to_bytes(package_map)?)?;
|
||||||
|
let flag_table = FlagTable::from_bytes(&read_file_to_bytes(flag_map)?)?;
|
||||||
|
let flag_value_list = FlagValueList::from_bytes(&read_file_to_bytes(flag_val)?)?;
|
||||||
|
let flag_info = FlagInfoList::from_bytes(&read_file_to_bytes(flag_info)?)?;
|
||||||
|
|
||||||
|
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_start_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut flags = Vec::new();
|
||||||
|
for node in flag_table.nodes.iter() {
|
||||||
|
let (package_name, boolean_start_index) = package_info[node.package_id as usize];
|
||||||
|
let flag_index = boolean_start_index + node.flag_index as u32;
|
||||||
|
let flag_value = flag_value_list.booleans[flag_index as usize];
|
||||||
|
let flag_attribute = flag_info.nodes[flag_index as usize].attributes;
|
||||||
|
flags.push(FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from(package_name),
|
||||||
|
flag_name: node.flag_name.clone(),
|
||||||
|
flag_value: flag_value.to_string(),
|
||||||
|
value_type: node.flag_type,
|
||||||
|
is_readwrite: flag_attribute & (FlagInfoBit::IsReadWrite as u8) != 0,
|
||||||
|
has_server_override: flag_attribute & (FlagInfoBit::HasServerOverride as u8) != 0,
|
||||||
|
has_local_override: flag_attribute & (FlagInfoBit::HasLocalOverride as u8) != 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.sort_by(|v1, v2| match v1.package_name.cmp(&v2.package_name) {
|
||||||
|
Ordering::Equal => v1.flag_name.cmp(&v2.flag_name),
|
||||||
|
other => other,
|
||||||
|
});
|
||||||
|
Ok(flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// *************************************** //
|
||||||
|
// CC INTERLOP
|
||||||
|
// *************************************** //
|
||||||
|
|
||||||
|
// Exported rust data structure and methods, c++ code will be generated
|
||||||
|
#[cxx::bridge]
|
||||||
|
mod ffi {
|
||||||
|
/// flag value and info summary cxx return
|
||||||
|
pub struct FlagValueAndInfoSummaryCXX {
|
||||||
|
pub package_name: String,
|
||||||
|
pub flag_name: String,
|
||||||
|
pub flag_value: String,
|
||||||
|
pub value_type: String,
|
||||||
|
pub is_readwrite: bool,
|
||||||
|
pub has_server_override: bool,
|
||||||
|
pub has_local_override: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// list flag result cxx return
|
||||||
|
pub struct ListFlagValueAndInfoResultCXX {
|
||||||
|
pub query_success: bool,
|
||||||
|
pub error_message: String,
|
||||||
|
pub flags: Vec<FlagValueAndInfoSummaryCXX>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rust export to c++
|
||||||
|
extern "Rust" {
|
||||||
|
pub fn list_flags_with_info_cxx(
|
||||||
|
package_map: &str,
|
||||||
|
flag_map: &str,
|
||||||
|
flag_val: &str,
|
||||||
|
flag_info: &str,
|
||||||
|
) -> ListFlagValueAndInfoResultCXX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// implement flag value and info summary cxx return type
|
||||||
|
impl ffi::FlagValueAndInfoSummaryCXX {
|
||||||
|
pub(crate) fn new(summary: FlagValueAndInfoSummary) -> Self {
|
||||||
|
Self {
|
||||||
|
package_name: summary.package_name,
|
||||||
|
flag_name: summary.flag_name,
|
||||||
|
flag_value: summary.flag_value,
|
||||||
|
value_type: format!("{:?}", summary.value_type),
|
||||||
|
is_readwrite: summary.is_readwrite,
|
||||||
|
has_server_override: summary.has_server_override,
|
||||||
|
has_local_override: summary.has_local_override,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// implement list flag with info cxx interlop
|
||||||
|
pub fn list_flags_with_info_cxx(
|
||||||
|
package_map: &str,
|
||||||
|
flag_map: &str,
|
||||||
|
flag_val: &str,
|
||||||
|
flag_info: &str,
|
||||||
|
) -> ffi::ListFlagValueAndInfoResultCXX {
|
||||||
|
match list_flags_with_info(package_map, flag_map, flag_val, flag_info) {
|
||||||
|
Ok(summary) => ffi::ListFlagValueAndInfoResultCXX {
|
||||||
|
query_success: true,
|
||||||
|
error_message: String::new(),
|
||||||
|
flags: summary.into_iter().map(ffi::FlagValueAndInfoSummaryCXX::new).collect(),
|
||||||
|
},
|
||||||
|
Err(errmsg) => ffi::ListFlagValueAndInfoResultCXX {
|
||||||
|
query_success: false,
|
||||||
|
error_message: format!("{:?}", errmsg),
|
||||||
|
flags: Vec::new(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::{
|
use crate::test_utils::{
|
||||||
create_test_flag_table, create_test_flag_value_list, create_test_package_table,
|
create_test_flag_info_list, create_test_flag_table, create_test_flag_value_list,
|
||||||
write_bytes_to_temp_file,
|
create_test_package_table, write_bytes_to_temp_file,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -337,54 +471,154 @@ mod tests {
|
|||||||
let flags =
|
let flags =
|
||||||
list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
|
list_flags(&package_table_path, &flag_table_path, &flag_value_list_path).unwrap();
|
||||||
let expected = [
|
let expected = [
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_1"),
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
String::from("disabled_rw"),
|
flag_name: String::from("disabled_rw"),
|
||||||
StoredFlagType::ReadWriteBoolean,
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
false,
|
flag_value: String::from("false"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_1"),
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
String::from("enabled_ro"),
|
flag_name: String::from("enabled_ro"),
|
||||||
StoredFlagType::ReadOnlyBoolean,
|
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_1"),
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
String::from("enabled_rw"),
|
flag_name: String::from("enabled_rw"),
|
||||||
StoredFlagType::ReadWriteBoolean,
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_2"),
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
String::from("disabled_rw"),
|
flag_name: String::from("disabled_rw"),
|
||||||
StoredFlagType::ReadWriteBoolean,
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
false,
|
flag_value: String::from("false"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_2"),
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
String::from("enabled_fixed_ro"),
|
flag_name: String::from("enabled_fixed_ro"),
|
||||||
StoredFlagType::FixedReadOnlyBoolean,
|
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_2"),
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
String::from("enabled_ro"),
|
flag_name: String::from("enabled_ro"),
|
||||||
StoredFlagType::ReadOnlyBoolean,
|
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_4"),
|
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||||
String::from("enabled_fixed_ro"),
|
flag_name: String::from("enabled_fixed_ro"),
|
||||||
StoredFlagType::FixedReadOnlyBoolean,
|
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
(
|
FlagValueSummary {
|
||||||
String::from("com.android.aconfig.storage.test_4"),
|
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||||
String::from("enabled_rw"),
|
flag_name: String::from("enabled_rw"),
|
||||||
StoredFlagType::ReadWriteBoolean,
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
true,
|
flag_value: String::from("true"),
|
||||||
),
|
},
|
||||||
|
];
|
||||||
|
assert_eq!(flags, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// this test point locks down the flag list with info api
|
||||||
|
fn test_list_flag_with_info() {
|
||||||
|
let package_table =
|
||||||
|
write_bytes_to_temp_file(&create_test_package_table().into_bytes()).unwrap();
|
||||||
|
let flag_table = write_bytes_to_temp_file(&create_test_flag_table().into_bytes()).unwrap();
|
||||||
|
let flag_value_list =
|
||||||
|
write_bytes_to_temp_file(&create_test_flag_value_list().into_bytes()).unwrap();
|
||||||
|
let flag_info_list =
|
||||||
|
write_bytes_to_temp_file(&create_test_flag_info_list().into_bytes()).unwrap();
|
||||||
|
|
||||||
|
let package_table_path = package_table.path().display().to_string();
|
||||||
|
let flag_table_path = flag_table.path().display().to_string();
|
||||||
|
let flag_value_list_path = flag_value_list.path().display().to_string();
|
||||||
|
let flag_info_list_path = flag_info_list.path().display().to_string();
|
||||||
|
|
||||||
|
let flags = list_flags_with_info(
|
||||||
|
&package_table_path,
|
||||||
|
&flag_table_path,
|
||||||
|
&flag_value_list_path,
|
||||||
|
&flag_info_list_path,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let expected = [
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
|
flag_name: String::from("disabled_rw"),
|
||||||
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
|
flag_value: String::from("false"),
|
||||||
|
is_readwrite: true,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
|
flag_name: String::from("enabled_ro"),
|
||||||
|
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: false,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_1"),
|
||||||
|
flag_name: String::from("enabled_rw"),
|
||||||
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: true,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
|
flag_name: String::from("disabled_rw"),
|
||||||
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
|
flag_value: String::from("false"),
|
||||||
|
is_readwrite: true,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
|
flag_name: String::from("enabled_fixed_ro"),
|
||||||
|
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: false,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_2"),
|
||||||
|
flag_name: String::from("enabled_ro"),
|
||||||
|
value_type: StoredFlagType::ReadOnlyBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: false,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||||
|
flag_name: String::from("enabled_fixed_ro"),
|
||||||
|
value_type: StoredFlagType::FixedReadOnlyBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: false,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
|
FlagValueAndInfoSummary {
|
||||||
|
package_name: String::from("com.android.aconfig.storage.test_4"),
|
||||||
|
flag_name: String::from("enabled_rw"),
|
||||||
|
value_type: StoredFlagType::ReadWriteBoolean,
|
||||||
|
flag_value: String::from("true"),
|
||||||
|
is_readwrite: true,
|
||||||
|
has_server_override: false,
|
||||||
|
has_local_override: false,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
assert_eq!(flags, expected);
|
assert_eq!(flags, expected);
|
||||||
}
|
}
|
||||||
|
@@ -17,8 +17,8 @@
|
|||||||
//! `aconfig-storage` is a debugging tool to parse storage files
|
//! `aconfig-storage` is a debugging tool to parse storage files
|
||||||
|
|
||||||
use aconfig_storage_file::{
|
use aconfig_storage_file::{
|
||||||
list_flags, read_file_to_bytes, AconfigStorageError, FlagInfoList, FlagTable, FlagValueList,
|
list_flags, list_flags_with_info, read_file_to_bytes, AconfigStorageError, FlagInfoList,
|
||||||
PackageTable, StorageFileType,
|
FlagTable, FlagValueList, PackageTable, StorageFileType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use clap::{builder::ArgAction, Arg, Command};
|
use clap::{builder::ArgAction, Arg, Command};
|
||||||
@@ -45,7 +45,10 @@ fn cli() -> Command {
|
|||||||
.action(ArgAction::Set),
|
.action(ArgAction::Set),
|
||||||
)
|
)
|
||||||
.arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
|
.arg(Arg::new("flag-map").long("flag-map").required(true).action(ArgAction::Set))
|
||||||
.arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set)),
|
.arg(Arg::new("flag-val").long("flag-val").required(true).action(ArgAction::Set))
|
||||||
|
.arg(
|
||||||
|
Arg::new("flag-info").long("flag-info").required(false).action(ArgAction::Set),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,9 +90,27 @@ fn main() -> Result<(), AconfigStorageError> {
|
|||||||
let package_map = sub_matches.get_one::<String>("package-map").unwrap();
|
let package_map = sub_matches.get_one::<String>("package-map").unwrap();
|
||||||
let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
|
let flag_map = sub_matches.get_one::<String>("flag-map").unwrap();
|
||||||
let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
|
let flag_val = sub_matches.get_one::<String>("flag-val").unwrap();
|
||||||
let flags = list_flags(package_map, flag_map, flag_val)?;
|
let flag_info = sub_matches.get_one::<String>("flag-info");
|
||||||
for (package_name, flag_name, flag_type, flag_value) in flags.iter() {
|
match flag_info {
|
||||||
println!("{} {} {:?} {}", package_name, flag_name, flag_type, flag_value);
|
Some(info_file) => {
|
||||||
|
let flags = list_flags_with_info(package_map, flag_map, flag_val, info_file)?;
|
||||||
|
for flag in flags.iter() {
|
||||||
|
println!(
|
||||||
|
"{} {} {} {:?} IsReadWrite: {}, HasServerOverride: {}, HasLocalOverride: {}",
|
||||||
|
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||||
|
flag.is_readwrite, flag.has_server_override, flag.has_local_override,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let flags = list_flags(package_map, flag_map, flag_val)?;
|
||||||
|
for flag in flags.iter() {
|
||||||
|
println!(
|
||||||
|
"{} {} {} {:?}",
|
||||||
|
flag.package_name, flag.flag_name, flag.flag_value, flag.value_type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
23
tools/aconfig/aconfig_storage_file/tests/Android.bp
Normal file
23
tools/aconfig/aconfig_storage_file/tests/Android.bp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
cc_test {
|
||||||
|
name: "aconfig_storage_file.test.cpp",
|
||||||
|
team: "trendy_team_android_core_experiments",
|
||||||
|
srcs: [
|
||||||
|
"storage_file_test.cpp",
|
||||||
|
],
|
||||||
|
static_libs: [
|
||||||
|
"libgmock",
|
||||||
|
"libaconfig_storage_file_cc",
|
||||||
|
"libbase",
|
||||||
|
],
|
||||||
|
data: [
|
||||||
|
"package.map",
|
||||||
|
"flag.map",
|
||||||
|
"flag.val",
|
||||||
|
"flag.info",
|
||||||
|
],
|
||||||
|
test_suites: [
|
||||||
|
"device-tests",
|
||||||
|
"general-tests",
|
||||||
|
],
|
||||||
|
}
|
BIN
tools/aconfig/aconfig_storage_file/tests/flag.info
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.info
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/flag.map
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.map
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/flag.val
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/flag.val
Normal file
Binary file not shown.
BIN
tools/aconfig/aconfig_storage_file/tests/package.map
Normal file
BIN
tools/aconfig/aconfig_storage_file/tests/package.map
Normal file
Binary file not shown.
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <android-base/file.h>
|
||||||
|
#include <android-base/result.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "aconfig_storage/aconfig_storage_file.hpp"
|
||||||
|
|
||||||
|
using namespace android::base;
|
||||||
|
using namespace aconfig_storage;
|
||||||
|
|
||||||
|
|
||||||
|
void verify_flag(const FlagValueAndInfoSummary& flag,
|
||||||
|
const std::string& package_name,
|
||||||
|
const std::string& flag_name,
|
||||||
|
const std::string& flag_val,
|
||||||
|
const std::string& value_type,
|
||||||
|
bool is_readwrite,
|
||||||
|
bool has_server_override,
|
||||||
|
bool has_local_override) {
|
||||||
|
ASSERT_EQ(flag.package_name, package_name);
|
||||||
|
ASSERT_EQ(flag.flag_name, flag_name);
|
||||||
|
ASSERT_EQ(flag.flag_value, flag_val);
|
||||||
|
ASSERT_EQ(flag.value_type, value_type);
|
||||||
|
ASSERT_EQ(flag.is_readwrite, is_readwrite);
|
||||||
|
ASSERT_EQ(flag.has_server_override, has_server_override);
|
||||||
|
ASSERT_EQ(flag.has_local_override, has_local_override);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(AconfigStorageFileTest, test_list_flag_with_info) {
|
||||||
|
auto const test_dir = GetExecutableDirectory();
|
||||||
|
auto const package_map = test_dir + "/package.map";
|
||||||
|
auto const flag_map = test_dir + "/flag.map";
|
||||||
|
auto const flag_val = test_dir + "/flag.val";
|
||||||
|
auto const flag_info = test_dir + "/flag.info";
|
||||||
|
auto flag_list_result = aconfig_storage::list_flags_with_info(
|
||||||
|
package_map, flag_map, flag_val, flag_info);
|
||||||
|
ASSERT_TRUE(flag_list_result.ok());
|
||||||
|
|
||||||
|
auto const& flag_list = *flag_list_result;
|
||||||
|
ASSERT_EQ(flag_list.size(), 8);
|
||||||
|
verify_flag(flag_list[0], "com.android.aconfig.storage.test_1", "disabled_rw",
|
||||||
|
"false", "ReadWriteBoolean", true, false, false);
|
||||||
|
verify_flag(flag_list[1], "com.android.aconfig.storage.test_1", "enabled_ro",
|
||||||
|
"true", "ReadOnlyBoolean", false, false, false);
|
||||||
|
verify_flag(flag_list[2], "com.android.aconfig.storage.test_1", "enabled_rw",
|
||||||
|
"true", "ReadWriteBoolean", true, false, false);
|
||||||
|
verify_flag(flag_list[3], "com.android.aconfig.storage.test_2", "disabled_rw",
|
||||||
|
"false", "ReadWriteBoolean", true, false, false);
|
||||||
|
verify_flag(flag_list[4], "com.android.aconfig.storage.test_2", "enabled_fixed_ro",
|
||||||
|
"true", "FixedReadOnlyBoolean", false, false, false);
|
||||||
|
verify_flag(flag_list[5], "com.android.aconfig.storage.test_2", "enabled_ro",
|
||||||
|
"true", "ReadOnlyBoolean", false, false, false);
|
||||||
|
verify_flag(flag_list[6], "com.android.aconfig.storage.test_4", "enabled_fixed_ro",
|
||||||
|
"true", "FixedReadOnlyBoolean", false, false, false);
|
||||||
|
verify_flag(flag_list[7], "com.android.aconfig.storage.test_4", "enabled_rw",
|
||||||
|
"true", "ReadWriteBoolean", true, false, false);
|
||||||
|
}
|
@@ -504,7 +504,7 @@ files {{
|
|||||||
let pb_file_path = pb_file.path().display().to_string();
|
let pb_file_path = pb_file.path().display().to_string();
|
||||||
let flag_info_file =
|
let flag_info_file =
|
||||||
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
|
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];
|
let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
|
||||||
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
||||||
let attribute =
|
let attribute =
|
||||||
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
||||||
|
Binary file not shown.
@@ -232,7 +232,7 @@ TEST_F(AconfigStorageTest, test_boolean_flag_info_query) {
|
|||||||
ASSERT_TRUE(mapped_file.ok());
|
ASSERT_TRUE(mapped_file.ok());
|
||||||
|
|
||||||
auto expected_value = std::vector<bool>{
|
auto expected_value = std::vector<bool>{
|
||||||
true, false, true, false, false, false, false, false};
|
true, false, true, true, false, false, false, true};
|
||||||
for (int index = 0; index < 8; ++index) {
|
for (int index = 0; index < 8; ++index) {
|
||||||
auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
|
auto attribute = api::get_flag_attribute(*mapped_file, api::FlagValueType::Boolean, index);
|
||||||
ASSERT_TRUE(attribute.ok());
|
ASSERT_TRUE(attribute.ok());
|
||||||
|
@@ -188,7 +188,7 @@ files {{
|
|||||||
// The safety here is ensured as the test process will not write to temp storage file
|
// The safety here is ensured as the test process will not write to temp storage file
|
||||||
let flag_info_file =
|
let flag_info_file =
|
||||||
unsafe { get_mapped_file(&pb_file_path, "mockup", StorageFileType::FlagInfo).unwrap() };
|
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];
|
let is_rw: Vec<bool> = vec![true, false, true, true, false, false, false, true];
|
||||||
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
for (offset, expected_value) in is_rw.into_iter().enumerate() {
|
||||||
let attribute =
|
let attribute =
|
||||||
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
get_flag_attribute(&flag_info_file, FlagValueType::Boolean, offset as u32).unwrap();
|
||||||
|
@@ -27,13 +27,13 @@ impl FlagSource for AconfigStorageSource {
|
|||||||
let container =
|
let container =
|
||||||
file_info.container.ok_or(anyhow!("storage file is missing container"))?;
|
file_info.container.ok_or(anyhow!("storage file is missing container"))?;
|
||||||
|
|
||||||
for (package, name, _flag_type, val) in
|
for listed_flag in
|
||||||
aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
|
aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)?
|
||||||
{
|
{
|
||||||
result.push(Flag {
|
result.push(Flag {
|
||||||
name: name.to_string(),
|
name: listed_flag.flag_name,
|
||||||
package: package.to_string(),
|
package: listed_flag.package_name,
|
||||||
value: FlagValue::try_from(val.to_string().as_str())?,
|
value: FlagValue::try_from(listed_flag.flag_value.as_str())?,
|
||||||
container: container.to_string(),
|
container: container.to_string(),
|
||||||
|
|
||||||
// TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
|
// TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
|
||||||
|
Reference in New Issue
Block a user