Create IVirtualizationServiceInternal

Add a new AIDL interface for communication between per-app VS and
a singleton global VS. The global VS will remain in charge of allocating
globally-unique resources, such as the CID, and running singleton
servers, like tombstone receiver. The interface will never be exposed
outside of the split VS instances.

For now, create IVirtualizationServiceInternal and start a local
instance as part of the still unified VS process. This way we can start
moving functionality around before the final split.

As a first step, move CID allocation and tombstone server to the
internal service.

Bug: 245727626
Test: atest -p packages/modules/Virtualization:avf-presubmit
Change-Id: I43beac45bb6ed5b60182c62e8bb08d8d702192d0
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index d6f4607..b767013 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -22,6 +22,7 @@
     rustlibs: [
         "android.system.virtualizationcommon-rust",
         "android.system.virtualizationservice-rust",
+        "android.system.virtualizationservice_internal-rust",
         "android.system.virtualmachineservice-rust",
         "android.os.permissions_aidl-rust",
         "libandroid_logger",
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index da237f8..a0bbc00 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -34,6 +34,23 @@
 }
 
 aidl_interface {
+    name: "android.system.virtualizationservice_internal",
+    srcs: ["android/system/virtualizationservice_internal/**/*.aidl"],
+    unstable: true,
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "com.android.virt",
+            ],
+        },
+    },
+}
+
+aidl_interface {
     name: "android.system.virtualmachineservice",
     srcs: ["android/system/virtualmachineservice/**/*.aidl"],
     imports: ["android.system.virtualizationcommon"],
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IGlobalVmContext.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IGlobalVmContext.aidl
new file mode 100644
index 0000000..1a7aa4a
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IGlobalVmContext.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2022 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.
+ */
+package android.system.virtualizationservice_internal;
+
+interface IGlobalVmContext {
+    /** Get the CID allocated to the VM. */
+    int getCid();
+}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
new file mode 100644
index 0000000..851ddf4
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 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.
+ */
+package android.system.virtualizationservice_internal;
+
+import android.system.virtualizationservice_internal.IGlobalVmContext;
+
+interface IVirtualizationServiceInternal {
+    /**
+     * Allocates global context for a new VM.
+     *
+     * This allocates VM's globally unique resources such as the CID.
+     * The resources will not be recycled as long as there is a strong reference
+     * to the returned object.
+     */
+    IGlobalVmContext allocateGlobalVmContext();
+}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 30b89da..2ec39e8 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -36,6 +36,10 @@
     VirtualMachineRawConfig::VirtualMachineRawConfig,
     VirtualMachineState::VirtualMachineState,
 };
+use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
+    IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
+    IVirtualizationServiceInternal::{BnVirtualizationServiceInternal, IVirtualizationServiceInternal},
+};
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
         BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
         VM_TOMBSTONES_SERVICE_PORT,
@@ -94,10 +98,94 @@
 
 const MICRODROID_OS_NAME: &str = "microdroid";
 
-/// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
+/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
+/// singleton servers, like tombstone receiver.
 #[derive(Debug, Default)]
