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
This commit is contained in:
Zhi Dou 2023-08-21 22:49:46 +00:00
parent 07c4b5c0f2
commit 71f1b35fb4
10 changed files with 236 additions and 4 deletions

View file

@ -39,6 +39,7 @@ message flag_declaration {
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 @@ message parsed_flag {
optional flag_state state = 6;
optional flag_permission permission = 7;
repeated tracepoint trace = 8;
optional bool is_fixed_read_only = 9;
}
message parsed_flags {

View file

@ -131,6 +131,8 @@ public:
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 @@ inline bool disabled_rw() {
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_ro();
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 @@ public:
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 @@ inline void disabled_rw(bool val) {
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 @@ bool com_android_aconfig_test_disabled_rw();
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 @@ namespace com::android::aconfig::test {
"false") == "true";
}
virtual bool enabled_fixed_ro() override {
return true;
}
virtual bool enabled_ro() override {
return true;
}
@ -319,6 +347,10 @@ bool com_android_aconfig_test_disabled_rw() {
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 @@ namespace com::android::aconfig::test {
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 @@ namespace com::android::aconfig::test {
overrides_["enabled_rw"] = val;
}
virtual void reset_flags() override {
overrides_.clear();
}
@ -430,6 +474,16 @@ void set_com_android_aconfig_test_disabled_rw(bool val) {
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();
}

View file

@ -121,6 +121,7 @@ mod tests {
public interface FeatureFlags {
boolean disabledRo();
boolean disabledRw();
boolean enabledFixedRo();
boolean enabledRo();
boolean enabledRw();
"#;
@ -130,6 +131,7 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
"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 @@ mod tests {
);
}
@Override
public boolean enabledFixedRo() {
return true;
}
@Override
public boolean enabledRo() {
return true;
}
@ -311,6 +325,10 @@ mod tests {
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 @@ mod tests {
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
)

View file

@ -108,6 +108,11 @@ impl FlagProvider {
"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 @@ pub fn disabled_rw() -> bool {
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 @@ impl FlagProvider {
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 @@ pub fn set_disabled_rw(val: bool) {
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 {

View file

@ -91,11 +91,17 @@ pub fn parse_flags(
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 @@ pub fn parse_flags(
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 @@ mod tests {
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 @@ mod tests {
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 @@ mod tests {
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]
@ -362,6 +388,46 @@ mod tests {
assert_eq!(ProtoFlagPermission::READ_ONLY, parsed_flag.permission());
}
#[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();

View file

@ -303,6 +303,7 @@ flag {
namespace: "second_ns"
description: "This is the description of the second flag."
bug: "abc"
is_fixed_read_only: true
}
"#,
)
@ -313,11 +314,13 @@ flag {
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 @@ parsed_flag {
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 @@ parsed_flag {
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();

View file

@ -41,6 +41,7 @@ parsed_flag {
state: DISABLED
permission: READ_ONLY
}
is_fixed_read_only: false
}
parsed_flag {
package: "com.android.aconfig.test"
@ -55,6 +56,27 @@ parsed_flag {
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 @@ parsed_flag {
state: ENABLED
permission: READ_ONLY
}
is_fixed_read_only: false
}
parsed_flag {
package: "com.android.aconfig.test"
@ -98,6 +121,7 @@ parsed_flag {
state: ENABLED
permission: READ_WRITE
}
is_fixed_read_only: false
}
"#;

View file

@ -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;
@ -34,6 +36,14 @@ public final class AconfigTest {
assertFalse(enabledRo());
}
@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);

View file

@ -16,3 +16,9 @@ flag_value {
state: ENABLED
permission: READ_WRITE
}
flag_value {
package: "com.android.aconfig.test"
name: "enabled_fixed_ro"
state: ENABLED
permission: READ_ONLY
}

View file

@ -40,3 +40,14 @@ flag {
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
}