aflags: add initial enable and disable commands
Bug: 324436145
Test: adb shell aflags enable --help
Change-Id: I6b4af4bccbbc6000a9dcb78c3130f836b0f85bd0
diff --git a/tools/aconfig/aflags/src/device_config_source.rs b/tools/aconfig/aflags/src/device_config_source.rs
index 12a62cf..1cea6ed 100644
--- a/tools/aconfig/aflags/src/device_config_source.rs
+++ b/tools/aconfig/aflags/src/device_config_source.rs
@@ -64,7 +64,7 @@
fn read_pb_files() -> Result<Vec<Flag>> {
let mut flags: BTreeMap<String, Flag> = BTreeMap::new();
for partition in ["system", "system_ext", "product", "vendor"] {
- let path = format!("/{}/etc/aconfig_flags.pb", partition);
+ let path = format!("/{partition}/etc/aconfig_flags.pb");
let Ok(bytes) = fs::read(&path) else {
eprintln!("warning: failed to read {}", path);
continue;
@@ -98,11 +98,13 @@
Ok(flags)
}
-fn read_device_config_output(command: &str) -> Result<String> {
- let output = Command::new("/system/bin/device_config").arg(command).output()?;
+fn read_device_config_output(command: &[&str]) -> Result<String> {
+ let output = Command::new("/system/bin/device_config").args(command).output()?;
if !output.status.success() {
let reason = match output.status.code() {
- Some(code) => format!("exit code {}", code),
+ Some(code) => {
+ format!("exit code {}, output was {}", code, str::from_utf8(&output.stdout)?)
+ }
None => "terminated by signal".to_string(),
};
bail!("failed to execute device_config: {}", reason);
@@ -111,7 +113,7 @@
}
fn read_device_config_flags() -> Result<HashMap<String, String>> {
- let list_output = read_device_config_output("list")?;
+ let list_output = read_device_config_output(&["list"])?;
parse_device_config(&list_output)
}
@@ -145,6 +147,10 @@
let flags = reconcile(&pb_flags, dc_flags);
Ok(flags)
}
+
+ fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()> {
+ read_device_config_output(&["put", namespace, qualified_name, value]).map(|_| ())
+ }
}
#[cfg(test)]
diff --git a/tools/aconfig/aflags/src/main.rs b/tools/aconfig/aflags/src/main.rs
index 1e2a7a0..ef0195f 100644
--- a/tools/aconfig/aflags/src/main.rs
+++ b/tools/aconfig/aflags/src/main.rs
@@ -16,7 +16,7 @@
//! `aflags` is a device binary to read and write aconfig flags.
-use anyhow::Result;
+use anyhow::{anyhow, Result};
use clap::Parser;
mod device_config_source;
@@ -63,8 +63,15 @@
value_picked_from: ValuePickedFrom,
}
+impl Flag {
+ fn qualified_name(&self) -> String {
+ format!("{}.{}", self.package, self.name)
+ }
+}
+
trait FlagSource {
fn list_flags() -> Result<Vec<Flag>>;
+ fn override_flag(namespace: &str, qualified_name: &str, value: &str) -> Result<()>;
}
const ABOUT_TEXT: &str = "Tool for reading and writing flags.
@@ -94,6 +101,18 @@
enum Command {
/// List all aconfig flags on this device.
List,
+
+ /// Enable an aconfig flag on this device, on the next boot.
+ Enable {
+ /// <package>.<flag_name>
+ qualified_name: String,
+ },
+
+ /// Disable an aconfig flag on this device, on the next boot.
+ Disable {
+ /// <package>.<flag_name>
+ qualified_name: String,
+ },
}
struct PaddingInfo {
@@ -125,6 +144,23 @@
format!("{pkg:p0$}{name:p1$}{val:p2$}{value_picked_from:p3$}{perm:p4$}{container}\n")
}
+fn set_flag(qualified_name: &str, value: &str) -> Result<()> {
+ let flags_binding = DeviceConfigSource::list_flags()?;
+ let flag = flags_binding.iter().find(|f| f.qualified_name() == qualified_name).ok_or(
+ anyhow!("no aconfig flag '{qualified_name}'. Does the flag have an .aconfig definition?"),
+ )?;
+
+ if let FlagPermission::ReadOnly = flag.permission {
+ return Err(anyhow!(
+ "could not write flag '{qualified_name}', it is read-only for the current release configuration.",
+ ));
+ }
+
+ DeviceConfigSource::override_flag(&flag.namespace, qualified_name, value)?;
+
+ Ok(())
+}
+
fn list() -> Result<String> {
let flags = DeviceConfigSource::list_flags()?;
let padding_info = PaddingInfo {
@@ -154,10 +190,13 @@
fn main() {
let cli = Cli::parse();
let output = match cli.command {
- Command::List => list(),
+ Command::List => list().map(Some),
+ Command::Enable { qualified_name } => set_flag(&qualified_name, "true").map(|_| None),
+ Command::Disable { qualified_name } => set_flag(&qualified_name, "false").map(|_| None),
};
match output {
- Ok(text) => println!("{text}"),
- Err(msg) => println!("Error: {}", msg),
+ Ok(Some(text)) => println!("{text}"),
+ Ok(None) => (),
+ Err(message) => println!("Error: {message}"),
}
}