aconfig: add create-device-config-defaults command

DeviceConfig is the backend for READ_WRITE flags.

Add a new command "create-device-config-defaults" to create a file that
DeviceConfig will read to pre-populate its data store on first init.

This will be used to quickly switch flag values during CI tests:
rebuilding and reflashing a device would have the same effect, but would
be costlier. This feature is not expected to be used outside CI tests.

Note: because DeviceConfig only works with READ_WRITE flags, the
generated file excludes READ_ONLY flags.

Bug: 285468565
Test: atest aconfig.test
Change-Id: I4caff1a10647b8da0ce4e3615678993a957a92dd
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index b3b6ac4..f632ce7 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -65,6 +65,11 @@
                 .arg(Arg::new("out").long("out").required(true)),
         )
         .subcommand(
+            Command::new("create-device-config-defaults")
+                .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
+                .arg(Arg::new("out").long("out").default_value("-")),
+        )
+        .subcommand(
             Command::new("dump")
                 .arg(Arg::new("cache").long("cache").action(ArgAction::Append).required(true))
                 .arg(
@@ -111,6 +116,15 @@
     Ok(())
 }
 
+fn write_output_to_file_or_stdout(path: &str, data: &[u8]) -> Result<()> {
+    if path == "-" {
+        io::stdout().write_all(data)?;
+    } else {
+        fs::File::create(path)?.write_all(data)?;
+    }
+    Ok(())
+}
+
 fn main() -> Result<()> {
     let matches = cli().get_matches();
     match matches.subcommand() {
@@ -147,6 +161,17 @@
             let generated_file = commands::create_rust_lib(cache)?;
             write_output_file_realtive_to_dir(&dir, &generated_file)?;
         }
+        Some(("create-device-config-defaults", sub_matches)) => {
+            let mut caches = Vec::new();
+            for path in sub_matches.get_many::<String>("cache").unwrap_or_default() {
+                let file = fs::File::open(path)?;
+                let cache = Cache::read_from_reader(file)?;
+                caches.push(cache);
+            }
+            let output = commands::create_device_config_defaults(caches)?;
+            let path = get_required_arg::<String>(sub_matches, "out")?;
+            write_output_to_file_or_stdout(path, &output)?;
+        }
         Some(("dump", sub_matches)) => {
             let mut caches = Vec::new();
             for path in sub_matches.get_many::<String>("cache").unwrap_or_default() {
@@ -157,12 +182,7 @@
             let format = get_required_arg::<DumpFormat>(sub_matches, "format")?;
             let output = commands::dump_cache(caches, *format)?;
             let path = get_required_arg::<String>(sub_matches, "out")?;
-            let mut file: Box<dyn Write> = if *path == "-" {
-                Box::new(io::stdout())
-            } else {
-                Box::new(fs::File::create(path)?)
-            };
-            file.write_all(&output)?;
+            write_output_to_file_or_stdout(path, &output)?;
         }
         _ => unreachable!(),
     }