Merge changes from topic "printflags-include-device-config" into main

* changes:
  printflags: add printflags to PRODUCT_PACKAGES
  printflags: include device_config values
This commit is contained in:
Mårten Kongstad
2023-09-27 13:04:40 +00:00
committed by Gerrit Code Review
3 changed files with 86 additions and 9 deletions

View File

@@ -235,6 +235,7 @@ PRODUCT_PACKAGES += \
pm \
pppd \
preinstalled-packages-platform.xml \
printflags \
privapp-permissions-platform.xml \
prng_seeder \
racoon \

View File

@@ -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"],
}

View File

@@ -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<String, String> {
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<String, Vec<String>> = 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);
}
}