diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs index d3f074a555..ec22ebc6c4 100644 --- a/tools/aconfig/aconfig/src/codegen/java.rs +++ b/tools/aconfig/aconfig/src/codegen/java.rs @@ -20,22 +20,24 @@ use std::collections::{BTreeMap, BTreeSet}; use std::path::PathBuf; use tinytemplate::TinyTemplate; -use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag}; - use crate::codegen; use crate::codegen::CodegenMode; use crate::commands::OutputFile; +use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag}; +use std::collections::HashMap; pub fn generate_java_code( package: &str, parsed_flags_iter: I, codegen_mode: CodegenMode, + flag_ids: HashMap, + allow_instrumentation: bool, ) -> Result> where I: Iterator, { let flag_elements: Vec = - parsed_flags_iter.map(|pf| create_flag_element(package, &pf)).collect(); + parsed_flags_iter.map(|pf| create_flag_element(package, &pf, flag_ids.clone())).collect(); let namespace_flags = gen_flags_by_namespace(&flag_elements); let properties_set: BTreeSet = flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect(); @@ -43,6 +45,7 @@ where let library_exported = codegen_mode == CodegenMode::Exported; let runtime_lookup_required = flag_elements.iter().any(|elem| elem.is_read_write) || library_exported; + let container = (flag_elements.first().expect("zero template flags").container).to_string(); let context = Context { flag_elements, @@ -52,6 +55,8 @@ where properties_set, package_name: package.to_string(), library_exported, + allow_instrumentation, + container, }; let mut template = TinyTemplate::new(); template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?; @@ -117,6 +122,8 @@ struct Context { pub properties_set: BTreeSet, pub package_name: String, pub library_exported: bool, + pub allow_instrumentation: bool, + pub container: String, } #[derive(Serialize, Debug)] @@ -127,23 +134,31 @@ struct NamespaceFlags { #[derive(Serialize, Clone, Debug)] struct FlagElement { + pub container: String, pub default_value: bool, pub device_config_namespace: String, pub device_config_flag: String, pub flag_name_constant_suffix: String, + pub flag_offset: u16, pub is_read_write: bool, pub method_name: String, pub properties: String, } -fn create_flag_element(package: &str, pf: &ProtoParsedFlag) -> FlagElement { +fn create_flag_element( + package: &str, + pf: &ProtoParsedFlag, + flag_offsets: HashMap, +) -> FlagElement { let device_config_flag = codegen::create_device_config_ident(package, pf.name()) .expect("values checked at flag parse time"); FlagElement { + container: pf.container().to_string(), default_value: pf.state() == ProtoFlagState::ENABLED, device_config_namespace: pf.namespace().to_string(), device_config_flag, flag_name_constant_suffix: pf.name().to_ascii_uppercase(), + flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("), is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE, method_name: format_java_method_name(pf.name()), properties: format_property_name(pf.namespace()), @@ -179,6 +194,7 @@ fn format_property_name(property_name: &str) -> String { #[cfg(test)] mod tests { use super::*; + use crate::commands::assign_flag_ids; use std::collections::HashMap; const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#" @@ -477,9 +493,16 @@ mod tests { let mode = CodegenMode::Production; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); - let generated_files = - generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) - .unwrap(); + let flag_ids = + assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); + let generated_files = generate_java_code( + crate::test::TEST_PACKAGE, + modified_parsed_flags.into_iter(), + mode, + flag_ids, + false, + ) + .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); @@ -647,9 +670,16 @@ mod tests { let mode = CodegenMode::Exported; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); - let generated_files = - generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) - .unwrap(); + let flag_ids = + assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); + let generated_files = generate_java_code( + crate::test::TEST_PACKAGE, + modified_parsed_flags.into_iter(), + mode, + flag_ids, + false, + ) + .unwrap(); let expect_flags_content = r#" package com.android.aconfig.test; @@ -833,9 +863,16 @@ mod tests { let mode = CodegenMode::Test; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); - let generated_files = - generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) - .unwrap(); + let flag_ids = + assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); + let generated_files = generate_java_code( + crate::test::TEST_PACKAGE, + modified_parsed_flags.into_iter(), + mode, + flag_ids, + false, + ) + .unwrap(); let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string() + r#" @@ -850,69 +887,58 @@ mod tests { "#; let expect_featureflagsimpl_content = r#" package com.android.aconfig.test; - // TODO(b/303773055): Remove the annotation after access issue is resolved. - import android.compat.annotation.UnsupportedAppUsage; /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags { @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean disabledRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean disabledRw() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean disabledRwExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean enabledFixedRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean enabledFixedRoExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean enabledRo() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean enabledRoExported() { throw new UnsupportedOperationException( "Method is not implemented."); } @Override @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage public boolean enabledRw() { throw new UnsupportedOperationException( "Method is not implemented."); @@ -958,9 +984,16 @@ mod tests { let mode = CodegenMode::ForceReadOnly; let modified_parsed_flags = crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); - let generated_files = - generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) - .unwrap(); + let flag_ids = + assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap(); + let generated_files = generate_java_code( + crate::test::TEST_PACKAGE, + modified_parsed_flags.into_iter(), + mode, + flag_ids, + false, + ) + .unwrap(); let expect_featureflags_content = r#" package com.android.aconfig.test; // TODO(b/303773055): Remove the annotation after access issue is resolved. diff --git a/tools/aconfig/aconfig/src/commands.rs b/tools/aconfig/aconfig/src/commands.rs index 6945fd4649..6d1c2f5cfc 100644 --- a/tools/aconfig/aconfig/src/commands.rs +++ b/tools/aconfig/aconfig/src/commands.rs @@ -191,15 +191,25 @@ pub fn parse_flags( Ok(output) } -pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result> { +pub fn create_java_lib( + mut input: Input, + codegen_mode: CodegenMode, + allow_instrumentation: bool, +) -> Result> { let parsed_flags = input.try_parse_flags()?; let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?; let Some(package) = find_unique_package(&modified_parsed_flags) else { 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_java_code(&package, modified_parsed_flags.into_iter(), codegen_mode) + let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; + generate_java_code( + &package, + modified_parsed_flags.into_iter(), + codegen_mode, + flag_ids, + allow_instrumentation, + ) } pub fn create_cpp_lib( diff --git a/tools/aconfig/aconfig/src/main.rs b/tools/aconfig/aconfig/src/main.rs index 72be1c9896..7ec272fbaa 100644 --- a/tools/aconfig/aconfig/src/main.rs +++ b/tools/aconfig/aconfig/src/main.rs @@ -72,6 +72,12 @@ fn cli() -> Command { .long("mode") .value_parser(EnumValueParser::::new()) .default_value("production"), + ) + .arg( + Arg::new("allow-instrumentation") + .long("allow-instrumentation") + .value_parser(clap::value_parser!(bool)) + .default_value("false"), ), ) .subcommand( @@ -237,8 +243,10 @@ fn main() -> Result<()> { Some(("create-java-lib", sub_matches)) => { let cache = open_single_file(sub_matches, "cache")?; let mode = get_required_arg::(sub_matches, "mode")?; - let generated_files = - commands::create_java_lib(cache, *mode).context("failed to create java lib")?; + let allow_instrumentation = + get_required_arg::(sub_matches, "allow-instrumentation")?; + let generated_files = commands::create_java_lib(cache, *mode, *allow_instrumentation) + .context("failed to create java lib")?; let dir = PathBuf::from(get_required_arg::(sub_matches, "out")?); generated_files .iter() diff --git a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template index 63c4f2de72..cd2e3db2d0 100644 --- a/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template +++ b/tools/aconfig/aconfig/templates/FeatureFlagsImpl.java.template @@ -1,13 +1,23 @@ package {package_name}; +{{ -if not is_test_mode }} {{ if not library_exported- }} // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; {{ -endif }} -{{ -if not is_test_mode }} + {{ -if runtime_lookup_required }} import android.provider.DeviceConfig; import android.provider.DeviceConfig.Properties; -{{ endif }} + + +{{ -if allow_instrumentation }} +import android.aconfig.storage.StorageInternalReader; +import android.util.Log; + +import java.io.File; +{{ -endif }} + +{{ -endif }} /** @hide */ public final class FeatureFlagsImpl implements FeatureFlags \{ {{ -if runtime_lookup_required }} @@ -20,14 +30,47 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ private static boolean {flag.method_name} = {flag.default_value}; {{ -endif }} {{ -endfor }} +{{ -if allow_instrumentation }} + StorageInternalReader reader; + boolean readFromNewStorage; + + private final static String TAG = "AconfigJavaCodegen"; + + public FeatureFlagsImpl() \{ + File file = new File("/metadata/aconfig_test_missions/mission_1"); + if (file.exists()) \{ + readFromNewStorage = true; + reader = new StorageInternalReader("{container}", "{package_name}"); + } + } +{{ -endif }} {{ for namespace_with_flags in namespace_flags }} private void load_overrides_{namespace_with_flags.namespace}() \{ try \{ +{{ -if allow_instrumentation }} + boolean val; +{{ -endif }} Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}"); {{ -for flag in namespace_with_flags.flags }} {{ -if flag.is_read_write }} {flag.method_name} = properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value}); +{{ -if allow_instrumentation }} + if (readFromNewStorage) \{ + try \{ + val = reader.getBooleanFlagValue({flag.flag_offset}); + if (val == {flag.method_name}) \{ + Log.i(TAG, "success: {flag.method_name} value matches"); + } else \{ + Log.i(TAG, String.format( + "error: {flag.method_name} value mismatch, new storage value is %s, old storage value is %s", + val, {flag.method_name})); + } + } catch (Exception e) \{ + Log.e(TAG,"error: failed to read flag value of {flag.method_name}"); + } + } +{{ -endif }} {{ -endif }} {{ -endfor }} } catch (NullPointerException e) \{ @@ -70,7 +113,6 @@ public final class FeatureFlagsImpl implements FeatureFlags \{ @Override {{ -if not library_exported }} @com.android.aconfig.annotations.AconfigFlagAccessor - @UnsupportedAppUsage {{ -endif }} public boolean {flag.method_name}() \{ throw new UnsupportedOperationException(