+pub struct VirtualizationServiceInternal {
+    state: Arc<Mutex<GlobalState>>,
+}
+
+impl VirtualizationServiceInternal {
+    pub fn init() -> VirtualizationServiceInternal {
+        let service = VirtualizationServiceInternal::default();
+
+        std::thread::spawn(|| {
+            if let Err(e) = handle_stream_connection_tombstoned() {
+                warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
+            }
+        });
+
+        service
+    }
+}
+
+impl Interface for VirtualizationServiceInternal {}
+
+impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
+    fn allocateGlobalVmContext(&self) -> binder::Result<Strong<dyn IGlobalVmContext>> {
+        let state = &mut *self.state.lock().unwrap();
+        let cid = state.allocate_cid().map_err(|e| {
+            Status::new_exception_str(ExceptionCode::ILLEGAL_STATE, Some(e.to_string()))
+        })?;
+        Ok(GlobalVmContext::create(cid))
+    }
+}
+
+/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
+/// of this struct.
+#[derive(Debug, Default)]
+struct GlobalState {}
+
+impl GlobalState {
+    /// Get the next available CID, or an error if we have run out. The last CID used is stored in
+    /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
+    /// Android is up.
+    fn allocate_cid(&mut self) -> Result<Cid> {
+        let cid = match system_properties::read(SYSPROP_LAST_CID)? {
+            Some(val) => match val.parse::<Cid>() {
+                Ok(num) => num.checked_add(1).ok_or_else(|| anyhow!("ran out of CIDs"))?,
+                Err(_) => {
+                    error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
+                    FIRST_GUEST_CID
+                }
+            },
+            None => FIRST_GUEST_CID,
+        };
+        system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
+        Ok(cid)
+    }
+}
+
+/// Implementation of the AIDL `IGlobalVmContext` interface.
+#[derive(Debug, Default)]
+struct GlobalVmContext {
+    /// The unique CID assigned to the VM for vsock communication.
+    cid: Cid,
+    /// Keeps our service process running as long as this VM instance exists.
+    #[allow(dead_code)]
+    lazy_service_guard: LazyServiceGuard,
+}
+
+impl GlobalVmContext {
+    fn create(cid: Cid) -> Strong<dyn IGlobalVmContext> {
+        let binder = GlobalVmContext { cid, ..Default::default() };
+        BnGlobalVmContext::new_binder(binder, BinderFeatures::default())
+    }
+}
+
+impl Interface for GlobalVmContext {}
+
+impl IGlobalVmContext for GlobalVmContext {
+    fn getCid(&self) -> binder::Result<i32> {
+        Ok(self.cid as i32)
+    }
+}
+
+/// Implementation of `IVirtualizationService`, the entry point of the AIDL service.
+#[derive(Debug)]
 pub struct VirtualizationService {
     state: Arc<Mutex<State>>,
+    global_service: Strong<dyn IVirtualizationServiceInternal>,
 }
 
 impl Interface for VirtualizationService {
@@ -299,13 +387,11 @@
 
 impl VirtualizationService {
     pub fn init() -> VirtualizationService {
-        let service = VirtualizationService::default();
+        let global_service = VirtualizationServiceInternal::init();
+        let global_service =
+            BnVirtualizationServiceInternal::new_binder(global_service, BinderFeatures::default());
 
-        std::thread::spawn(|| {
-            if let Err(e) = handle_stream_connection_tombstoned() {
-                warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
-            }
-        });
+        let service = VirtualizationService { global_service, state: Default::default() };
 
         // binder server for vm
         // reference to state (not the state itself) is copied
@@ -346,12 +432,14 @@
             check_use_custom_virtual_machine()?;
         }
 
+        let vm_context = self.global_service.allocateGlobalVmContext()?;
+        let cid = vm_context.getCid()? as Cid;
+
         let state = &mut *self.state.lock().unwrap();
         let console_fd = console_fd.map(clone_file).transpose()?;
         let log_fd = log_fd.map(clone_file).transpose()?;
         let requester_uid = ThreadState::get_calling_uid();
         let requester_debug_pid = ThreadState::get_calling_pid();
-        let cid = state.next_cid().or(Err(ExceptionCode::ILLEGAL_STATE))?;
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -468,14 +556,20 @@
             detect_hangup: is_app_config,
         };
         let instance = Arc::new(
-            VmInstance::new(crosvm_config, temporary_directory, requester_uid, requester_debug_pid)
-                .map_err(|e| {
-                    error!("Failed to create VM with config {:?}: {:?}", config, e);
-                    Status::new_service_specific_error_str(
-                        -1,
-                        Some(format!("Failed to create VM: {:?}", e)),
-                    )
-                })?,
+            VmInstance::new(
+                crosvm_config,
+                temporary_directory,
+                requester_uid,
+                requester_debug_pid,
+                vm_context,
+            )
+            .map_err(|e| {
+                error!("Failed to create VM with config {:?}: {:?}", config, e);
+                Status::new_service_specific_error_str(
+                    -1,
+                    Some(format!("Failed to create VM: {:?}", e)),
+                )
+            })?,
         );
         state.add_vm(Arc::downgrade(&instance));
         Ok(VirtualMachine::create(instance))
@@ -943,27 +1037,6 @@
         let vm = self.debug_held_vms.swap_remove(pos);
         Some(vm)
     }
-
-    /// Get the next available CID, or an error if we have run out. The last CID used is stored in
-    /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
-    /// Android is up.
-    fn next_cid(&mut self) -> Result<Cid> {
-        let next = if let Some(val) = system_properties::read(SYSPROP_LAST_CID)? {
-            if let Ok(num) = val.parse::<u32>() {
-                num.checked_add(1).ok_or_else(|| anyhow!("run out of CID"))?
-            } else {
-                error!("Invalid last CID {}. Using {}", &val, FIRST_GUEST_CID);
-                FIRST_GUEST_CID
-            }
-        } else {
-            // First VM since the boot
-            FIRST_GUEST_CID
-        };
-        // Persist the last value for next use
-        let str_val = format!("{}", next);
-        system_properties::write(SYSPROP_LAST_CID, &str_val)?;
-        Ok(next)
-    }
 }
 
 /// Gets the `VirtualMachineState` of the given `VmInstance`.
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index db6da43..68324c5 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -38,6 +38,7 @@
 use std::time::{Duration, SystemTime};
 use std::thread;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::DeathReason::DeathReason;
+use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IGlobalVmContext::IGlobalVmContext;
 use binder::Strong;
 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
 use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
@@ -202,6 +203,9 @@
 pub struct VmInstance {
     /// The current state of the VM.
     pub vm_state: Mutex<VmState>,
+    /// Handle to global resources allocated for this VM.
+    #[allow(dead_code)] // The handle is never read, we only need to hold it.
+    vm_context: Strong<dyn IGlobalVmContext>,
     /// The CID assigned to the VM for vsock communication.
     pub cid: Cid,
     /// The name of the VM.
@@ -234,6 +238,7 @@
         temporary_directory: PathBuf,
         requester_uid: u32,
         requester_debug_pid: i32,
+        vm_context: Strong<dyn IGlobalVmContext>,
     ) -> Result<VmInstance, Error> {
         validate_config(&config)?;
         let cid = config.cid;
@@ -241,6 +246,7 @@
         let protected = config.protected;
         Ok(VmInstance {
             vm_state: Mutex::new(VmState::NotStarted { config }),
+            vm_context,
             cid,
             name,
             protected,