diff --git a/tools/aconfig/aconfig/src/codegen/java.rs b/tools/aconfig/aconfig/src/codegen/java.rs index 18a4be5094..9abc892908 100644 --- a/tools/aconfig/aconfig/src/codegen/java.rs +++ b/tools/aconfig/aconfig/src/codegen/java.rs @@ -63,21 +63,28 @@ where "FeatureFlags.java", include_str!("../../templates/FeatureFlags.java.template"), )?; + template.add_template( + "CustomFeatureFlags.java", + include_str!("../../templates/CustomFeatureFlags.java.template"), + )?; template.add_template( "FakeFeatureFlagsImpl.java", include_str!("../../templates/FakeFeatureFlagsImpl.java.template"), )?; let path: PathBuf = package.split('.').collect(); - ["Flags.java", "FeatureFlags.java", "FeatureFlagsImpl.java", "FakeFeatureFlagsImpl.java"] - .iter() - .map(|file| { - Ok(OutputFile { - contents: template.render(file, &context)?.into(), - path: path.join(file), - }) - }) - .collect::>>() + [ + "Flags.java", + "FeatureFlags.java", + "FeatureFlagsImpl.java", + "CustomFeatureFlags.java", + "FakeFeatureFlagsImpl.java", + ] + .iter() + .map(|file| { + Ok(OutputFile { contents: template.render(file, &context)?.into(), path: path.join(file) }) + }) + .collect::>>() } fn gen_flags_by_namespace(flags: &[FlagElement]) -> Vec { @@ -292,76 +299,82 @@ mod tests { } "#; - const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#" + const EXPECTED_CUSTOMFEATUREFLAGS_CONTENT: &str = r#" package com.android.aconfig.test; + // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override @UnsupportedAppUsage public boolean disabledRo() { - return getValue(Flags.FLAG_DISABLED_RO); + return getValue(Flags.FLAG_DISABLED_RO, + FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { - return getValue(Flags.FLAG_DISABLED_RW); + return getValue(Flags.FLAG_DISABLED_RW, + FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwExported() { - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED); + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, + FeatureFlags::disabledRwExported); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE); + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO); + return getValue(Flags.FLAG_ENABLED_FIXED_RO, + FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledFixedRoExported() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + FeatureFlags::enabledFixedRoExported); } @Override @UnsupportedAppUsage public boolean enabledRo() { - return getValue(Flags.FLAG_ENABLED_RO); + return getValue(Flags.FLAG_ENABLED_RO, + FeatureFlags::enabledRo); } @Override @UnsupportedAppUsage public boolean enabledRoExported() { - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, + FeatureFlags::enabledRoExported); } @Override @UnsupportedAppUsage public boolean enabledRw() { - return getValue(Flags.FLAG_ENABLED_RW); - } - public void setFlag(String flagName, boolean value) { - if (!this.mFlagMap.containsKey(flagName)) { - throw new IllegalArgumentException("no such flag " + flagName); - } - this.mFlagMap.put(flagName, value); - } - public void resetAll() { - for (Map.Entry entry : mFlagMap.entrySet()) { - entry.setValue(null); - } + return getValue(Flags.FLAG_ENABLED_RW, + FeatureFlags::enabledRw); } + public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { @@ -369,30 +382,30 @@ mod tests { } return false; } + @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } - private boolean getValue(String flagName) { - Boolean value = this.mFlagMap.get(flagName); - if (value == null) { - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; + + protected boolean getValue(String flagName, Predicate getter) { + return mGetValueImpl.test(flagName, getter); } - private Map mFlagMap = new HashMap<>( - Map.ofEntries( - Map.entry(Flags.FLAG_DISABLED_RO, false), - Map.entry(Flags.FLAG_DISABLED_RW, false), - Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false), - Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RO, false), - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RW, false) - ) - ); + + public List getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RO, + Flags.FLAG_DISABLED_RW, + Flags.FLAG_DISABLED_RW_EXPORTED, + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + Flags.FLAG_ENABLED_FIXED_RO, + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + Flags.FLAG_ENABLED_RO, + Flags.FLAG_ENABLED_RO_EXPORTED, + Flags.FLAG_ENABLED_RW + ); + } + private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, @@ -406,6 +419,49 @@ mod tests { } "#; + const EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT: &str = r#" + package com.android.aconfig.test; + + import java.util.HashMap; + import java.util.Map; + import java.util.function.Predicate; + + /** @hide */ + public class FakeFeatureFlagsImpl extends CustomFeatureFlags { + private Map mFlagMap = new HashMap<>(); + + public FakeFeatureFlagsImpl() { + super(null); + // Initialize the map with null values + for (String flagName : getFlagNames()) { + mFlagMap.put(flagName, null); + } + } + + @Override + protected boolean getValue(String flagName, Predicate getter) { + Boolean value = this.mFlagMap.get(flagName); + if (value == null) { + throw new IllegalArgumentException(flagName + " is not set"); + } + return value; + } + + public void setFlag(String flagName, boolean value) { + if (!this.mFlagMap.containsKey(flagName)) { + throw new IllegalArgumentException("no such flag " + flagName); + } + this.mFlagMap.put(flagName, value); + } + + public void resetAll() { + for (Map.Entry entry : mFlagMap.entrySet()) { + entry.setValue(null); + } + } + } + "#; + #[test] fn test_generate_java_code_production() { let parsed_flags = crate::test::parse_test_flags(); @@ -548,6 +604,10 @@ mod tests { ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), + ( + "com/android/aconfig/test/CustomFeatureFlags.java", + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, + ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, @@ -671,55 +731,53 @@ mod tests { } }"#; - let expect_fake_feature_flags_impl_content = r#" + let expect_custom_feature_flags_content = r#" package com.android.aconfig.test; + import java.util.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override public boolean disabledRwExported() { - return getValue(Flags.FLAG_DISABLED_RW_EXPORTED); + return getValue(Flags.FLAG_DISABLED_RW_EXPORTED, + FeatureFlags::disabledRwExported); } @Override public boolean enabledFixedRoExported() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + FeatureFlags::enabledFixedRoExported); } @Override public boolean enabledRoExported() { - return getValue(Flags.FLAG_ENABLED_RO_EXPORTED); + return getValue(Flags.FLAG_ENABLED_RO_EXPORTED, + FeatureFlags::enabledRoExported); } - public void setFlag(String flagName, boolean value) { - if (!this.mFlagMap.containsKey(flagName)) { - throw new IllegalArgumentException("no such flag " + flagName); - } - this.mFlagMap.put(flagName, value); + + protected boolean getValue(String flagName, Predicate getter) { + return mGetValueImpl.test(flagName, getter); } - public void resetAll() { - for (Map.Entry entry : mFlagMap.entrySet()) { - entry.setValue(null); - } + + public List getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RW_EXPORTED, + Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, + Flags.FLAG_ENABLED_RO_EXPORTED + ); } - private boolean getValue(String flagName) { - Boolean value = this.mFlagMap.get(flagName); - if (value == null) { - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; - } - private Map mFlagMap = new HashMap<>( - Map.ofEntries( - Map.entry(Flags.FLAG_DISABLED_RW_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO_EXPORTED, false), - Map.entry(Flags.FLAG_ENABLED_RO_EXPORTED, false) - ) - ); + private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( "" @@ -732,9 +790,13 @@ mod tests { ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlags.java", expect_feature_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_feature_flags_impl_content), + ( + "com/android/aconfig/test/CustomFeatureFlags.java", + expect_custom_feature_flags_content, + ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", - expect_fake_feature_flags_impl_content, + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, ), ]); @@ -853,6 +915,10 @@ mod tests { ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()), ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_COMMON_CONTENT), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), + ( + "com/android/aconfig/test/CustomFeatureFlags.java", + EXPECTED_CUSTOMFEATUREFLAGS_CONTENT, + ), ( "com/android/aconfig/test/FakeFeatureFlagsImpl.java", EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, @@ -1020,61 +1086,64 @@ mod tests { private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl(); }"#; - let expect_fakefeatureflags_content = r#" + let expect_customfeatureflags_content = r#" package com.android.aconfig.test; + // TODO(b/303773055): Remove the annotation after access issue is resolved. import android.compat.annotation.UnsupportedAppUsage; import java.util.Arrays; - import java.util.HashMap; import java.util.HashSet; - import java.util.Map; + import java.util.List; import java.util.Set; + import java.util.function.BiPredicate; + import java.util.function.Predicate; + /** @hide */ - public class FakeFeatureFlagsImpl implements FeatureFlags { - public FakeFeatureFlagsImpl() { - resetAll(); + public class CustomFeatureFlags implements FeatureFlags { + + private BiPredicate> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate> getValueImpl) { + mGetValueImpl = getValueImpl; } + @Override @UnsupportedAppUsage public boolean disabledRo() { - return getValue(Flags.FLAG_DISABLED_RO); + return getValue(Flags.FLAG_DISABLED_RO, + FeatureFlags::disabledRo); } @Override @UnsupportedAppUsage public boolean disabledRw() { - return getValue(Flags.FLAG_DISABLED_RW); + return getValue(Flags.FLAG_DISABLED_RW, + FeatureFlags::disabledRw); } @Override @UnsupportedAppUsage public boolean disabledRwInOtherNamespace() { - return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE); + return getValue(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + FeatureFlags::disabledRwInOtherNamespace); } @Override @UnsupportedAppUsage public boolean enabledFixedRo() { - return getValue(Flags.FLAG_ENABLED_FIXED_RO); + return getValue(Flags.FLAG_ENABLED_FIXED_RO, + FeatureFlags::enabledFixedRo); } @Override @UnsupportedAppUsage public boolean enabledRo() { - return getValue(Flags.FLAG_ENABLED_RO); + return getValue(Flags.FLAG_ENABLED_RO, + FeatureFlags::enabledRo); } @Override @UnsupportedAppUsage public boolean enabledRw() { - return getValue(Flags.FLAG_ENABLED_RW); - } - public void setFlag(String flagName, boolean value) { - if (!this.mFlagMap.containsKey(flagName)) { - throw new IllegalArgumentException("no such flag " + flagName); - } - this.mFlagMap.put(flagName, value); - } - public void resetAll() { - for (Map.Entry entry : mFlagMap.entrySet()) { - entry.setValue(null); - } + return getValue(Flags.FLAG_ENABLED_RW, + FeatureFlags::enabledRw); } + public boolean isFlagReadOnlyOptimized(String flagName) { if (mReadOnlyFlagsSet.contains(flagName) && isOptimizationEnabled()) { @@ -1082,27 +1151,27 @@ mod tests { } return false; } + @com.android.aconfig.annotations.AssumeTrueForR8 private boolean isOptimizationEnabled() { return false; } - private boolean getValue(String flagName) { - Boolean value = this.mFlagMap.get(flagName); - if (value == null) { - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; + + protected boolean getValue(String flagName, Predicate getter) { + return mGetValueImpl.test(flagName, getter); } - private Map mFlagMap = new HashMap<>( - Map.ofEntries( - Map.entry(Flags.FLAG_DISABLED_RO, false), - Map.entry(Flags.FLAG_DISABLED_RW, false), - Map.entry(Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, false), - Map.entry(Flags.FLAG_ENABLED_FIXED_RO, false), - Map.entry(Flags.FLAG_ENABLED_RO, false), - Map.entry(Flags.FLAG_ENABLED_RW, false) - ) - ); + + public List getFlagNames() { + return Arrays.asList( + Flags.FLAG_DISABLED_RO, + Flags.FLAG_DISABLED_RW, + Flags.FLAG_DISABLED_RW_IN_OTHER_NAMESPACE, + Flags.FLAG_ENABLED_FIXED_RO, + Flags.FLAG_ENABLED_RO, + Flags.FLAG_ENABLED_RW + ); + } + private Set mReadOnlyFlagsSet = new HashSet<>( Arrays.asList( Flags.FLAG_DISABLED_RO, @@ -1116,11 +1185,16 @@ mod tests { ); } "#; + let mut file_set = HashMap::from([ ("com/android/aconfig/test/Flags.java", expect_flags_content), ("com/android/aconfig/test/FeatureFlagsImpl.java", expect_featureflagsimpl_content), ("com/android/aconfig/test/FeatureFlags.java", expect_featureflags_content), - ("com/android/aconfig/test/FakeFeatureFlagsImpl.java", expect_fakefeatureflags_content), + ("com/android/aconfig/test/CustomFeatureFlags.java", expect_customfeatureflags_content), + ( + "com/android/aconfig/test/FakeFeatureFlagsImpl.java", + EXPECTED_FAKEFEATUREFLAGSIMPL_CONTENT, + ), ]); for file in generated_files { diff --git a/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template new file mode 100644 index 0000000000..b82b9cb827 --- /dev/null +++ b/tools/aconfig/aconfig/templates/CustomFeatureFlags.java.template @@ -0,0 +1,70 @@ +package {package_name}; + +{{ if not library_exported- }} +// TODO(b/303773055): Remove the annotation after access issue is resolved. +import android.compat.annotation.UnsupportedAppUsage; +{{ -endif }} +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +/** @hide */ +public class CustomFeatureFlags implements FeatureFlags \{ + + private BiPredicate> mGetValueImpl; + + public CustomFeatureFlags(BiPredicate> getValueImpl) \{ + mGetValueImpl = getValueImpl; + } + +{{ -for item in flag_elements}} + @Override +{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }} + public boolean {item.method_name}() \{ + return getValue(Flags.FLAG_{item.flag_name_constant_suffix}, + FeatureFlags::{item.method_name}); + } +{{ endfor }} + +{{ -if not library_exported }} + public boolean isFlagReadOnlyOptimized(String flagName) \{ + if (mReadOnlyFlagsSet.contains(flagName) && + isOptimizationEnabled()) \{ + return true; + } + return false; + } + + @com.android.aconfig.annotations.AssumeTrueForR8 + private boolean isOptimizationEnabled() \{ + return false; + } +{{ -endif }} + + protected boolean getValue(String flagName, Predicate getter) \{ + return mGetValueImpl.test(flagName, getter); + } + + public List getFlagNames() \{ + return Arrays.asList( + {{ -for item in flag_elements }} + Flags.FLAG_{item.flag_name_constant_suffix} + {{ -if not @last }},{{ endif }} + {{ -endfor }} + ); + } + + private Set mReadOnlyFlagsSet = new HashSet<>( + Arrays.asList( + {{ -for item in flag_elements }} + {{ -if not item.is_read_write }} + Flags.FLAG_{item.flag_name_constant_suffix}, + {{ -endif }} + {{ -endfor }} + ""{# The empty string here is to resolve the ending comma #} + ) + ); +} diff --git a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template index 177e711e77..c20d3c5061 100644 --- a/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template +++ b/tools/aconfig/aconfig/templates/FakeFeatureFlagsImpl.java.template @@ -1,27 +1,30 @@ package {package_name}; -{{ if not library_exported- }} -// TODO(b/303773055): Remove the annotation after access issue is resolved. -import android.compat.annotation.UnsupportedAppUsage; -{{ -endif }} -import java.util.Arrays; + import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; +import java.util.function.Predicate; /** @hide */ -public class FakeFeatureFlagsImpl implements FeatureFlags \{ +public class FakeFeatureFlagsImpl extends CustomFeatureFlags \{ + private Map mFlagMap = new HashMap<>(); + public FakeFeatureFlagsImpl() \{ - resetAll(); + super(null); + // Initialize the map with null values + for (String flagName : getFlagNames()) \{ + mFlagMap.put(flagName, null); + } } -{{ for item in flag_elements}} @Override -{{ if not library_exported }} @UnsupportedAppUsage{{ -endif }} - public boolean {item.method_name}() \{ - return getValue(Flags.FLAG_{item.flag_name_constant_suffix}); + protected boolean getValue(String flagName, Predicate getter) \{ + Boolean value = this.mFlagMap.get(flagName); + if (value == null) \{ + throw new IllegalArgumentException(flagName + " is not set"); + } + return value; } -{{ endfor}} + public void setFlag(String flagName, boolean value) \{ if (!this.mFlagMap.containsKey(flagName)) \{ throw new IllegalArgumentException("no such flag " + flagName); @@ -34,46 +37,4 @@ public class FakeFeatureFlagsImpl implements FeatureFlags \{ entry.setValue(null); } } -{{ if not library_exported }} - public boolean isFlagReadOnlyOptimized(String flagName) \{ - if (mReadOnlyFlagsSet.contains(flagName) && - isOptimizationEnabled()) \{ - return true; - } - return false; - } - - @com.android.aconfig.annotations.AssumeTrueForR8 - private boolean isOptimizationEnabled() \{ - return false; - } -{{ -endif }} - private boolean getValue(String flagName) \{ - Boolean value = this.mFlagMap.get(flagName); - if (value == null) \{ - throw new IllegalArgumentException(flagName + " is not set"); - } - return value; - } - - - private Map mFlagMap = new HashMap<>( - Map.ofEntries( - {{ -for item in flag_elements }} - Map.entry(Flags.FLAG_{item.flag_name_constant_suffix}, false) - {{ -if not @last }},{{ endif }} - {{ -endfor }} - ) - ); - - private Set mReadOnlyFlagsSet = new HashSet<>( - Arrays.asList( - {{ -for item in flag_elements }} - {{ -if not item.is_read_write }} - Flags.FLAG_{item.flag_name_constant_suffix}, - {{ -endif }} - {{ -endfor }} - ""{# The empty string here is to resolve the ending comma #} - ) - ); }