Merge "DO NOT MERGE: manually cherry pick" into main
This commit is contained in:
@@ -45,6 +45,8 @@ where
|
||||
let header = package.replace('.', "_");
|
||||
let package_macro = header.to_uppercase();
|
||||
let cpp_namespace = package.replace('.', "::");
|
||||
ensure!(class_elements.len() > 0);
|
||||
let container = class_elements[0].container.clone();
|
||||
ensure!(codegen::is_valid_name_ident(&header));
|
||||
let context = Context {
|
||||
header: &header,
|
||||
@@ -56,6 +58,7 @@ where
|
||||
readwrite_count,
|
||||
is_test_mode: codegen_mode == CodegenMode::Test,
|
||||
class_elements,
|
||||
container,
|
||||
allow_instrumentation,
|
||||
};
|
||||
|
||||
@@ -100,6 +103,7 @@ pub struct Context<'a> {
|
||||
pub readwrite_count: i32,
|
||||
pub is_test_mode: bool,
|
||||
pub class_elements: Vec<ClassElement>,
|
||||
pub container: String,
|
||||
pub allow_instrumentation: bool,
|
||||
}
|
||||
|
||||
|
@@ -20,26 +20,34 @@ use tinytemplate::TinyTemplate;
|
||||
|
||||
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::codegen;
|
||||
use crate::codegen::CodegenMode;
|
||||
use crate::commands::OutputFile;
|
||||
|
||||
pub fn generate_rust_code<I>(
|
||||
package: &str,
|
||||
flag_ids: HashMap<String, u16>,
|
||||
parsed_flags_iter: I,
|
||||
codegen_mode: CodegenMode,
|
||||
allow_instrumentation: bool,
|
||||
) -> Result<OutputFile>
|
||||
where
|
||||
I: Iterator<Item = ProtoParsedFlag>,
|
||||
{
|
||||
let template_flags: Vec<TemplateParsedFlag> =
|
||||
parsed_flags_iter.map(|pf| TemplateParsedFlag::new(package, &pf)).collect();
|
||||
let template_flags: Vec<TemplateParsedFlag> = parsed_flags_iter
|
||||
.map(|pf| TemplateParsedFlag::new(package, flag_ids.clone(), &pf))
|
||||
.collect();
|
||||
let has_readwrite = template_flags.iter().any(|item| item.readwrite);
|
||||
let container = (template_flags.first().expect("zero template flags").container).to_string();
|
||||
let context = TemplateContext {
|
||||
package: package.to_string(),
|
||||
template_flags,
|
||||
modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
|
||||
has_readwrite,
|
||||
allow_instrumentation,
|
||||
container,
|
||||
};
|
||||
let mut template = TinyTemplate::new();
|
||||
template.add_template(
|
||||
@@ -62,6 +70,8 @@ struct TemplateContext {
|
||||
pub template_flags: Vec<TemplateParsedFlag>,
|
||||
pub modules: Vec<String>,
|
||||
pub has_readwrite: bool,
|
||||
pub allow_instrumentation: bool,
|
||||
pub container: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -69,25 +79,28 @@ struct TemplateParsedFlag {
|
||||
pub readwrite: bool,
|
||||
pub default_value: String,
|
||||
pub name: String,
|
||||
pub container: String,
|
||||
pub flag_offset: u16,
|
||||
pub device_config_namespace: String,
|
||||
pub device_config_flag: String,
|
||||
}
|
||||
|
||||
impl TemplateParsedFlag {
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
fn new(package: &str, pf: &ProtoParsedFlag) -> Self {
|
||||
let template = TemplateParsedFlag {
|
||||
fn new(package: &str, flag_offsets: HashMap<String, u16>, pf: &ProtoParsedFlag) -> Self {
|
||||
Self {
|
||||
readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
|
||||
default_value: match pf.state() {
|
||||
ProtoFlagState::ENABLED => "true".to_string(),
|
||||
ProtoFlagState::DISABLED => "false".to_string(),
|
||||
},
|
||||
name: pf.name().to_string(),
|
||||
container: pf.container().to_string(),
|
||||
flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("),
|
||||
device_config_namespace: pf.namespace().to_string(),
|
||||
device_config_flag: codegen::create_device_config_ident(package, pf.name())
|
||||
.expect("values checked at flag parse time"),
|
||||
};
|
||||
template
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +110,14 @@ mod tests {
|
||||
|
||||
const PROD_EXPECTED: &str = r#"
|
||||
//! codegenerated rust flag lib
|
||||
use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use log::{log, LevelFilter, Level};
|
||||
|
||||
static STORAGE_MIGRATION_MARKER_FILE: &str =
|
||||
"/metadata/aconfig_test_missions/mission_1";
|
||||
static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
|
||||
|
||||
/// flag provider
|
||||
pub struct FlagProvider;
|
||||
@@ -226,6 +247,697 @@ pub fn enabled_ro_exported() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// query flag enabled_rw
|
||||
#[inline(always)]
|
||||
pub fn enabled_rw() -> bool {
|
||||
PROVIDER.enabled_rw()
|
||||
}
|
||||
"#;
|
||||
|
||||
const PROD_INSTRUMENTED_EXPECTED: &str = r#"
|
||||
//! codegenerated rust flag lib
|
||||
use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use log::{log, LevelFilter, Level};
|
||||
|
||||
static STORAGE_MIGRATION_MARKER_FILE: &str =
|
||||
"/metadata/aconfig_test_missions/mission_1";
|
||||
static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
|
||||
|
||||
/// flag provider
|
||||
pub struct FlagProvider;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
static ref PACKAGE_OFFSET: Result<Option<u32>, AconfigStorageError> = unsafe {
|
||||
get_mapped_storage_file("system", StorageFileType::PackageMap)
|
||||
.and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test"))
|
||||
.map(|context| context.map(|c| c.boolean_start_index))
|
||||
};
|
||||
|
||||
static ref FLAG_VAL_MAP: Result<Mmap, AconfigStorageError> = unsafe {
|
||||
get_mapped_storage_file("system", StorageFileType::FlagVal)
|
||||
};
|
||||
/// flag value cache for disabled_rw
|
||||
|
||||
static ref CACHED_disabled_rw: bool = {
|
||||
let result = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.aconfig_test",
|
||||
"com.android.aconfig.test.disabled_rw",
|
||||
"false") == "true";
|
||||
|
||||
let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
|
||||
if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
// This will be called multiple times. Subsequent calls after the first are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info));
|
||||
|
||||
let aconfig_storage_result = FLAG_VAL_MAP
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get flag val map: {err}"))
|
||||
.and_then(|flag_val_map| {
|
||||
PACKAGE_OFFSET
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get package read offset: {err}"))
|
||||
.and_then(|package_offset| {
|
||||
match package_offset {
|
||||
Some(offset) => {
|
||||
get_boolean_flag_value(&flag_val_map, offset + 1)
|
||||
.map_err(|err| format!("failed to get flag: {err}"))
|
||||
},
|
||||
None => Err("no context found for package 'com.android.aconfig.test'".to_string())
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match aconfig_storage_result {
|
||||
Ok(storage_result) if storage_result == result => {
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Ok(storage_result) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: {err}");
|
||||
if use_new_storage_value {
|
||||
panic!("failed to read flag value: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
/// flag value cache for disabled_rw_exported
|
||||
|
||||
static ref CACHED_disabled_rw_exported: bool = {
|
||||
let result = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.aconfig_test",
|
||||
"com.android.aconfig.test.disabled_rw_exported",
|
||||
"false") == "true";
|
||||
|
||||
let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
|
||||
if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
// This will be called multiple times. Subsequent calls after the first are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info));
|
||||
|
||||
let aconfig_storage_result = FLAG_VAL_MAP
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get flag val map: {err}"))
|
||||
.and_then(|flag_val_map| {
|
||||
PACKAGE_OFFSET
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get package read offset: {err}"))
|
||||
.and_then(|package_offset| {
|
||||
match package_offset {
|
||||
Some(offset) => {
|
||||
get_boolean_flag_value(&flag_val_map, offset + 2)
|
||||
.map_err(|err| format!("failed to get flag: {err}"))
|
||||
},
|
||||
None => Err("no context found for package 'com.android.aconfig.test'".to_string())
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match aconfig_storage_result {
|
||||
Ok(storage_result) if storage_result == result => {
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Ok(storage_result) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_exported'. Legacy storage was {result}, new storage was {storage_result}");
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: {err}");
|
||||
if use_new_storage_value {
|
||||
panic!("failed to read flag value: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
/// flag value cache for disabled_rw_in_other_namespace
|
||||
|
||||
static ref CACHED_disabled_rw_in_other_namespace: bool = {
|
||||
let result = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.other_namespace",
|
||||
"com.android.aconfig.test.disabled_rw_in_other_namespace",
|
||||
"false") == "true";
|
||||
|
||||
let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
|
||||
if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
// This will be called multiple times. Subsequent calls after the first are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info));
|
||||
|
||||
let aconfig_storage_result = FLAG_VAL_MAP
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get flag val map: {err}"))
|
||||
.and_then(|flag_val_map| {
|
||||
PACKAGE_OFFSET
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get package read offset: {err}"))
|
||||
.and_then(|package_offset| {
|
||||
match package_offset {
|
||||
Some(offset) => {
|
||||
get_boolean_flag_value(&flag_val_map, offset + 3)
|
||||
.map_err(|err| format!("failed to get flag: {err}"))
|
||||
},
|
||||
None => Err("no context found for package 'com.android.aconfig.test'".to_string())
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match aconfig_storage_result {
|
||||
Ok(storage_result) if storage_result == result => {
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Ok(storage_result) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_in_other_namespace'. Legacy storage was {result}, new storage was {storage_result}");
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: {err}");
|
||||
if use_new_storage_value {
|
||||
panic!("failed to read flag value: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
/// flag value cache for enabled_rw
|
||||
|
||||
static ref CACHED_enabled_rw: bool = {
|
||||
let result = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.aconfig_test",
|
||||
"com.android.aconfig.test.enabled_rw",
|
||||
"true") == "true";
|
||||
|
||||
let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
|
||||
if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
// This will be called multiple times. Subsequent calls after the first are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info));
|
||||
|
||||
let aconfig_storage_result = FLAG_VAL_MAP
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get flag val map: {err}"))
|
||||
.and_then(|flag_val_map| {
|
||||
PACKAGE_OFFSET
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get package read offset: {err}"))
|
||||
.and_then(|package_offset| {
|
||||
match package_offset {
|
||||
Some(offset) => {
|
||||
get_boolean_flag_value(&flag_val_map, offset + 8)
|
||||
.map_err(|err| format!("failed to get flag: {err}"))
|
||||
},
|
||||
None => Err("no context found for package 'com.android.aconfig.test'".to_string())
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match aconfig_storage_result {
|
||||
Ok(storage_result) if storage_result == result => {
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Ok(storage_result) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'enabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
|
||||
if use_new_storage_value {
|
||||
return storage_result;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: {err}");
|
||||
if use_new_storage_value {
|
||||
panic!("failed to read flag value: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
impl FlagProvider {
|
||||
|
||||
|
||||
/// query flag disabled_ro
|
||||
pub fn disabled_ro(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// query flag disabled_rw
|
||||
pub fn disabled_rw(&self) -> bool {
|
||||
*CACHED_disabled_rw
|
||||
}
|
||||
|
||||
/// query flag disabled_rw_exported
|
||||
pub fn disabled_rw_exported(&self) -> bool {
|
||||
*CACHED_disabled_rw_exported
|
||||
}
|
||||
|
||||
/// query flag disabled_rw_in_other_namespace
|
||||
pub fn disabled_rw_in_other_namespace(&self) -> bool {
|
||||
*CACHED_disabled_rw_in_other_namespace
|
||||
}
|
||||
|
||||
/// query flag enabled_fixed_ro
|
||||
pub fn enabled_fixed_ro(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// query flag enabled_fixed_ro_exported
|
||||
pub fn enabled_fixed_ro_exported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// query flag enabled_ro
|
||||
pub fn enabled_ro(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// query flag enabled_ro_exported
|
||||
pub fn enabled_ro_exported(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// query flag enabled_rw
|
||||
pub fn enabled_rw(&self) -> bool {
|
||||
*CACHED_enabled_rw
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// flag provider
|
||||
pub static PROVIDER: FlagProvider = FlagProvider;
|
||||
|
||||
|
||||
/// query flag disabled_ro
|
||||
#[inline(always)]
|
||||
pub fn disabled_ro() -> bool {
|
||||
|
||||
|
||||
let result = false;
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, 0 + package_read_context.boolean_start_index) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value {
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'disabled_ro'. Legacy storage was {result}, new storage was {value}");
|
||||
} else {
|
||||
let default_value = false;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
}
|
||||
|
||||
/// query flag disabled_rw
|
||||
#[inline(always)]
|
||||
pub fn disabled_rw() -> bool {
|
||||
PROVIDER.disabled_rw()
|
||||
}
|
||||
|
||||
/// query flag disabled_rw_exported
|
||||
#[inline(always)]
|
||||
pub fn disabled_rw_exported() -> bool {
|
||||
PROVIDER.disabled_rw_exported()
|
||||
}
|
||||
|
||||
/// query flag disabled_rw_in_other_namespace
|
||||
#[inline(always)]
|
||||
pub fn disabled_rw_in_other_namespace() -> bool {
|
||||
PROVIDER.disabled_rw_in_other_namespace()
|
||||
}
|
||||
|
||||
/// query flag enabled_fixed_ro
|
||||
#[inline(always)]
|
||||
pub fn enabled_fixed_ro() -> bool {
|
||||
|
||||
|
||||
let result = true;
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, 4 + package_read_context.boolean_start_index) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value {
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro'. Legacy storage was {result}, new storage was {value}");
|
||||
} else {
|
||||
let default_value = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
}
|
||||
|
||||
/// query flag enabled_fixed_ro_exported
|
||||
#[inline(always)]
|
||||
pub fn enabled_fixed_ro_exported() -> bool {
|
||||
|
||||
|
||||
let result = true;
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, 5 + package_read_context.boolean_start_index) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value {
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro_exported'. Legacy storage was {result}, new storage was {value}");
|
||||
} else {
|
||||
let default_value = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
}
|
||||
|
||||
/// query flag enabled_ro
|
||||
#[inline(always)]
|
||||
pub fn enabled_ro() -> bool {
|
||||
|
||||
|
||||
let result = true;
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, 6 + package_read_context.boolean_start_index) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value {
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro'. Legacy storage was {result}, new storage was {value}");
|
||||
} else {
|
||||
let default_value = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
}
|
||||
|
||||
/// query flag enabled_ro_exported
|
||||
#[inline(always)]
|
||||
pub fn enabled_ro_exported() -> bool {
|
||||
|
||||
|
||||
let result = true;
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
|
||||
Ok(file) => file,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, 7 + package_read_context.boolean_start_index) {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value {
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro_exported'. Legacy storage was {result}, new storage was {value}");
|
||||
} else {
|
||||
let default_value = true;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
|
||||
}
|
||||
|
||||
/// query flag enabled_rw
|
||||
#[inline(always)]
|
||||
pub fn enabled_rw() -> bool {
|
||||
@@ -492,6 +1204,14 @@ pub fn reset_flags() {
|
||||
|
||||
const EXPORTED_EXPECTED: &str = r#"
|
||||
//! codegenerated rust flag lib
|
||||
use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use log::{log, LevelFilter, Level};
|
||||
|
||||
static STORAGE_MIGRATION_MARKER_FILE: &str =
|
||||
"/metadata/aconfig_test_missions/mission_1";
|
||||
static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
|
||||
|
||||
/// flag provider
|
||||
pub struct FlagProvider;
|
||||
@@ -558,6 +1278,14 @@ pub fn enabled_ro_exported() -> bool {
|
||||
|
||||
const FORCE_READ_ONLY_EXPECTED: &str = r#"
|
||||
//! codegenerated rust flag lib
|
||||
use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use log::{log, LevelFilter, Level};
|
||||
|
||||
static STORAGE_MIGRATION_MARKER_FILE: &str =
|
||||
"/metadata/aconfig_test_missions/mission_1";
|
||||
static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
|
||||
|
||||
/// flag provider
|
||||
pub struct FlagProvider;
|
||||
@@ -633,24 +1361,27 @@ pub fn enabled_rw() -> bool {
|
||||
true
|
||||
}
|
||||
"#;
|
||||
use crate::commands::assign_flag_ids;
|
||||
|
||||
fn test_generate_rust_code(mode: CodegenMode) {
|
||||
fn test_generate_rust_code(mode: CodegenMode, allow_instrumentation: bool, expected: &str) {
|
||||
let parsed_flags = crate::test::parse_test_flags();
|
||||
let modified_parsed_flags =
|
||||
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
|
||||
let generated =
|
||||
generate_rust_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode)
|
||||
let flag_ids =
|
||||
assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
|
||||
let generated = generate_rust_code(
|
||||
crate::test::TEST_PACKAGE,
|
||||
flag_ids,
|
||||
modified_parsed_flags.into_iter(),
|
||||
mode,
|
||||
allow_instrumentation,
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
|
||||
assert_eq!(
|
||||
None,
|
||||
crate::test::first_significant_code_diff(
|
||||
match mode {
|
||||
CodegenMode::Production => PROD_EXPECTED,
|
||||
CodegenMode::Test => TEST_EXPECTED,
|
||||
CodegenMode::Exported => EXPORTED_EXPECTED,
|
||||
CodegenMode::ForceReadOnly => FORCE_READ_ONLY_EXPECTED,
|
||||
},
|
||||
expected,
|
||||
&String::from_utf8(generated.contents).unwrap()
|
||||
)
|
||||
);
|
||||
@@ -658,21 +1389,26 @@ pub fn enabled_rw() -> bool {
|
||||
|
||||
#[test]
|
||||
fn test_generate_rust_code_for_prod() {
|
||||
test_generate_rust_code(CodegenMode::Production);
|
||||
test_generate_rust_code(CodegenMode::Production, false, PROD_EXPECTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_rust_code_for_prod_instrumented() {
|
||||
test_generate_rust_code(CodegenMode::Production, true, PROD_INSTRUMENTED_EXPECTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_rust_code_for_test() {
|
||||
test_generate_rust_code(CodegenMode::Test);
|
||||
test_generate_rust_code(CodegenMode::Test, false, TEST_EXPECTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_rust_code_for_exported() {
|
||||
test_generate_rust_code(CodegenMode::Exported);
|
||||
test_generate_rust_code(CodegenMode::Exported, false, EXPORTED_EXPECTED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_rust_code_for_force_read_only() {
|
||||
test_generate_rust_code(CodegenMode::ForceReadOnly);
|
||||
test_generate_rust_code(CodegenMode::ForceReadOnly, false, FORCE_READ_ONLY_EXPECTED);
|
||||
}
|
||||
}
|
||||
|
@@ -238,7 +238,11 @@ pub fn create_cpp_lib(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<OutputFile> {
|
||||
pub fn create_rust_lib(
|
||||
mut input: Input,
|
||||
codegen_mode: CodegenMode,
|
||||
allow_instrumentation: bool,
|
||||
) -> Result<OutputFile> {
|
||||
// // TODO(327420679): Enable export mode for native flag library
|
||||
ensure!(
|
||||
codegen_mode != CodegenMode::Exported,
|
||||
@@ -250,8 +254,14 @@ pub fn create_rust_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Ou
|
||||
bail!("no parsed flags, or the parsed flags use different packages");
|
||||
};
|
||||
let package = package.to_string();
|
||||
let _flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
|
||||
generate_rust_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
|
||||
let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
|
||||
generate_rust_code(
|
||||
&package,
|
||||
flag_ids,
|
||||
modified_parsed_flags.into_iter(),
|
||||
codegen_mode,
|
||||
allow_instrumentation,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_storage(
|
||||
|
@@ -101,6 +101,12 @@ fn cli() -> Command {
|
||||
Command::new("create-rust-lib")
|
||||
.arg(Arg::new("cache").long("cache").required(true))
|
||||
.arg(Arg::new("out").long("out").required(true))
|
||||
.arg(
|
||||
Arg::new("allow-instrumentation")
|
||||
.long("allow-instrumentation")
|
||||
.value_parser(clap::value_parser!(bool))
|
||||
.default_value("false"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("mode")
|
||||
.long("mode")
|
||||
@@ -267,8 +273,10 @@ fn main() -> Result<()> {
|
||||
Some(("create-rust-lib", sub_matches)) => {
|
||||
let cache = open_single_file(sub_matches, "cache")?;
|
||||
let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
|
||||
let generated_file =
|
||||
commands::create_rust_lib(cache, *mode).context("failed to create rust lib")?;
|
||||
let allow_instrumentation =
|
||||
get_required_arg::<bool>(sub_matches, "allow-instrumentation")?;
|
||||
let generated_file = commands::create_rust_lib(cache, *mode, *allow_instrumentation)
|
||||
.context("failed to create rust lib")?;
|
||||
let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
|
||||
write_output_file_realtive_to_dir(&dir, &generated_file)?;
|
||||
}
|
||||
|
@@ -1,13 +1,13 @@
|
||||
#include "{header}.h"
|
||||
|
||||
{{ if allow_instrumentation }}
|
||||
{{ if readwrite- }}
|
||||
#include <sys/stat.h>
|
||||
#include "aconfig_storage/aconfig_storage_read_api.hpp"
|
||||
#include <android/log.h>
|
||||
|
||||
#define ALOGI(msg, ...) \
|
||||
__android_log_print(ANDROID_LOG_INFO, "AconfigTestMission1", (msg), __VA_ARGS__)
|
||||
|
||||
#define LOG_TAG "aconfig_cpp_codegen"
|
||||
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
||||
{{ -endif }}
|
||||
{{ endif }}
|
||||
|
||||
{{ if readwrite- }}
|
||||
@@ -66,8 +66,68 @@ namespace {cpp_namespace} \{
|
||||
class flag_provider : public flag_provider_interface \{
|
||||
public:
|
||||
|
||||
{{ -for item in class_elements }}
|
||||
{{ if allow_instrumentation- }}
|
||||
{{ if readwrite- }}
|
||||
flag_provider()
|
||||
{{ if readwrite- }}
|
||||
: cache_({readwrite_count}, -1)
|
||||
, boolean_start_index_()
|
||||
{{ -else- }}
|
||||
: boolean_start_index_()
|
||||
{{ -endif }}
|
||||
, flag_value_file_(nullptr)
|
||||
, read_from_new_storage_(false)
|
||||
, use_new_storage_value(false) \{
|
||||
|
||||
struct stat buffer;
|
||||
if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) == 0) \{
|
||||
read_from_new_storage_ = true;
|
||||
} else \{
|
||||
return;
|
||||
}
|
||||
|
||||
auto package_map_file = aconfig_storage::get_mapped_file(
|
||||
"{container}",
|
||||
aconfig_storage::StorageFileType::package_map);
|
||||
if (!package_map_file.ok()) \{
|
||||
ALOGI("error: failed to get package map file: %s", package_map_file.error().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto context = aconfig_storage::get_package_read_context(
|
||||
**package_map_file, "{package}");
|
||||
if (!context.ok()) \{
|
||||
ALOGI("error: failed to get package read context: %s", context.error().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// cache package boolean flag start index
|
||||
boolean_start_index_ = context->boolean_start_index;
|
||||
|
||||
// unmap package map file and free memory
|
||||
delete *package_map_file;
|
||||
|
||||
auto flag_value_file = aconfig_storage::get_mapped_file(
|
||||
"{container}",
|
||||
aconfig_storage::StorageFileType::flag_val);
|
||||
if (!flag_value_file.ok()) \{
|
||||
ALOGI("error: failed to get flag value file: %s", flag_value_file.error().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// cache flag value file
|
||||
flag_value_file_ = std::unique_ptr<aconfig_storage::MappedStorageFile>(
|
||||
*flag_value_file);
|
||||
|
||||
use_new_storage_value = server_configurable_flags::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
}
|
||||
{{ -endif }}
|
||||
{{ -endif }}
|
||||
|
||||
{{ -for item in class_elements }}
|
||||
virtual bool {item.flag_name}() override \{
|
||||
{{ -if item.readwrite }}
|
||||
if (cache_[{item.readwrite_idx}] == -1) \{
|
||||
@@ -76,6 +136,39 @@ namespace {cpp_namespace} \{
|
||||
"{item.device_config_flag}",
|
||||
"{item.default_value}") == "true";
|
||||
}
|
||||
|
||||
|
||||
{{ if allow_instrumentation- }}
|
||||
if (read_from_new_storage_) \{
|
||||
if (!flag_value_file_) \{
|
||||
ALOGI("error: failed to get flag {item.flag_name}: flag value file is null");
|
||||
return cache_[{item.readwrite_idx}];
|
||||
}
|
||||
|
||||
auto value = aconfig_storage::get_boolean_flag_value(
|
||||
*flag_value_file_,
|
||||
boolean_start_index_ + {item.flag_offset});
|
||||
|
||||
if (!value.ok()) \{
|
||||
ALOGI("error: failed to read flag value: %s", value.error().c_str());
|
||||
return cache_[{item.readwrite_idx}];
|
||||
}
|
||||
|
||||
bool expected_value = cache_[{item.readwrite_idx}];
|
||||
if (*value != expected_value) \{
|
||||
ALOGI("error: {item.flag_name} value mismatch, new storage value is %s, old storage value is %s",
|
||||
*value ? "true" : "false", expected_value ? "true" : "false");
|
||||
}
|
||||
|
||||
if (use_new_storage_value) \{
|
||||
return *value;
|
||||
} else \{
|
||||
return expected_value;
|
||||
}
|
||||
}
|
||||
{{ -endif }}
|
||||
|
||||
|
||||
return cache_[{item.readwrite_idx}];
|
||||
{{ -else }}
|
||||
{{ -if item.is_fixed_read_only }}
|
||||
@@ -86,12 +179,21 @@ namespace {cpp_namespace} \{
|
||||
{{ -endif }}
|
||||
}
|
||||
{{ -endfor }}
|
||||
|
||||
{{ if readwrite- }}
|
||||
private:
|
||||
std::vector<int8_t> cache_ = std::vector<int8_t>({readwrite_count}, -1);
|
||||
{{ -endif }}
|
||||
};
|
||||
{{ if allow_instrumentation- }}
|
||||
uint32_t boolean_start_index_;
|
||||
|
||||
std::unique_ptr<aconfig_storage::MappedStorageFile> flag_value_file_;
|
||||
|
||||
bool read_from_new_storage_;
|
||||
bool use_new_storage_value;
|
||||
{{ -endif }}
|
||||
{{ -endif }}
|
||||
|
||||
};
|
||||
|
||||
{{ -endif }}
|
||||
|
||||
@@ -107,62 +209,6 @@ bool {header}_{item.flag_name}() \{
|
||||
{{ -if item.readwrite }}
|
||||
return {cpp_namespace}::{item.flag_name}();
|
||||
{{ -else }}
|
||||
{{ if allow_instrumentation }}
|
||||
auto result =
|
||||
{{ if item.is_fixed_read_only }}
|
||||
{package_macro}_{item.flag_macro}
|
||||
{{ else }}
|
||||
{item.default_value}
|
||||
{{ endif }};
|
||||
|
||||
struct stat buffer;
|
||||
if (stat("/metadata/aconfig_test_missions/mission_1", &buffer) != 0) \{
|
||||
return result;
|
||||
}
|
||||
|
||||
auto package_map_file = aconfig_storage::get_mapped_file(
|
||||
"{item.container}",
|
||||
aconfig_storage::StorageFileType::package_map);
|
||||
if (!package_map_file.ok()) \{
|
||||
ALOGI("error: failed to get package map file: %s", package_map_file.error().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
auto package_read_context = aconfig_storage::get_package_read_context(
|
||||
**package_map_file, "{package}");
|
||||
if (!package_read_context.ok()) \{
|
||||
ALOGI("error: failed to get package read context: %s", package_map_file.error().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
delete *package_map_file;
|
||||
|
||||
auto flag_val_map = aconfig_storage::get_mapped_file(
|
||||
"{item.container}",
|
||||
aconfig_storage::StorageFileType::flag_val);
|
||||
if (!flag_val_map.ok()) \{
|
||||
ALOGI("error: failed to get flag val map: %s", package_map_file.error().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
auto value = aconfig_storage::get_boolean_flag_value(
|
||||
**flag_val_map,
|
||||
package_read_context->boolean_start_index + {item.flag_offset});
|
||||
if (!value.ok()) \{
|
||||
ALOGI("error: failed to get flag val: %s", package_map_file.error().c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
delete *flag_val_map;
|
||||
|
||||
if (*value != result) \{
|
||||
ALOGI("error: new storage value '%d' does not match current value '%d'", *value, result);
|
||||
} else \{
|
||||
ALOGI("success: new storage value was '%d, legacy storage was '%d'", *value, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
{{ else }}
|
||||
{{ -if item.is_fixed_read_only }}
|
||||
return {package_macro}_{item.flag_macro};
|
||||
{{ -else }}
|
||||
@@ -170,7 +216,6 @@ bool {header}_{item.flag_name}() \{
|
||||
{{ -endif }}
|
||||
{{ -endif }}
|
||||
{{ -endif }}
|
||||
{{ -endif }}
|
||||
}
|
||||
|
||||
{{ -if is_test_mode }}
|
||||
@@ -185,5 +230,3 @@ void {header}_reset_flags() \{
|
||||
{cpp_namespace}::reset_flags();
|
||||
}
|
||||
{{ -endif }}
|
||||
|
||||
|
||||
|
@@ -1,17 +1,103 @@
|
||||
//! codegenerated rust flag lib
|
||||
use aconfig_storage_read_api::\{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
|
||||
use std::path::Path;
|
||||
use std::io::Write;
|
||||
use log::\{log, LevelFilter, Level};
|
||||
|
||||
static STORAGE_MIGRATION_MARKER_FILE: &str =
|
||||
"/metadata/aconfig_test_missions/mission_1";
|
||||
static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
|
||||
|
||||
/// flag provider
|
||||
pub struct FlagProvider;
|
||||
|
||||
{{ if has_readwrite- }}
|
||||
lazy_static::lazy_static! \{
|
||||
{{ if allow_instrumentation }}
|
||||
static ref PACKAGE_OFFSET: Result<Option<u32>, AconfigStorageError> = unsafe \{
|
||||
get_mapped_storage_file("{container}", StorageFileType::PackageMap)
|
||||
.and_then(|package_map| get_package_read_context(&package_map, "{package}"))
|
||||
.map(|context| context.map(|c| c.boolean_start_index))
|
||||
};
|
||||
|
||||
static ref FLAG_VAL_MAP: Result<Mmap, AconfigStorageError> = unsafe \{
|
||||
get_mapped_storage_file("{container}", StorageFileType::FlagVal)
|
||||
};
|
||||
{{ -endif }}
|
||||
|
||||
{{ -for flag in template_flags }}
|
||||
{{ -if flag.readwrite }}
|
||||
/// flag value cache for {flag.name}
|
||||
{{ if allow_instrumentation }}
|
||||
static ref CACHED_{flag.name}: bool = \{
|
||||
let result = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.{flag.device_config_namespace}",
|
||||
"{flag.device_config_flag}",
|
||||
"{flag.default_value}") == "true";
|
||||
|
||||
let use_new_storage_value = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.core_experiments_team_internal",
|
||||
"com.android.providers.settings.use_new_storage_value",
|
||||
"false") == "true";
|
||||
|
||||
if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{
|
||||
// This will be called multiple times. Subsequent calls after the first are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info));
|
||||
|
||||
let aconfig_storage_result = FLAG_VAL_MAP
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get flag val map: \{err}"))
|
||||
.and_then(|flag_val_map| \{
|
||||
PACKAGE_OFFSET
|
||||
.as_ref()
|
||||
.map_err(|err| format!("failed to get package read offset: \{err}"))
|
||||
.and_then(|package_offset| \{
|
||||
match package_offset \{
|
||||
Some(offset) => \{
|
||||
get_boolean_flag_value(&flag_val_map, offset + {flag.flag_offset})
|
||||
.map_err(|err| format!("failed to get flag: \{err}"))
|
||||
},
|
||||
None => Err("no context found for package '{package}'".to_string())
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
match aconfig_storage_result \{
|
||||
Ok(storage_result) if storage_result == result => \{
|
||||
if use_new_storage_value \{
|
||||
return storage_result;
|
||||
} else \{
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Ok(storage_result) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: mismatch for flag '{flag.name}'. Legacy storage was \{result}, new storage was \{storage_result}");
|
||||
if use_new_storage_value \{
|
||||
return storage_result;
|
||||
} else \{
|
||||
return result;
|
||||
}
|
||||
},
|
||||
Err(err) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: \{err}");
|
||||
if use_new_storage_value \{
|
||||
panic!("failed to read flag value: \{err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
};
|
||||
{{ else }}
|
||||
static ref CACHED_{flag.name}: bool = flags_rust::GetServerConfigurableFlag(
|
||||
"aconfig_flags.{flag.device_config_namespace}",
|
||||
"{flag.device_config_flag}",
|
||||
"{flag.default_value}") == "true";
|
||||
{{ endif }}
|
||||
{{ -endif }}
|
||||
{{ -endfor }}
|
||||
}
|
||||
@@ -43,7 +129,67 @@ pub fn {flag.name}() -> bool \{
|
||||
PROVIDER.{flag.name}()
|
||||
{{ -else }}
|
||||
pub fn {flag.name}() -> bool \{
|
||||
{{ if not allow_instrumentation }}
|
||||
{flag.default_value}
|
||||
{{ else }}
|
||||
|
||||
let result = {flag.default_value};
|
||||
if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() \{
|
||||
return result;
|
||||
}
|
||||
|
||||
// This will be called multiple times. Subsequent calls after the first
|
||||
// are noops.
|
||||
logger::init(
|
||||
logger::Config::default()
|
||||
.with_tag_on_device(MIGRATION_LOG_TAG)
|
||||
.with_max_level(LevelFilter::Info),
|
||||
);
|
||||
|
||||
unsafe \{
|
||||
let package_map = match get_mapped_storage_file("{flag.container}", StorageFileType::PackageMap) \{
|
||||
Ok(file) => file,
|
||||
Err(err) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
let package_read_context = match get_package_read_context(&package_map, "{package}") \{
|
||||
Ok(Some(context)) => context,
|
||||
Ok(None) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': did not get context");
|
||||
return result;
|
||||
},
|
||||
Err(err) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let flag_val_map = match get_mapped_storage_file("{flag.container}", StorageFileType::FlagVal) \{
|
||||
Ok(val_map) => val_map,
|
||||
Err(err) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
let value = match get_boolean_flag_value(&flag_val_map, {flag.flag_offset} + package_read_context.boolean_start_index) \{
|
||||
Ok(val) => val,
|
||||
Err(err) => \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: failed to read flag '{flag.name}': \{err}");
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
if result != value \{
|
||||
log!(Level::Error, "AconfigTestMission1: error: flag mismatch for '{flag.name}'. Legacy storage was \{result}, new storage was \{value}");
|
||||
} else \{
|
||||
let default_value = {flag.default_value};
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
{{ endif }}
|
||||
{{ -endif }}
|
||||
}
|
||||
{{ endfor }}
|
||||
|
@@ -53,7 +53,7 @@ use flag_value_query::find_boolean_flag_value;
|
||||
use package_table_query::find_package_read_context;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use memmap2::Mmap;
|
||||
pub use memmap2::Mmap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
|
@@ -11,6 +11,7 @@ rust_defaults {
|
||||
rustlibs: [
|
||||
"libaconfig_device_paths",
|
||||
"libaconfig_protos",
|
||||
"libaconfigd_protos",
|
||||
"libaconfig_storage_read_api",
|
||||
"libaconfig_storage_file",
|
||||
"libanyhow",
|
||||
|
@@ -9,6 +9,7 @@ paste = "1.0.11"
|
||||
protobuf = "3.2.0"
|
||||
regex = "1.10.3"
|
||||
aconfig_protos = { path = "../aconfig_protos" }
|
||||
aconfigd_protos = { version = "0.1.0", path = "../../../../../system/server_configurable_flags/aconfigd"}
|
||||
nix = { version = "0.28.0", features = ["user"] }
|
||||
aconfig_storage_file = { version = "0.1.0", path = "../aconfig_storage_file" }
|
||||
aconfig_storage_read_api = { version = "0.1.0", path = "../aconfig_storage_read_api" }
|
||||
|
@@ -1,136 +1,125 @@
|
||||
use crate::{Flag, FlagPermission, FlagSource, FlagValue, ValuePickedFrom};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use crate::{Flag, FlagSource};
|
||||
use crate::{FlagPermission, FlagValue, ValuePickedFrom};
|
||||
use aconfigd_protos::{
|
||||
ProtoFlagQueryReturnMessage, ProtoListStorageMessage, ProtoListStorageMessageMsg,
|
||||
ProtoStorageRequestMessage, ProtoStorageRequestMessageMsg, ProtoStorageRequestMessages,
|
||||
ProtoStorageReturnMessage, ProtoStorageReturnMessageMsg, ProtoStorageReturnMessages,
|
||||
};
|
||||
use anyhow::anyhow;
|
||||
use anyhow::Result;
|
||||
use protobuf::Message;
|
||||
use protobuf::SpecialFields;
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Shutdown;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
||||
pub struct AconfigStorageSource {}
|
||||
|
||||
use aconfig_storage_file::protos::ProtoStorageFileInfo;
|
||||
use aconfig_storage_file::protos::ProtoStorageFiles;
|
||||
use aconfig_storage_file::FlagValueAndInfoSummary;
|
||||
|
||||
static STORAGE_INFO_FILE_PATH: &str = "/metadata/aconfig/storage_records.pb";
|
||||
|
||||
fn read_default_values(file_info: ProtoStorageFileInfo) -> Result<HashMap<String, FlagValue>> {
|
||||
let package_map =
|
||||
file_info.package_map.ok_or(anyhow!("storage file is missing package map"))?;
|
||||
let flag_map = file_info.flag_map.ok_or(anyhow!("storage file is missing flag map"))?;
|
||||
let flag_val = file_info.flag_val.ok_or(anyhow!("storage file is missing flag val"))?;
|
||||
|
||||
let mut result = HashMap::new();
|
||||
for listed_flag in aconfig_storage_file::list_flags(&package_map, &flag_map, &flag_val)? {
|
||||
let value = FlagValue::try_from(listed_flag.flag_value.as_str())?;
|
||||
result.insert(listed_flag.package_name + &listed_flag.flag_name, value);
|
||||
fn convert(msg: ProtoFlagQueryReturnMessage) -> Result<Flag> {
|
||||
let (value, value_picked_from) = match (
|
||||
&msg.boot_flag_value,
|
||||
msg.default_flag_value,
|
||||
msg.local_flag_value,
|
||||
msg.has_local_override,
|
||||
) {
|
||||
(_, _, Some(local), Some(has_local)) if has_local => {
|
||||
(FlagValue::try_from(local.as_str())?, ValuePickedFrom::Local)
|
||||
}
|
||||
Ok(result)
|
||||
(Some(boot), Some(default), _, _) => {
|
||||
let value = FlagValue::try_from(boot.as_str())?;
|
||||
if *boot == default {
|
||||
(value, ValuePickedFrom::Default)
|
||||
} else {
|
||||
(value, ValuePickedFrom::Server)
|
||||
}
|
||||
|
||||
fn read_next_boot_values(
|
||||
listed_flags: &[FlagValueAndInfoSummary],
|
||||
) -> Result<HashMap<String, FlagValue>> {
|
||||
let mut result = HashMap::new();
|
||||
for flag in listed_flags {
|
||||
result.insert(
|
||||
flag.package_name.clone() + &flag.flag_name,
|
||||
FlagValue::try_from(flag.flag_value.as_str())?,
|
||||
);
|
||||
}
|
||||
Ok(result)
|
||||
_ => return Err(anyhow!("missing override")),
|
||||
};
|
||||
|
||||
let staged_value = match (msg.boot_flag_value, msg.server_flag_value, msg.has_server_override) {
|
||||
(Some(boot), Some(server), _) if boot == server => None,
|
||||
(Some(boot), Some(server), Some(has_server)) if boot != server && has_server => {
|
||||
Some(FlagValue::try_from(server.as_str())?)
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
|
||||
fn reconcile(
|
||||
default_values: HashMap<String, FlagValue>,
|
||||
next_boot_values: HashMap<String, FlagValue>,
|
||||
flags_current_boot: &[FlagValueAndInfoSummary],
|
||||
container: &str,
|
||||
) -> Result<Vec<Flag>> {
|
||||
let mut result = Vec::new();
|
||||
for listed_flag in flags_current_boot {
|
||||
let default_value = default_values
|
||||
.get(&(listed_flag.package_name.clone() + &listed_flag.flag_name))
|
||||
.copied();
|
||||
|
||||
let name = listed_flag.flag_name.clone();
|
||||
let package = listed_flag.package_name.clone();
|
||||
let value = FlagValue::try_from(listed_flag.flag_value.as_str())?;
|
||||
let container = container.to_string();
|
||||
let staged_value = next_boot_values
|
||||
.get(&(listed_flag.package_name.clone() + &listed_flag.flag_name))
|
||||
.filter(|&v| value != *v)
|
||||
.copied();
|
||||
let permission = if listed_flag.is_readwrite {
|
||||
let permission = match msg.is_readwrite {
|
||||
Some(is_readwrite) => {
|
||||
if is_readwrite {
|
||||
FlagPermission::ReadWrite
|
||||
} else {
|
||||
FlagPermission::ReadOnly
|
||||
};
|
||||
let value_picked_from = if listed_flag.has_local_override {
|
||||
ValuePickedFrom::Local
|
||||
} else if Some(value) == default_value {
|
||||
ValuePickedFrom::Default
|
||||
} else {
|
||||
ValuePickedFrom::Server
|
||||
}
|
||||
}
|
||||
None => return Err(anyhow!("missing permission")),
|
||||
};
|
||||
|
||||
result.push(Flag {
|
||||
name,
|
||||
package,
|
||||
Ok(Flag {
|
||||
name: msg.flag_name.ok_or(anyhow!("missing flag name"))?,
|
||||
package: msg.package_name.ok_or(anyhow!("missing package name"))?,
|
||||
value,
|
||||
container,
|
||||
staged_value,
|
||||
permission,
|
||||
value_picked_from,
|
||||
staged_value,
|
||||
container: msg.container.ok_or(anyhow!("missing container"))?,
|
||||
|
||||
// TODO(b/324436145): delete namespace field once DeviceConfig isn't in CLI.
|
||||
// TODO: remove once DeviceConfig is not in the CLI.
|
||||
namespace: "-".to_string(),
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn read_from_socket() -> Result<Vec<ProtoFlagQueryReturnMessage>> {
|
||||
let messages = ProtoStorageRequestMessages {
|
||||
msgs: vec![ProtoStorageRequestMessage {
|
||||
msg: Some(ProtoStorageRequestMessageMsg::ListStorageMessage(ProtoListStorageMessage {
|
||||
msg: Some(ProtoListStorageMessageMsg::All(true)),
|
||||
special_fields: SpecialFields::new(),
|
||||
})),
|
||||
special_fields: SpecialFields::new(),
|
||||
}],
|
||||
special_fields: SpecialFields::new(),
|
||||
};
|
||||
|
||||
let mut socket = UnixStream::connect("/dev/socket/aconfigd")?;
|
||||
|
||||
let message_buffer = messages.write_to_bytes()?;
|
||||
let mut message_length_buffer: [u8; 4] = [0; 4];
|
||||
let message_size = &message_buffer.len();
|
||||
message_length_buffer[0] = (message_size >> 24) as u8;
|
||||
message_length_buffer[1] = (message_size >> 16) as u8;
|
||||
message_length_buffer[2] = (message_size >> 8) as u8;
|
||||
message_length_buffer[3] = *message_size as u8;
|
||||
socket.write_all(&message_length_buffer)?;
|
||||
socket.write_all(&message_buffer)?;
|
||||
socket.shutdown(Shutdown::Write)?;
|
||||
|
||||
let mut response_length_buffer: [u8; 4] = [0; 4];
|
||||
socket.read_exact(&mut response_length_buffer)?;
|
||||
let response_length = u32::from_be_bytes(response_length_buffer) as usize;
|
||||
let mut response_buffer = vec![0; response_length];
|
||||
socket.read_exact(&mut response_buffer)?;
|
||||
|
||||
let response: ProtoStorageReturnMessages =
|
||||
protobuf::Message::parse_from_bytes(&response_buffer)?;
|
||||
|
||||
match response.msgs.as_slice() {
|
||||
[ProtoStorageReturnMessage {
|
||||
msg: Some(ProtoStorageReturnMessageMsg::ListStorageMessage(list_storage_message)),
|
||||
..
|
||||
}] => Ok(list_storage_message.flags.clone()),
|
||||
_ => Err(anyhow!("unexpected response from aconfigd")),
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl FlagSource for AconfigStorageSource {
|
||||
fn list_flags() -> Result<Vec<Flag>> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
let mut file = File::open(STORAGE_INFO_FILE_PATH)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)?;
|
||||
let storage_file_info: ProtoStorageFiles = protobuf::Message::parse_from_bytes(&bytes)?;
|
||||
|
||||
for file_info in storage_file_info.files {
|
||||
let default_values = read_default_values(file_info.clone())?;
|
||||
|
||||
let container =
|
||||
file_info.container.ok_or(anyhow!("storage file is missing container"))?;
|
||||
let package_map = format!("/metadata/aconfig/maps/{container}.package.map");
|
||||
let flag_map = format!("/metadata/aconfig/maps/{container}.flag.map");
|
||||
let flag_info = format!("/metadata/aconfig/boot/{container}.info");
|
||||
|
||||
let flag_val_current_boot = format!("/metadata/aconfig/boot/{container}.val");
|
||||
let flag_val_next_boot = format!("/metadata/aconfig/flags/{container}.val");
|
||||
|
||||
let flags_next_boot = aconfig_storage_file::list_flags_with_info(
|
||||
&package_map,
|
||||
&flag_map,
|
||||
&flag_val_next_boot,
|
||||
&flag_info,
|
||||
)?;
|
||||
let flags_current_boot = aconfig_storage_file::list_flags_with_info(
|
||||
&package_map,
|
||||
&flag_map,
|
||||
&flag_val_current_boot,
|
||||
&flag_info,
|
||||
)?;
|
||||
|
||||
let next_boot_values = read_next_boot_values(&flags_next_boot)?;
|
||||
let processed_flags =
|
||||
reconcile(default_values, next_boot_values, &flags_current_boot, &container)?;
|
||||
|
||||
result.extend(processed_flags);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
read_from_socket()
|
||||
.map(|query_messages| {
|
||||
query_messages.iter().map(|message| convert(message.clone())).collect::<Vec<_>>()
|
||||
})?
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn override_flag(_namespace: &str, _qualified_name: &str, _value: &str) -> Result<()> {
|
||||
|
@@ -289,7 +289,7 @@ fn main() -> Result<()> {
|
||||
let output = match cli.command {
|
||||
Command::List { use_new_storage: true, container } => {
|
||||
list(FlagSourceType::AconfigStorage, container)
|
||||
.map_err(|_| anyhow!("storage may not be enabled"))
|
||||
.map_err(|err| anyhow!("storage may not be enabled: {err}"))
|
||||
.map(Some)
|
||||
}
|
||||
Command::List { use_new_storage: false, container } => {
|
||||
|
Reference in New Issue
Block a user