diff --git a/target/product/base_system.mk b/target/product/base_system.mk index f8dbafd6e7..29e4ecd557 100644 --- a/target/product/base_system.mk +++ b/target/product/base_system.mk @@ -235,6 +235,7 @@ PRODUCT_PACKAGES += \ pm \ pppd \ preinstalled-packages-platform.xml \ + printflags \ privapp-permissions-platform.xml \ prng_seeder \ racoon \ diff --git a/tools/aconfig/printflags/Android.bp b/tools/aconfig/printflags/Android.bp index 5d73d96c72..da18cdc589 100644 --- a/tools/aconfig/printflags/Android.bp +++ b/tools/aconfig/printflags/Android.bp @@ -2,8 +2,8 @@ package { default_applicable_licenses: ["Android-Apache-2.0"], } -rust_binary { - name: "printflags", +rust_defaults { + name: "printflags.defaults", edition: "2021", clippy_lints: "android", lints: "android", @@ -12,5 +12,16 @@ rust_binary { "libaconfig_protos", "libanyhow", "libprotobuf", + "libregex", ], } + +rust_binary { + name: "printflags", + defaults: ["printflags.defaults"], +} + +rust_test_host { + name: "printflags.test", + defaults: ["printflags.defaults"], +} diff --git a/tools/aconfig/printflags/src/main.rs b/tools/aconfig/printflags/src/main.rs index a9f7c03b1e..88fdea9d45 100644 --- a/tools/aconfig/printflags/src/main.rs +++ b/tools/aconfig/printflags/src/main.rs @@ -16,12 +16,43 @@ //! `printflags` is a device binary to print feature flags. +use aconfig_protos::aconfig::Flag_state as State; use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags; -use anyhow::Result; +use anyhow::{bail, Result}; +use regex::Regex; use std::collections::HashMap; -use std::fs; +use std::process::Command; +use std::{fs, str}; + +fn parse_device_config(raw: &str) -> HashMap { + let mut flags = HashMap::new(); + let regex = Regex::new(r"(?m)^([[[:alnum:]]_]+/[[[:alnum:]]_\.]+)=(true|false)$").unwrap(); + for capture in regex.captures_iter(raw) { + let key = capture.get(1).unwrap().as_str().to_string(); + let value = match capture.get(2).unwrap().as_str() { + "true" => format!("{:?} (device_config)", State::ENABLED), + "false" => format!("{:?} (device_config)", State::DISABLED), + _ => panic!(), + }; + flags.insert(key, value); + } + flags +} fn main() -> Result<()> { + // read device_config + let output = Command::new("/system/bin/device_config").arg("list").output()?; + if !output.status.success() { + let reason = match output.status.code() { + Some(code) => format!("exit code {}", code), + None => "terminated by signal".to_string(), + }; + bail!("failed to execute device_config: {}", reason); + } + let dc_stdout = str::from_utf8(&output.stdout)?; + let device_config_flags = parse_device_config(dc_stdout); + + // read aconfig_flags.pb files let mut flags: HashMap> = HashMap::new(); for partition in ["system", "system_ext", "product", "vendor"] { let path = format!("/{}/etc/aconfig_flags.pb", partition); @@ -31,15 +62,49 @@ fn main() -> Result<()> { }; let parsed_flags: ProtoParsedFlags = protobuf::Message::parse_from_bytes(&bytes)?; for flag in parsed_flags.parsed_flag { - let key = format!("{}.{}", flag.package(), flag.name()); + let key = format!("{}/{}.{}", flag.namespace(), flag.package(), flag.name()); let value = format!("{:?} + {:?} ({})", flag.permission(), flag.state(), partition); flags.entry(key).or_default().push(value); } } - for (key, value) in flags { - // TODO: if the flag is READ_WRITE (for any partition), call "device_config get" to obtain - // the flag's current state, and append value to the output - println!("{}: {}", key, value.join(", ")); + + // print flags + for (key, mut value) in flags { + let (_, package_and_name) = key.split_once('/').unwrap(); + if let Some(dc_value) = device_config_flags.get(&key) { + value.push(dc_value.to_string()); + } + println!("{}: {}", package_and_name, value.join(", ")); } + Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_foo() { + let input = r#" +namespace_one/com.foo.bar.flag_one=true +namespace_one/com.foo.bar.flag_two=false +random_noise; +namespace_two/android.flag_one=true +namespace_two/android.flag_two=nonsense +"#; + let expected = HashMap::from([ + ( + "namespace_one/com.foo.bar.flag_one".to_string(), + "ENABLED (device_config)".to_string(), + ), + ( + "namespace_one/com.foo.bar.flag_two".to_string(), + "DISABLED (device_config)".to_string(), + ), + ("namespace_two/android.flag_one".to_string(), "ENABLED (device_config)".to_string()), + ]); + let actual = parse_device_config(input); + assert_eq!(expected, actual); + } +}