microdroid_manager executes a task via microdroid_launcher

Task can be one of "executable" or "microdroid_launcher".
When it is a microdroid_launcher task, microdroid_manager find the
target library in /mnt/apk/lib/{abi}/, and then executes it via
microdroid_launcher.

Bug: 186396080
Test: MicrodroidHostTestCases
Test: /system/bin/microdroid_manager in microdroid launched with
   default configuration.
   it should run testapk's MicrodroidTestNativeLib.so
Change-Id: I065f5c5d789e6a96fd658a52ed8533f42b1cbc42
diff --git a/microdroid_manager/Android.bp b/microdroid_manager/Android.bp
index ea811c8..cb628b1 100644
--- a/microdroid_manager/Android.bp
+++ b/microdroid_manager/Android.bp
@@ -11,8 +11,10 @@
     rustlibs: [
         "libandroid_logger",
         "libanyhow",
+        "libkeystore2_system_property-rust",
         "liblog_rust",
         "libmicrodroid_metadata_proto_rust",
+        "libmicrodroid_payload_config",
         "libprotobuf",
         "libserde_json",
         "libserde",
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 1ab17d5..ae72a59 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -16,39 +16,76 @@
 
 mod ioutil;
 mod metadata;
-mod payload_config;
 
 use android_logger::Config;
+use anyhow::{anyhow, bail, Result};
+use keystore2_system_property::PropertyWatcher;
 use log::{info, Level};
-use payload_config::{Task, VmPayloadConfig};
-use std::io;
+use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
+use std::fs;
 use std::path::Path;
-use std::process::{Command, Stdio};
+use std::process::Command;
+use std::time::Duration;
+
+const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
 
 const LOG_TAG: &str = "MicrodroidManager";
 
-fn main() -> io::Result<()> {
+fn main() -> Result<()> {
     android_logger::init_once(Config::default().with_tag(LOG_TAG).with_min_level(Level::Debug));
 
     info!("started.");
 
     let metadata = metadata::load()?;
     if !metadata.payload_config_path.is_empty() {
-        let config = VmPayloadConfig::load_from(Path::new(&metadata.payload_config_path))?;
+        let config = load_config(Path::new(&metadata.payload_config_path))?;
+
+        // TODO(jooyung): wait until sys.boot_completed?
         if let Some(main_task) = &config.task {
-            exec(main_task)?;
+            exec_task(main_task)?;
         }
     }
 
     Ok(())
 }
 
-/// executes a task
-/// TODO(jooyung): fork a child process
-fn exec(task: &Task) -> io::Result<()> {
-    info!("executing main task {} {:?}...", task.command, task.args);
-    let exit_status =
-        Command::new(&task.command).args(&task.args).stdout(Stdio::inherit()).status()?;
-    info!("exit with {}", &exit_status);
+fn load_config(path: &Path) -> Result<VmPayloadConfig> {
+    info!("loading config from {:?}...", path);
+    let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
+    Ok(serde_json::from_reader(file)?)
+}
+
+fn exec_task(task: &Task) -> Result<()> {
+    info!("executing main task {:?}...", task);
+    build_command(task)?.spawn()?;
     Ok(())
 }
+
+fn build_command(task: &Task) -> Result<Command> {
+    Ok(match task.type_ {
+        TaskType::Executable => {
+            let mut command = Command::new(&task.command);
+            command.args(&task.args);
+            command
+        }
+        TaskType::MicrodroidLauncher => {
+            let mut command = Command::new("/system/bin/microdroid_launcher");
+            command.arg(find_library_path(&task.command)?).args(&task.args);
+            command
+        }
+    })
+}
+
+fn find_library_path(name: &str) -> Result<String> {
+    let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
+    let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
+    let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
+    let path = format!("/mnt/apk/lib/{}/{}", abi, name);
+
+    let metadata = fs::metadata(&path)?;
+    if !metadata.is_file() {
+        bail!("{} is not a file", &path);
+    }
+
+    Ok(path)
+}
diff --git a/microdroid_manager/src/payload_config.rs b/microdroid_manager/src/payload_config.rs
deleted file mode 100644
index bac841a..0000000
--- a/microdroid_manager/src/payload_config.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2021, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! VM Payload Config
-
-use log::info;
-use serde::{Deserialize, Serialize};
-use std::io;
-use std::path::Path;
-use std::time::Duration;
-
-use crate::ioutil;
-
-const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
-
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub struct VmPayloadConfig {
-    #[serde(default)]
-    pub task: Option<Task>,
-}
-
-#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
-pub struct Task {
-    pub command: String,
-    #[serde(default)]
-    pub args: Vec<String>,
-}
-
-impl VmPayloadConfig {
-    pub fn load_from(path: &Path) -> io::Result<VmPayloadConfig> {
-        info!("loading config from {:?}...", path);
-        let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
-        Ok(serde_json::from_reader(file)?)
-    }
-}