Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8350c6dd authored by Ted Bauer's avatar Ted Bauer
Browse files

aconfig: read from new storage in Rust codegen

Read storage values for read-only flags, and log failures or mismatches
with compiled-in defaults. Continue to return values fetched from the legacy storage.

Test: cargo t
Bug: 328444881
Ignore-AOSP-First: internal storage migration, this code will be deleted
Change-Id: I4651617270129794dec64e324b4aa7836cce7a43
parent 9881ae26
Loading
Loading
Loading
Loading
+380 −23
Original line number Diff line number Diff line
@@ -20,26 +20,32 @@ 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 context = TemplateContext {
        package: package.to_string(),
        template_flags,
        modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
        has_readwrite,
        allow_instrumentation,
    };
    let mut template = TinyTemplate::new();
    template.add_template(
@@ -62,6 +68,7 @@ struct TemplateContext {
    pub template_flags: Vec<TemplateParsedFlag>,
    pub modules: Vec<String>,
    pub has_readwrite: bool,
    pub allow_instrumentation: bool,
}

#[derive(Serialize)]
@@ -69,25 +76,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 +107,14 @@ mod tests {

    const PROD_EXPECTED: &str = r#"
//! codegenerated rust flag lib
use aconfig_storage_read_api::{StorageFileType, get_mapped_storage_file, get_boolean_flag_value, get_package_offset};
use std::path::Path;
use std::io::Write;
use log::{info, error, LevelFilter};

static STORAGE_MIGRATION_MARKER_FILE: &str =
    "/metadata/aconfig/storage_test_mission_1";
static MIGRATION_LOG_TAG: &str = "AconfigStorageTestMission1";

/// flag provider
pub struct FlagProvider;
@@ -492,6 +510,14 @@ pub fn reset_flags() {

    const EXPORTED_EXPECTED: &str = r#"
//! codegenerated rust flag lib
use aconfig_storage_read_api::{StorageFileType, get_mapped_storage_file, get_boolean_flag_value, get_package_offset};
use std::path::Path;
use std::io::Write;
use log::{info, error, LevelFilter};

static STORAGE_MIGRATION_MARKER_FILE: &str =
    "/metadata/aconfig/storage_test_mission_1";
static MIGRATION_LOG_TAG: &str = "AconfigStorageTestMission1";

/// flag provider
pub struct FlagProvider;
@@ -520,17 +546,20 @@ lazy_static::lazy_static! {
impl FlagProvider {
    /// query flag disabled_rw_exported
    pub fn disabled_rw_exported(&self) -> bool {
        *CACHED_disabled_rw_exported
        let result = *CACHED_disabled_rw_exported;
        result
    }

    /// query flag enabled_fixed_ro_exported
    pub fn enabled_fixed_ro_exported(&self) -> bool {
        *CACHED_enabled_fixed_ro_exported
        let result = *CACHED_enabled_fixed_ro_exported;
        result
    }

    /// query flag enabled_ro_exported
    pub fn enabled_ro_exported(&self) -> bool {
        *CACHED_enabled_ro_exported
        let result = *CACHED_enabled_ro_exported;
        result
    }
}

@@ -558,6 +587,14 @@ pub fn enabled_ro_exported() -> bool {

    const FORCE_READ_ONLY_EXPECTED: &str = r#"
//! codegenerated rust flag lib
use aconfig_storage_read_api::{StorageFileType, get_mapped_storage_file, get_boolean_flag_value, get_package_offset};
use std::path::Path;
use std::io::Write;
use log::{info, error, LevelFilter};

static STORAGE_MIGRATION_MARKER_FILE: &str =
    "/metadata/aconfig/storage_test_mission_1";
static MIGRATION_LOG_TAG: &str = "AconfigStorageTestMission1";

/// flag provider
pub struct FlagProvider;
@@ -565,32 +602,344 @@ pub struct FlagProvider;
impl FlagProvider {
    /// query flag disabled_ro
    pub fn disabled_ro(&self) -> bool {
        false
        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) => {
                    error!("failed to read flag 'disabled_ro': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'disabled_ro', not found in package map");
                    return result;
                },
                Err(err) => {
                    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) => {
                    error!("failed to read flag 'disabled_ro': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 0 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'disabled_ro': {}", err);
                    return result;
                }
            };
            if false != value {
                let default_value = false;
                error!("flag mismatch for 'disabled_ro'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }

    /// query flag disabled_rw
    pub fn disabled_rw(&self) -> bool {
        false
        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) => {
                    error!("failed to read flag 'disabled_rw': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'disabled_rw', not found in package map");
                    return result;
                },
                Err(err) => {
                    error!("failed to read flag 'disabled_rw': {}", err);
                    return result;
                }
            };
            let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
                Ok(val_map) => val_map,
                Err(err) => {
                    error!("failed to read flag 'disabled_rw': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 1 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'disabled_rw': {}", err);
                    return result;
                }
            };
            if false != value {
                let default_value = false;
                error!("flag mismatch for 'disabled_rw'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }

    /// query flag disabled_rw_in_other_namespace
    pub fn disabled_rw_in_other_namespace(&self) -> bool {
        false
        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) => {
                    error!("failed to read flag 'disabled_rw_in_other_namespace': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'disabled_rw_in_other_namespace', not found in package map");
                    return result;
                },
                Err(err) => {
                    error!("failed to read flag 'disabled_rw_in_other_namespace': {}", err);
                    return result;
                }
            };
            let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
                Ok(val_map) => val_map,
                Err(err) => {
                    error!("failed to read flag 'disabled_rw_in_other_namespace': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 2 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'disabled_rw_in_other_namespace': {}", err);
                    return result;
                }
            };
            if false != value {
                let default_value = false;
                error!("flag mismatch for 'disabled_rw_in_other_namespace'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }

    /// query flag enabled_fixed_ro
    pub fn enabled_fixed_ro(&self) -> bool {
        true
        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) => {
                    error!("failed to read flag 'enabled_fixed_ro': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'enabled_fixed_ro', not found in package map");
                    return result;
                },
                Err(err) => {
                    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) => {
                    error!("failed to read flag 'enabled_fixed_ro': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 3 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'enabled_fixed_ro': {}", err);
                    return result;
                }
            };
            if true != value {
                let default_value = true;
                error!("flag mismatch for 'enabled_fixed_ro'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }

    /// query flag enabled_ro
    pub fn enabled_ro(&self) -> bool {
        true
        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) => {
                    error!("failed to read flag 'enabled_ro': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'enabled_ro', not found in package map");
                    return result;
                },
                Err(err) => {
                    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) => {
                    error!("failed to read flag 'enabled_ro': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 4 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'enabled_ro': {}", err);
                    return result;
                }
            };
            if true != value {
                let default_value = true;
                error!("flag mismatch for 'enabled_ro'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }

    /// query flag enabled_rw
    pub fn enabled_rw(&self) -> bool {
        true
        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) => {
                    error!("failed to read flag 'enabled_rw': {}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "com.android.aconfig.test") {
                Ok(Some(offset)) => offset,
                Ok(None) => {
                    error!("failed to read flag 'enabled_rw', not found in package map");
                    return result;
                },
                Err(err) => {
                    error!("failed to read flag 'enabled_rw': {}", err);
                    return result;
                }
            };
            let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
                Ok(val_map) => val_map,
                Err(err) => {
                    error!("failed to read flag 'enabled_rw': {}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, 5 + package_offset.boolean_offset) {
                Ok(val) => val,
                Err(err) => {
                    error!("failed to read flag 'enabled_rw': {}", err);
                    return result;
                }
            };
            if true != value {
                let default_value = true;
                error!("flag mismatch for 'enabled_rw'. Legacy storage was {default_value}, new storage was {value}")
            }
        }
        result
    }
}

@@ -633,13 +982,21 @@ 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, instrumentation: bool) {
        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,
            instrumentation,
        )
        .unwrap();
        assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
        assert_eq!(
@@ -658,21 +1015,21 @@ 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);
    }

    #[test]
    fn test_generate_rust_code_for_test() {
        test_generate_rust_code(CodegenMode::Test);
        test_generate_rust_code(CodegenMode::Test, true);
    }

    #[test]
    fn test_generate_rust_code_for_exported() {
        test_generate_rust_code(CodegenMode::Exported);
        test_generate_rust_code(CodegenMode::Exported, true);
    }

    #[test]
    fn test_generate_rust_code_for_force_read_only() {
        test_generate_rust_code(CodegenMode::ForceReadOnly);
        test_generate_rust_code(CodegenMode::ForceReadOnly, true);
    }
}
+13 −3
Original line number Diff line number Diff line
@@ -218,7 +218,11 @@ pub fn create_cpp_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec
    generate_cpp_code(&package, modified_parsed_flags.into_iter(), codegen_mode)
}

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,
@@ -230,8 +234,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(
+10 −2
Original line number Diff line number Diff line
@@ -89,6 +89,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")
@@ -251,8 +257,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)?;
        }
+77 −3
Original line number Diff line number Diff line
//! codegenerated rust flag lib

use aconfig_storage_read_api::\{StorageFileType, get_mapped_storage_file, get_boolean_flag_value, get_package_offset};
use std::path::Path;
use std::io::Write;
use log::\{info, error, LevelFilter};

static STORAGE_MIGRATION_MARKER_FILE: &str =
    "/metadata/aconfig/storage_test_mission_1";
static MIGRATION_LOG_TAG: &str = "AconfigStorageTestMission1";

/// flag provider
pub struct FlagProvider;

@@ -22,11 +31,76 @@ impl FlagProvider \{
{{ for flag in template_flags }}
    /// query flag {flag.name}
    pub fn {flag.name}(&self) -> bool \{
        {{ if not allow_instrumentation }}

        {{ -if flag.readwrite }}
        *CACHED_{flag.name}
        {{ -else }}
        {flag.default_value}
        {{ -endif }}

        {{ else }}
        let result = {{ -if flag.readwrite }} *CACHED_{flag.name}{{ else }} {flag.default_value} {{ -endif }};

        {{ if flag.readwrite }}
        result
        {{ else }}

        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) => \{
                    error!("failed to read flag '{flag.name}': \{}", err);
                    return result;
                }
            };
            let package_offset = match get_package_offset(&package_map, "{package}") \{
                Ok(Some(offset)) => offset,
                Ok(None) => \{
                    error!("failed to read flag '{flag.name}', not found in package map");
                    return result;
                },
                Err(err) => \{
                    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) => \{
                    error!("failed to read flag '{flag.name}': \{}", err);
                    return result;
                }
            };
            let value = match get_boolean_flag_value(&flag_val_map, {flag.flag_offset} + package_offset.boolean_offset) \{
                Ok(val) => val,
                Err(err) => \{
                    error!("failed to read flag '{flag.name}': \{}", err);
                    return result;
                }
            };

            if {flag.default_value} != value \{
                let default_value = {flag.default_value};
                error!("flag mismatch for '{flag.name}'. Legacy storage was \{default_value}, new storage was \{value}")
            }
        }

        result
        {{ endif }}
        {{ endif }}
    }
{{ endfor }}