Change java codegen to read from new storage

This change adds code in java code to read from new storage. However the
generated code won't geneerate code contains code for new storage, since
the build system won't pass `allow-instrumentation` to java codegen.

Test: atest aconfig.test.java
Bug: 349874828
Change-Id: I70983fa97de6633f467d968109d134d46f895a89
This commit is contained in:
Zhi Dou
2024-07-15 17:23:47 +00:00
parent 6f9f7ccfe3
commit 9f8ed73934
4 changed files with 128 additions and 35 deletions

View File

@@ -20,22 +20,24 @@ use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf; use std::path::PathBuf;
use tinytemplate::TinyTemplate; use tinytemplate::TinyTemplate;
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
use crate::codegen; use crate::codegen;
use crate::codegen::CodegenMode; use crate::codegen::CodegenMode;
use crate::commands::OutputFile; use crate::commands::OutputFile;
use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
use std::collections::HashMap;
pub fn generate_java_code<I>( pub fn generate_java_code<I>(
package: &str, package: &str,
parsed_flags_iter: I, parsed_flags_iter: I,
codegen_mode: CodegenMode, codegen_mode: CodegenMode,
flag_ids: HashMap<String, u16>,
allow_instrumentation: bool,
) -> Result<Vec<OutputFile>> ) -> Result<Vec<OutputFile>>
where where
I: Iterator<Item = ProtoParsedFlag>, I: Iterator<Item = ProtoParsedFlag>,
{ {
let flag_elements: Vec<FlagElement> = let flag_elements: Vec<FlagElement> =
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 namespace_flags = gen_flags_by_namespace(&flag_elements);
let properties_set: BTreeSet<String> = let properties_set: BTreeSet<String> =
flag_elements.iter().map(|fe| format_property_name(&fe.device_config_namespace)).collect(); 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 library_exported = codegen_mode == CodegenMode::Exported;
let runtime_lookup_required = let runtime_lookup_required =
flag_elements.iter().any(|elem| elem.is_read_write) || library_exported; 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 { let context = Context {
flag_elements, flag_elements,
@@ -52,6 +55,8 @@ where
properties_set, properties_set,
package_name: package.to_string(), package_name: package.to_string(),
library_exported, library_exported,
allow_instrumentation,
container,
}; };
let mut template = TinyTemplate::new(); let mut template = TinyTemplate::new();
template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?; template.add_template("Flags.java", include_str!("../../templates/Flags.java.template"))?;
@@ -117,6 +122,8 @@ struct Context {
pub properties_set: BTreeSet<String>, pub properties_set: BTreeSet<String>,
pub package_name: String, pub package_name: String,
pub library_exported: bool, pub library_exported: bool,
pub allow_instrumentation: bool,
pub container: String,
} }
#[derive(Serialize, Debug)] #[derive(Serialize, Debug)]
@@ -127,23 +134,31 @@ struct NamespaceFlags {
#[derive(Serialize, Clone, Debug)] #[derive(Serialize, Clone, Debug)]
struct FlagElement { struct FlagElement {
pub container: String,
pub default_value: bool, pub default_value: bool,
pub device_config_namespace: String, pub device_config_namespace: String,
pub device_config_flag: String, pub device_config_flag: String,
pub flag_name_constant_suffix: String, pub flag_name_constant_suffix: String,
pub flag_offset: u16,
pub is_read_write: bool, pub is_read_write: bool,
pub method_name: String, pub method_name: String,
pub properties: String, pub properties: String,
} }
fn create_flag_element(package: &str, pf: &ProtoParsedFlag) -> FlagElement { fn create_flag_element(
package: &str,
pf: &ProtoParsedFlag,
flag_offsets: HashMap<String, u16>,
) -> FlagElement {
let device_config_flag = codegen::create_device_config_ident(package, pf.name()) let device_config_flag = codegen::create_device_config_ident(package, pf.name())
.expect("values checked at flag parse time"); .expect("values checked at flag parse time");
FlagElement { FlagElement {
container: pf.container().to_string(),
default_value: pf.state() == ProtoFlagState::ENABLED, default_value: pf.state() == ProtoFlagState::ENABLED,
device_config_namespace: pf.namespace().to_string(), device_config_namespace: pf.namespace().to_string(),
device_config_flag, device_config_flag,
flag_name_constant_suffix: pf.name().to_ascii_uppercase(), 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, is_read_write: pf.permission() == ProtoFlagPermission::READ_WRITE,
method_name: format_java_method_name(pf.name()), method_name: format_java_method_name(pf.name()),
properties: format_property_name(pf.namespace()), properties: format_property_name(pf.namespace()),
@@ -179,6 +194,7 @@ fn format_property_name(property_name: &str) -> String {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::commands::assign_flag_ids;
use std::collections::HashMap; use std::collections::HashMap;
const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#" const EXPECTED_FEATUREFLAGS_COMMON_CONTENT: &str = r#"
@@ -477,9 +493,16 @@ mod tests {
let mode = CodegenMode::Production; let mode = CodegenMode::Production;
let modified_parsed_flags = let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
let generated_files = let flag_ids =
generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
.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() let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+ r#" + r#"
private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
@@ -647,9 +670,16 @@ mod tests {
let mode = CodegenMode::Exported; let mode = CodegenMode::Exported;
let modified_parsed_flags = let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
let generated_files = let flag_ids =
generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
.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#" let expect_flags_content = r#"
package com.android.aconfig.test; package com.android.aconfig.test;
@@ -833,9 +863,16 @@ mod tests {
let mode = CodegenMode::Test; let mode = CodegenMode::Test;
let modified_parsed_flags = let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
let generated_files = let flag_ids =
generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
.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() let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+ r#" + r#"
@@ -850,69 +887,58 @@ mod tests {
"#; "#;
let expect_featureflagsimpl_content = r#" let expect_featureflagsimpl_content = r#"
package com.android.aconfig.test; package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage;
/** @hide */ /** @hide */
public final class FeatureFlagsImpl implements FeatureFlags { public final class FeatureFlagsImpl implements FeatureFlags {
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean disabledRo() { public boolean disabledRo() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean disabledRw() { public boolean disabledRw() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean disabledRwExported() { public boolean disabledRwExported() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean disabledRwInOtherNamespace() { public boolean disabledRwInOtherNamespace() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean enabledFixedRo() { public boolean enabledFixedRo() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean enabledFixedRoExported() { public boolean enabledFixedRoExported() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean enabledRo() { public boolean enabledRo() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean enabledRoExported() { public boolean enabledRoExported() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
} }
@Override @Override
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
public boolean enabledRw() { public boolean enabledRw() {
throw new UnsupportedOperationException( throw new UnsupportedOperationException(
"Method is not implemented."); "Method is not implemented.");
@@ -958,9 +984,16 @@ mod tests {
let mode = CodegenMode::ForceReadOnly; let mode = CodegenMode::ForceReadOnly;
let modified_parsed_flags = let modified_parsed_flags =
crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap(); crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
let generated_files = let flag_ids =
generate_java_code(crate::test::TEST_PACKAGE, modified_parsed_flags.into_iter(), mode) assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
.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#" let expect_featureflags_content = r#"
package com.android.aconfig.test; package com.android.aconfig.test;
// TODO(b/303773055): Remove the annotation after access issue is resolved. // TODO(b/303773055): Remove the annotation after access issue is resolved.

View File

@@ -191,15 +191,25 @@ pub fn parse_flags(
Ok(output) Ok(output)
} }
pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> { pub fn create_java_lib(
mut input: Input,
codegen_mode: CodegenMode,
allow_instrumentation: bool,
) -> Result<Vec<OutputFile>> {
let parsed_flags = input.try_parse_flags()?; let parsed_flags = input.try_parse_flags()?;
let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?; let modified_parsed_flags = modify_parsed_flags_based_on_mode(parsed_flags, codegen_mode)?;
let Some(package) = find_unique_package(&modified_parsed_flags) else { let Some(package) = find_unique_package(&modified_parsed_flags) else {
bail!("no parsed flags, or the parsed flags use different packages"); bail!("no parsed flags, or the parsed flags use different packages");
}; };
let package = package.to_string(); let package = package.to_string();
let _flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?; let flag_ids = assign_flag_ids(&package, modified_parsed_flags.iter())?;
generate_java_code(&package, modified_parsed_flags.into_iter(), codegen_mode) generate_java_code(
&package,
modified_parsed_flags.into_iter(),
codegen_mode,
flag_ids,
allow_instrumentation,
)
} }
pub fn create_cpp_lib( pub fn create_cpp_lib(

View File

@@ -72,6 +72,12 @@ fn cli() -> Command {
.long("mode") .long("mode")
.value_parser(EnumValueParser::<CodegenMode>::new()) .value_parser(EnumValueParser::<CodegenMode>::new())
.default_value("production"), .default_value("production"),
)
.arg(
Arg::new("allow-instrumentation")
.long("allow-instrumentation")
.value_parser(clap::value_parser!(bool))
.default_value("false"),
), ),
) )
.subcommand( .subcommand(
@@ -237,8 +243,10 @@ fn main() -> Result<()> {
Some(("create-java-lib", sub_matches)) => { Some(("create-java-lib", sub_matches)) => {
let cache = open_single_file(sub_matches, "cache")?; let cache = open_single_file(sub_matches, "cache")?;
let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?; let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
let generated_files = let allow_instrumentation =
commands::create_java_lib(cache, *mode).context("failed to create java lib")?; get_required_arg::<bool>(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::<String>(sub_matches, "out")?); let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
generated_files generated_files
.iter() .iter()

View File

@@ -1,13 +1,23 @@
package {package_name}; package {package_name};
{{ -if not is_test_mode }}
{{ if not library_exported- }} {{ if not library_exported- }}
// TODO(b/303773055): Remove the annotation after access issue is resolved. // TODO(b/303773055): Remove the annotation after access issue is resolved.
import android.compat.annotation.UnsupportedAppUsage; import android.compat.annotation.UnsupportedAppUsage;
{{ -endif }} {{ -endif }}
{{ -if not is_test_mode }}
{{ -if runtime_lookup_required }} {{ -if runtime_lookup_required }}
import android.provider.DeviceConfig; import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties; 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 */ /** @hide */
public final class FeatureFlagsImpl implements FeatureFlags \{ public final class FeatureFlagsImpl implements FeatureFlags \{
{{ -if runtime_lookup_required }} {{ -if runtime_lookup_required }}
@@ -20,14 +30,47 @@ public final class FeatureFlagsImpl implements FeatureFlags \{
private static boolean {flag.method_name} = {flag.default_value}; private static boolean {flag.method_name} = {flag.default_value};
{{ -endif }} {{ -endif }}
{{ -endfor }} {{ -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 }} {{ for namespace_with_flags in namespace_flags }}
private void load_overrides_{namespace_with_flags.namespace}() \{ private void load_overrides_{namespace_with_flags.namespace}() \{
try \{ try \{
{{ -if allow_instrumentation }}
boolean val;
{{ -endif }}
Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}"); Properties properties = DeviceConfig.getProperties("{namespace_with_flags.namespace}");
{{ -for flag in namespace_with_flags.flags }} {{ -for flag in namespace_with_flags.flags }}
{{ -if flag.is_read_write }} {{ -if flag.is_read_write }}
{flag.method_name} = {flag.method_name} =
properties.getBoolean(Flags.FLAG_{flag.flag_name_constant_suffix}, {flag.default_value}); 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 }} {{ -endif }}
{{ -endfor }} {{ -endfor }}
} catch (NullPointerException e) \{ } catch (NullPointerException e) \{
@@ -70,7 +113,6 @@ public final class FeatureFlagsImpl implements FeatureFlags \{
@Override @Override
{{ -if not library_exported }} {{ -if not library_exported }}
@com.android.aconfig.annotations.AconfigFlagAccessor @com.android.aconfig.annotations.AconfigFlagAccessor
@UnsupportedAppUsage
{{ -endif }} {{ -endif }}
public boolean {flag.method_name}() \{ public boolean {flag.method_name}() \{
throw new UnsupportedOperationException( throw new UnsupportedOperationException(