Merge "Prevent VABC from being disabled on release-key builds" into main
diff --git a/core/soong_config.mk b/core/soong_config.mk
index e3eb780..52bcd9a 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -40,7 +40,9 @@
 $(call add_json_str,  Platform_base_os,                  $(PLATFORM_BASE_OS))
 $(call add_json_str,  Platform_version_last_stable,      $(PLATFORM_VERSION_LAST_STABLE))
 $(call add_json_str,  Platform_version_known_codenames,  $(PLATFORM_VERSION_KNOWN_CODENAMES))
+
 $(call add_json_bool, Release_aidl_use_unfrozen,         $(RELEASE_AIDL_USE_UNFROZEN))
+$(call add_json_bool, Release_expose_flagged_api,        $(RELEASE_EXPOSE_FLAGGED_API))
 
 $(call add_json_str,  Platform_min_supported_target_sdk_version, $(PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION))
 
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index f8dbafd..29e4ecd 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -235,6 +235,7 @@
     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 5d73d96..da18cdc 100644
--- a/tools/aconfig/printflags/Android.bp
+++ b/tools/aconfig/printflags/Android.bp
@@ -2,8 +2,8 @@
     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 @@
         "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 a9f7c03..88fdea9 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<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 @@
         };
         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);
+    }
+}