Run odrefresh in composd

This is far from finished, but it is at least started. This currently
fails when pvm_exec tries to create a vsock; instead it will need to
request one from composd.

Test: adb shell apex/com.android.compos/bin/composd_cmd
Bug: 186126194
Change-Id: Ic193ddd3835be3daf70b15e78c56c0ccb98e7a1f
diff --git a/compos/composd/Android.bp b/compos/composd/Android.bp
index 007eda9..5c968b8 100644
--- a/compos/composd/Android.bp
+++ b/compos/composd/Android.bp
@@ -6,6 +6,7 @@
     name: "composd",
     srcs: ["src/composd_main.rs"],
     edition: "2018",
+    prefer_rlib: true,
     rustlibs: [
         "android.system.composd-rust",
         "compos_aidl_interface-rust",
@@ -13,9 +14,10 @@
         "libanyhow",
         "libbinder_rs",
         "libcompos_common",
+        "libnum_traits",
         "liblog_rust",
     ],
-    prefer_rlib: true,
+    proc_macros: ["libnum_derive"],
     apex_available: [
         "com.android.compos",
     ],
diff --git a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
index 0dd5b6f..9240bc6 100644
--- a/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
+++ b/compos/composd/aidl/android/system/composd/IIsolatedCompilationService.aidl
@@ -16,6 +16,6 @@
 package android.system.composd;
 
 interface IIsolatedCompilationService {
-    // TODO: Add real methods
-    void doSomething();
+    /// Run "odrefresh --force-compile" in CompOS
+    void runForcedCompile();
 }
diff --git a/compos/composd/src/compos_instance.rs b/compos/composd/src/compos_instance.rs
new file mode 100644
index 0000000..e30a8b3
--- /dev/null
+++ b/compos/composd/src/compos_instance.rs
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//! Starts and manages instances of the CompOS VM.
+
+use anyhow::{Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::binder::Strong;
+use compos_common::compos_client::VmInstance;
+use compos_common::{COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE};
+use std::fs;
+use std::path::PathBuf;
+
+#[allow(dead_code)]
+pub struct CompOsInstance {
+    instance: VmInstance,
+    service: Strong<dyn ICompOsService>,
+}
+
+impl CompOsInstance {
+    pub fn start_current_instance() -> Result<CompOsInstance> {
+        let instance_image: PathBuf =
+            [COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE].iter().collect();
+
+        let instance = VmInstance::start(&instance_image).context("Starting VM")?;
+        let service = instance.get_service().context("Connecting to CompOS")?;
+
+        let key_blob: PathBuf =
+            [COMPOS_DATA_ROOT, CURRENT_DIR, PRIVATE_KEY_BLOB_FILE].iter().collect();
+        let key_blob = fs::read(key_blob).context("Reading private key")?;
+        service.initializeSigningKey(&key_blob).context("Loading key")?;
+
+        Ok(CompOsInstance { instance, service })
+    }
+
+    pub fn cid(&self) -> i32 {
+        self.instance.cid()
+    }
+}
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index f674448..33da889 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -18,6 +18,8 @@
 //! responsible for managing the lifecycle of the CompOS VM instances, providing key management for
 //! them, and orchestrating trusted compilation.
 
+mod compos_instance;
+mod odrefresh;
 mod service;
 
 use android_system_composd::binder::{register_lazy_service, ProcessState};
@@ -29,6 +31,8 @@
         android_logger::Config::default().with_tag("composd").with_min_level(log::Level::Info),
     );
 
+    ProcessState::start_thread_pool();
+
     let service = service::new_binder();
     register_lazy_service("android.system.composd", service.as_binder())
         .context("Registering service")?;
diff --git a/compos/composd/src/odrefresh.rs b/compos/composd/src/odrefresh.rs
new file mode 100644
index 0000000..c0042f0
--- /dev/null
+++ b/compos/composd/src/odrefresh.rs
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//! Handle the details of executing odrefresh to generate compiled artifacts.
+
+use anyhow::{bail, Context, Result};
+use num_derive::FromPrimitive;
+use num_traits::FromPrimitive;
+use std::process::Command;
+
+// TODO: What if this changes?
+const EX_MAX: i32 = 78;
+const ODREFRESH_BIN: &str = "/apex/com.android.art/bin/odrefresh";
+
+#[derive(Debug, PartialEq, Eq, FromPrimitive)]
+#[repr(i32)]
+pub enum ExitCode {
+    // Copied from art/odrefresh/include/odrefresh/odrefresh.h
+    Okay = 0i32,
+    CompilationRequired = EX_MAX + 1,
+    CompilationSuccess = EX_MAX + 2,
+    CompilationFailed = EX_MAX + 3,
+    CleanupFailed = EX_MAX + 4,
+}
+
+pub fn run_forced_compile(cid: i32) -> Result<ExitCode> {
+    // We don`t need to capture stdout/stderr - odrefresh writes to the log
+    let mut odrefresh = Command::new(ODREFRESH_BIN)
+        .arg(format!("--use-compilation-os={}", cid))
+        .arg("--force-compile")
+        .spawn()
+        .context("Running odrefresh")?;
+
+    // TODO: timeout?
+    let status = odrefresh.wait()?;
+
+    if let Some(exit_code) = status.code().and_then(FromPrimitive::from_i32) {
+        Ok(exit_code)
+    } else {
+        bail!("odrefresh exited with {}", status)
+    }
+}
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 8fe28ec..7fc9ab0 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -17,10 +17,15 @@
 //! Implementation of IIsolatedCompilationService, called from system server when compilation is
 //! desired.
 
+use crate::compos_instance::CompOsInstance;
+use crate::odrefresh;
 use android_system_composd::aidl::android::system::composd::IIsolatedCompilationService::{
     BnIsolatedCompilationService, IIsolatedCompilationService,
 };
-use android_system_composd::binder::{self, BinderFeatures, Interface, Strong};
+use android_system_composd::binder::{self, BinderFeatures, Interface, Status, Strong};
+use anyhow::{bail, Context, Result};
+use log::{error, info};
+use std::ffi::CString;
 
 pub struct IsolatedCompilationService {}
 
@@ -29,12 +34,35 @@
     BnIsolatedCompilationService::new_binder(service, BinderFeatures::default())
 }
 
-impl IsolatedCompilationService {}
-
 impl Interface for IsolatedCompilationService {}
 
 impl IIsolatedCompilationService for IsolatedCompilationService {
-    fn doSomething(&self) -> binder::Result<()> {
+    fn runForcedCompile(&self) -> binder::Result<()> {
+        to_binder_result(self.do_run_forced_compile())
+    }
+}
+
+fn to_binder_result<T>(result: Result<T>) -> binder::Result<T> {
+    result.map_err(|e| {
+        error!("Returning binder error: {:#}", e);
+        Status::new_service_specific_error(-1, CString::new(format!("{:#}", e)).ok().as_deref())
+    })
+}
+
+impl IsolatedCompilationService {
+    fn do_run_forced_compile(&self) -> Result<()> {
+        info!("runForcedCompile");
+
+        // TODO: Create instance if need be, handle instance failure, prevent
+        // multiple instances running
+        let comp_os = CompOsInstance::start_current_instance().context("Starting CompOS")?;
+
+        let exit_code = odrefresh::run_forced_compile(comp_os.cid())?;
+
+        if exit_code != odrefresh::ExitCode::CompilationSuccess {
+            bail!("Unexpected odrefresh result: {:?}", exit_code);
+        }
+
         Ok(())
     }
 }