aconfig: add fixed read only flag
Add a new field in the declaration to indicate whether
the permission can be overridden.
When the field “is_fixed_read_only” is set to true,
the flag permission will be set as fixed “READ_ONLY”,
and the permission should not be changed by Gantry.
Bug: 292521627
Test: atest aconfig.test
Change-Id: Ic9bcd7823bccb8b947cf05568c7ced3763490a23
diff --git a/tools/aconfig/protos/aconfig.proto b/tools/aconfig/protos/aconfig.proto
index 4cad69a..d5e2868 100644
--- a/tools/aconfig/protos/aconfig.proto
+++ b/tools/aconfig/protos/aconfig.proto
@@ -39,6 +39,7 @@
optional string namespace = 2;
optional string description = 3;
repeated string bug = 4;
+ optional bool is_fixed_read_only = 5;
};
message flag_declarations {
@@ -75,6 +76,7 @@
optional flag_state state = 6;
optional flag_permission permission = 7;
repeated tracepoint trace = 8;
+ optional bool is_fixed_read_only = 9;
}
message parsed_flags {
diff --git a/tools/aconfig/src/codegen_cpp.rs b/tools/aconfig/src/codegen_cpp.rs
index 30e564a..8c2d7ba 100644
--- a/tools/aconfig/src/codegen_cpp.rs
+++ b/tools/aconfig/src/codegen_cpp.rs
@@ -131,6 +131,8 @@
virtual bool disabled_rw() = 0;
+ virtual bool enabled_fixed_ro() = 0;
+
virtual bool enabled_ro() = 0;
virtual bool enabled_rw() = 0;
@@ -146,6 +148,10 @@
return provider_->disabled_rw();
}
+inline bool enabled_fixed_ro() {
+ return true;
+}
+
inline bool enabled_ro() {
return true;
}
@@ -163,6 +169,8 @@
bool com_android_aconfig_test_disabled_rw();
+bool com_android_aconfig_test_enabled_fixed_ro();
+
bool com_android_aconfig_test_enabled_ro();
bool com_android_aconfig_test_enabled_rw();
@@ -194,6 +202,10 @@
virtual void disabled_rw(bool val) = 0;
+ virtual bool enabled_fixed_ro() = 0;
+
+ virtual void enabled_fixed_ro(bool val) = 0;
+
virtual bool enabled_ro() = 0;
virtual void enabled_ro(bool val) = 0;
@@ -223,6 +235,14 @@
provider_->disabled_rw(val);
}
+inline bool enabled_fixed_ro() {
+ return provider_->enabled_fixed_ro();
+}
+
+inline void enabled_fixed_ro(bool val) {
+ provider_->enabled_fixed_ro(val);
+}
+
inline bool enabled_ro() {
return provider_->enabled_ro();
}
@@ -256,6 +276,10 @@
void set_com_android_aconfig_test_disabled_rw(bool val);
+bool com_android_aconfig_test_enabled_fixed_ro();
+
+void set_com_android_aconfig_test_enabled_fixed_ro(bool val);
+
bool com_android_aconfig_test_enabled_ro();
void set_com_android_aconfig_test_enabled_ro(bool val);
@@ -294,6 +318,10 @@
"false") == "true";
}
+ virtual bool enabled_fixed_ro() override {
+ return true;
+ }
+
virtual bool enabled_ro() override {
return true;
}
@@ -319,6 +347,10 @@
return com::android::aconfig::test::disabled_rw();
}
+bool com_android_aconfig_test_enabled_fixed_ro() {
+ return true;
+}
+
bool com_android_aconfig_test_enabled_ro() {
return true;
}
@@ -373,6 +405,19 @@
overrides_["disabled_rw"] = val;
}
+ virtual bool enabled_fixed_ro() override {
+ auto it = overrides_.find("enabled_fixed_ro");
+ if (it != overrides_.end()) {
+ return it->second;
+ } else {
+ return true;
+ }
+ }
+
+ virtual void enabled_fixed_ro(bool val) override {
+ overrides_["enabled_fixed_ro"] = val;
+ }
+
virtual bool enabled_ro() override {
auto it = overrides_.find("enabled_ro");
if (it != overrides_.end()) {
@@ -402,7 +447,6 @@
overrides_["enabled_rw"] = val;
}
-
virtual void reset_flags() override {
overrides_.clear();
}
@@ -430,6 +474,16 @@
com::android::aconfig::test::disabled_rw(val);
}
+
+bool com_android_aconfig_test_enabled_fixed_ro() {
+ return com::android::aconfig::test::enabled_fixed_ro();
+}
+
+void set_com_android_aconfig_test_enabled_fixed_ro(bool val) {
+ com::android::aconfig::test::enabled_fixed_ro(val);
+}
+
+
bool com_android_aconfig_test_enabled_ro() {
return com::android::aconfig::test::enabled_ro();
}
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 2c9dcf5..be0ec97 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -121,6 +121,7 @@
public interface FeatureFlags {
boolean disabledRo();
boolean disabledRw();
+ boolean enabledFixedRo();
boolean enabledRo();
boolean enabledRw();
"#;
@@ -130,6 +131,7 @@
public final class Flags {
public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
+ public static final String FLAG_ENABLED_FIXED_RO = "com.android.aconfig.test.enabled_fixed_ro";
public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
@@ -139,6 +141,9 @@
public static boolean disabledRw() {
return FEATURE_FLAGS.disabledRw();
}
+ public static boolean enabledFixedRo() {
+ return FEATURE_FLAGS.enabledFixedRo();
+ }
public static boolean enabledRo() {
return FEATURE_FLAGS.enabledRo();
}
@@ -159,6 +164,11 @@
"Method is not implemented.");
}
@Override
+ public boolean enabledFixedRo() {
+ throw new UnsupportedOperationException(
+ "Method is not implemented.");
+ }
+ @Override
public boolean enabledRo() {
throw new UnsupportedOperationException(
"Method is not implemented.");
@@ -211,6 +221,10 @@
);
}
@Override
+ public boolean enabledFixedRo() {
+ return true;
+ }
+ @Override
public boolean enabledRo() {
return true;
}
@@ -311,6 +325,10 @@
return getFlag(Flags.FLAG_DISABLED_RW);
}
@Override
+ public boolean enabledFixedRo() {
+ return getFlag(Flags.FLAG_ENABLED_FIXED_RO);
+ }
+ @Override
public boolean enabledRo() {
return getFlag(Flags.FLAG_ENABLED_RO);
}
@@ -341,6 +359,7 @@
private HashMap<String, Boolean> mFlagMap = Stream.of(
Flags.FLAG_DISABLED_RO,
Flags.FLAG_DISABLED_RW,
+ Flags.FLAG_ENABLED_FIXED_RO,
Flags.FLAG_ENABLED_RO,
Flags.FLAG_ENABLED_RW
)
diff --git a/tools/aconfig/src/codegen_rust.rs b/tools/aconfig/src/codegen_rust.rs
index 0234eb2..4e4c7dd 100644
--- a/tools/aconfig/src/codegen_rust.rs
+++ b/tools/aconfig/src/codegen_rust.rs
@@ -108,6 +108,11 @@
"false") == "true"
}
+ /// query flag enabled_fixed_ro
+ pub fn enabled_fixed_ro(&self) -> bool {
+ true
+ }
+
/// query flag enabled_ro
pub fn enabled_ro(&self) -> bool {
true
@@ -137,6 +142,12 @@
PROVIDER.disabled_rw()
}
+/// query flag enabled_fixed_ro
+#[inline(always)]
+pub fn enabled_fixed_ro() -> bool {
+ true
+}
+
/// query flag enabled_ro
#[inline(always)]
pub fn enabled_ro() -> bool {
@@ -189,6 +200,18 @@
self.overrides.insert("disabled_rw", val);
}
+ /// query flag enabled_fixed_ro
+ pub fn enabled_fixed_ro(&self) -> bool {
+ self.overrides.get("enabled_fixed_ro").copied().unwrap_or(
+ true
+ )
+ }
+
+ /// set flag enabled_fixed_ro
+ pub fn set_enabled_fixed_ro(&mut self, val: bool) {
+ self.overrides.insert("enabled_fixed_ro", val);
+ }
+
/// query flag enabled_ro
pub fn enabled_ro(&self) -> bool {
self.overrides.get("enabled_ro").copied().unwrap_or(
@@ -251,6 +274,18 @@
PROVIDER.lock().unwrap().set_disabled_rw(val);
}
+/// query flag enabled_fixed_ro
+#[inline(always)]
+pub fn enabled_fixed_ro() -> bool {
+ PROVIDER.lock().unwrap().enabled_fixed_ro()
+}
+
+/// set flag enabled_fixed_ro
+#[inline(always)]
+pub fn set_enabled_fixed_ro(val: bool) {
+ PROVIDER.lock().unwrap().set_enabled_fixed_ro(val);
+}
+
/// query flag enabled_ro
#[inline(always)]
pub fn enabled_ro() -> bool {
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index ab5b0f2..e4baa82 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -91,11 +91,17 @@
parsed_flag.set_description(flag_declaration.take_description());
parsed_flag.bug.append(&mut flag_declaration.bug);
parsed_flag.set_state(DEFAULT_FLAG_STATE);
- parsed_flag.set_permission(default_permission);
+ let flag_permission = if flag_declaration.is_fixed_read_only() {
+ ProtoFlagPermission::READ_ONLY
+ } else {
+ default_permission
+ };
+ parsed_flag.set_permission(flag_permission);
+ parsed_flag.set_is_fixed_read_only(flag_declaration.is_fixed_read_only());
let mut tracepoint = ProtoTracepoint::new();
tracepoint.set_source(input.source.clone());
tracepoint.set_state(DEFAULT_FLAG_STATE);
- tracepoint.set_permission(default_permission);
+ tracepoint.set_permission(flag_permission);
parsed_flag.trace.push(tracepoint);
// verify ParsedFlag looks reasonable
@@ -135,6 +141,13 @@
continue;
};
+ ensure!(
+ !parsed_flag.is_fixed_read_only()
+ || flag_value.permission() == ProtoFlagPermission::READ_ONLY,
+ "failed to set permission of flag {}, since this flag is fixed read only flag",
+ flag_value.name()
+ );
+
parsed_flag.set_state(flag_value.state());
parsed_flag.set_permission(flag_value.permission());
let mut tracepoint = ProtoTracepoint::new();
@@ -310,6 +323,7 @@
assert_eq!(ProtoFlagState::ENABLED, enabled_ro.state());
assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.permission());
assert_eq!(3, enabled_ro.trace.len());
+ assert!(!enabled_ro.is_fixed_read_only());
assert_eq!("tests/test.aconfig", enabled_ro.trace[0].source());
assert_eq!(ProtoFlagState::DISABLED, enabled_ro.trace[0].state());
assert_eq!(ProtoFlagPermission::READ_WRITE, enabled_ro.trace[0].permission());
@@ -320,8 +334,11 @@
assert_eq!(ProtoFlagState::ENABLED, enabled_ro.trace[2].state());
assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_ro.trace[2].permission());
- assert_eq!(4, parsed_flags.parsed_flag.len());
+ assert_eq!(5, parsed_flags.parsed_flag.len());
for pf in parsed_flags.parsed_flag.iter() {
+ if pf.name() == "enabled_fixed_ro" {
+ continue;
+ }
let first = pf.trace.first().unwrap();
assert_eq!(DEFAULT_FLAG_STATE, first.state());
assert_eq!(DEFAULT_FLAG_PERMISSION, first.permission());
@@ -330,6 +347,15 @@
assert_eq!(pf.state(), last.state());
assert_eq!(pf.permission(), last.permission());
}
+
+ let enabled_fixed_ro =
+ parsed_flags.parsed_flag.iter().find(|pf| pf.name() == "enabled_fixed_ro").unwrap();
+ assert!(enabled_fixed_ro.is_fixed_read_only());
+ assert_eq!(ProtoFlagState::ENABLED, enabled_fixed_ro.state());
+ assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.permission());
+ assert_eq!(2, enabled_fixed_ro.trace.len());
+ assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[0].permission());
+ assert_eq!(ProtoFlagPermission::READ_ONLY, enabled_fixed_ro.trace[1].permission());
}
#[test]
@@ -363,6 +389,46 @@
}
#[test]
+ fn test_parse_flags_override_fixed_read_only() {
+ let first_flag = r#"
+ package: "com.first"
+ flag {
+ name: "first"
+ namespace: "first_ns"
+ description: "This is the description of the first flag."
+ bug: "123"
+ is_fixed_read_only: true
+ }
+ "#;
+ let declaration =
+ vec![Input { source: "memory".to_string(), reader: Box::new(first_flag.as_bytes()) }];
+
+ let first_flag_value = r#"
+ flag_value {
+ package: "com.first"
+ name: "first"
+ state: DISABLED
+ permission: READ_WRITE
+ }
+ "#;
+ let value = vec![Input {
+ source: "memory".to_string(),
+ reader: Box::new(first_flag_value.as_bytes()),
+ }];
+ let error = crate::commands::parse_flags(
+ "com.first",
+ declaration,
+ value,
+ ProtoFlagPermission::READ_WRITE,
+ )
+ .unwrap_err();
+ assert_eq!(
+ format!("{:?}", error),
+ "failed to set permission of flag first, since this flag is fixed read only flag"
+ );
+ }
+
+ #[test]
fn test_create_device_config_defaults() {
let input = parse_test_flags_as_input();
let bytes = create_device_config_defaults(input).unwrap();
diff --git a/tools/aconfig/src/protos.rs b/tools/aconfig/src/protos.rs
index c3911e5..b93abcc 100644
--- a/tools/aconfig/src/protos.rs
+++ b/tools/aconfig/src/protos.rs
@@ -303,6 +303,7 @@
namespace: "second_ns"
description: "This is the description of the second flag."
bug: "abc"
+ is_fixed_read_only: true
}
"#,
)
@@ -313,11 +314,13 @@
assert_eq!(first.namespace(), "first_ns");
assert_eq!(first.description(), "This is the description of the first flag.");
assert_eq!(first.bug, vec!["123"]);
+ assert!(!first.is_fixed_read_only());
let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
assert_eq!(second.name(), "second");
assert_eq!(second.namespace(), "second_ns");
assert_eq!(second.description(), "This is the description of the second flag.");
assert_eq!(second.bug, vec!["abc"]);
+ assert!(second.is_fixed_read_only());
// bad input: missing package in flag declarations
let error = flag_declarations::try_from_text_proto(
@@ -555,6 +558,7 @@
state: ENABLED
permission: READ_WRITE
}
+ is_fixed_read_only: true
}
"#;
let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
@@ -574,6 +578,7 @@
assert_eq!(second.trace[1].source(), "flags.values");
assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
+ assert!(second.is_fixed_read_only());
// valid input: empty
let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
diff --git a/tools/aconfig/src/test.rs b/tools/aconfig/src/test.rs
index 6c27885..9034704 100644
--- a/tools/aconfig/src/test.rs
+++ b/tools/aconfig/src/test.rs
@@ -41,6 +41,7 @@
state: DISABLED
permission: READ_ONLY
}
+ is_fixed_read_only: false
}
parsed_flag {
package: "com.android.aconfig.test"
@@ -55,6 +56,27 @@
state: DISABLED
permission: READ_WRITE
}
+ is_fixed_read_only: false
+}
+parsed_flag {
+ package: "com.android.aconfig.test"
+ name: "enabled_fixed_ro"
+ namespace: "aconfig_test"
+ description: "This flag is fixed READ_ONLY + ENABLED"
+ bug: ""
+ state: ENABLED
+ permission: READ_ONLY
+ trace {
+ source: "tests/test.aconfig"
+ state: DISABLED
+ permission: READ_ONLY
+ }
+ trace {
+ source: "tests/first.values"
+ state: ENABLED
+ permission: READ_ONLY
+ }
+ is_fixed_read_only: true
}
parsed_flag {
package: "com.android.aconfig.test"
@@ -79,6 +101,7 @@
state: ENABLED
permission: READ_ONLY
}
+ is_fixed_read_only: false
}
parsed_flag {
package: "com.android.aconfig.test"
@@ -98,6 +121,7 @@
state: ENABLED
permission: READ_WRITE
}
+ is_fixed_read_only: false
}
"#;
diff --git a/tools/aconfig/tests/AconfigTest.java b/tools/aconfig/tests/AconfigTest.java
index 317289d..79b5a55 100644
--- a/tools/aconfig/tests/AconfigTest.java
+++ b/tools/aconfig/tests/AconfigTest.java
@@ -1,9 +1,11 @@
import static com.android.aconfig.test.Flags.FLAG_DISABLED_RO;
import static com.android.aconfig.test.Flags.FLAG_DISABLED_RW;
+import static com.android.aconfig.test.Flags.FLAG_ENABLED_FIXED_RO;
import static com.android.aconfig.test.Flags.FLAG_ENABLED_RO;
import static com.android.aconfig.test.Flags.FLAG_ENABLED_RW;
import static com.android.aconfig.test.Flags.disabledRo;
import static com.android.aconfig.test.Flags.disabledRw;
+import static com.android.aconfig.test.Flags.enabledFixedRo;
import static com.android.aconfig.test.Flags.enabledRo;
import static com.android.aconfig.test.Flags.enabledRw;
import static org.junit.Assert.assertEquals;
@@ -35,6 +37,14 @@
}
@Test
+ public void testEnabledFixedReadOnlyFlag() {
+ assertEquals("com.android.aconfig.test.enabled_fixed_ro", FLAG_ENABLED_FIXED_RO);
+ // TODO: change to assertTrue(enabledFixedRo()) when the build supports reading tests/*.values
+ // (currently all flags are assigned the default READ_ONLY + DISABLED)
+ assertFalse(enabledFixedRo());
+ }
+
+ @Test
public void testDisabledReadWriteFlag() {
assertEquals("com.android.aconfig.test.enabled_ro", FLAG_ENABLED_RO);
assertFalse(disabledRw());
diff --git a/tools/aconfig/tests/first.values b/tools/aconfig/tests/first.values
index e524404..a450f78 100644
--- a/tools/aconfig/tests/first.values
+++ b/tools/aconfig/tests/first.values
@@ -16,3 +16,9 @@
state: ENABLED
permission: READ_WRITE
}
+flag_value {
+ package: "com.android.aconfig.test"
+ name: "enabled_fixed_ro"
+ state: ENABLED
+ permission: READ_ONLY
+}
diff --git a/tools/aconfig/tests/test.aconfig b/tools/aconfig/tests/test.aconfig
index 46cf1e9..aaa6df5 100644
--- a/tools/aconfig/tests/test.aconfig
+++ b/tools/aconfig/tests/test.aconfig
@@ -40,3 +40,14 @@
description: "This flag is DISABLED + READ_WRITE"
bug: "456"
}
+
+# This flag's final value calculated from:
+# - test.aconfig: DISABLED + READ_ONLY
+# - first.values: ENABLED + READ_ONLY
+flag {
+ name: "enabled_fixed_ro"
+ namespace: "aconfig_test"
+ description: "This flag is fixed READ_ONLY + ENABLED"
+ bug: ""
+ is_fixed_read_only: true
+}