Merge "Add a skeleton virtualizationmaintenance service" into main
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 843873b..38a9ec5 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -23,6 +23,7 @@
     rustlibs: [
         "android.hardware.security.rkp-V3-rust",
         "android.system.virtualizationcommon-rust",
+        "android.system.virtualizationmaintenance-rust",
         "android.system.virtualizationservice-rust",
         "android.system.virtualizationservice_internal-rust",
         "android.system.virtualmachineservice-rust",
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index 8ca375a..66de092 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -55,6 +55,23 @@
 }
 
 aidl_interface {
+    name: "android.system.virtualizationmaintenance",
+    srcs: ["android/system/virtualizationmaintenance/**/*.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: [
diff --git a/virtualizationservice/aidl/android/system/virtualizationmaintenance/IVirtualizationMaintenance.aidl b/virtualizationservice/aidl/android/system/virtualizationmaintenance/IVirtualizationMaintenance.aidl
new file mode 100644
index 0000000..161673a
--- /dev/null
+++ b/virtualizationservice/aidl/android/system/virtualizationmaintenance/IVirtualizationMaintenance.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2024 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.virtualizationmaintenance;
+
+interface IVirtualizationMaintenance {
+    void appRemoved(int userId, int appId);
+
+    void userRemoved(int userId);
+
+    // TODO: Something for daily reconciliation
+}
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index c7ae5f0..c7a33ba 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -59,7 +59,9 @@
     IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
     IVfioHandler::VfioDev::VfioDev,
     IVfioHandler::{BpVfioHandler, IVfioHandler},
-    IVirtualizationServiceInternal::IVirtualizationServiceInternal,
+    IVirtualizationServiceInternal::{
+        BnVirtualizationServiceInternal, IVirtualizationServiceInternal,
+    },
 };
 use virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
 use vsock::{VsockListener, VsockStream};
@@ -67,8 +69,6 @@
 /// The unique ID of a VM used (together with a port number) for vsock communication.
 pub type Cid = u32;
 
-pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
-
 /// Directory in which to write disk image files used while running VMs.
 pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
 
@@ -165,7 +165,7 @@
 }
 
 impl VirtualizationServiceInternal {
-    pub fn init() -> VirtualizationServiceInternal {
+    pub fn init() -> Strong<dyn IVirtualizationServiceInternal> {
         let service = VirtualizationServiceInternal::default();
 
         std::thread::spawn(|| {
@@ -174,7 +174,7 @@
             }
         });
 
-        service
+        BnVirtualizationServiceInternal::new_binder(service, BinderFeatures::default())
     }
 }
 
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index ad21e89..97bb38f 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -16,18 +16,15 @@
 
 mod aidl;
 mod atom;
+mod maintenance;
 mod remote_provisioning;
 mod rkpvm;
 
-use crate::aidl::{
-    remove_temporary_dir, BINDER_SERVICE_IDENTIFIER, TEMPORARY_DIRECTORY,
-    VirtualizationServiceInternal
-};
+use crate::aidl::{remove_temporary_dir, VirtualizationServiceInternal, TEMPORARY_DIRECTORY};
 use android_logger::{Config, FilterBuilder};
-use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::IVirtualizationServiceInternal::BnVirtualizationServiceInternal;
-use anyhow::Error;
-use binder::{register_lazy_service, BinderFeatures, ProcessState, ThreadState};
-use log::{info, LevelFilter};
+use anyhow::{bail, Context, Error, Result};
+use binder::{register_lazy_service, ProcessState, ThreadState};
+use log::{error, info, LevelFilter};
 use std::fs::{create_dir, read_dir};
 use std::os::unix::raw::{pid_t, uid_t};
 use std::path::Path;
@@ -35,6 +32,8 @@
 const LOG_TAG: &str = "VirtualizationService";
 pub(crate) const REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME: &str =
     "android.hardware.security.keymint.IRemotelyProvisionedComponent/avf";
+const INTERNAL_SERVICE_NAME: &str = "android.system.virtualizationservice";
+const MAINTENANCE_SERVICE_NAME: &str = "android.system.virtualizationmaintenance";
 
 fn get_calling_pid() -> pid_t {
     ThreadState::get_calling_pid()
@@ -45,6 +44,13 @@
 }
 
 fn main() {
+    if let Err(e) = try_main() {
+        error!("failed with {e:?}");
+        std::process::exit(1);
+    }
+}
+
+fn try_main() -> Result<()> {
     android_logger::init_once(
         Config::default()
             .with_tag(LOG_TAG)
@@ -57,31 +63,33 @@
             ),
     );
 
-    clear_temporary_files().expect("Failed to delete old temporary files");
+    clear_temporary_files().context("Failed to delete old temporary files")?;
 
     let common_dir_path = Path::new(TEMPORARY_DIRECTORY).join("common");
-    create_dir(common_dir_path).expect("Failed to create common directory");
+    create_dir(common_dir_path).context("Failed to create common directory")?;
 
     ProcessState::start_thread_pool();
-
-    let service = VirtualizationServiceInternal::init();
-    let service = BnVirtualizationServiceInternal::new_binder(service, BinderFeatures::default());
-    register_lazy_service(BINDER_SERVICE_IDENTIFIER, service.as_binder()).unwrap();
-    info!("Registered Binder service {}.", BINDER_SERVICE_IDENTIFIER);
+    register(INTERNAL_SERVICE_NAME, VirtualizationServiceInternal::init())?;
 
     if cfg!(remote_attestation) {
         // The IRemotelyProvisionedComponent service is only supposed to be triggered by rkpd for
         // RKP VM attestation.
-        let remote_provisioning_service = remote_provisioning::new_binder();
-        register_lazy_service(
-            REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
-            remote_provisioning_service.as_binder(),
-        )
-        .unwrap();
-        info!("Registered Binder service {}.", REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME);
+        register(REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME, remote_provisioning::new_binder())?;
+    }
+
+    if cfg!(llpvm_changes) {
+        register(MAINTENANCE_SERVICE_NAME, maintenance::new_binder())?;
     }
 
     ProcessState::join_thread_pool();
+    bail!("Thread pool unexpectedly ended");
+}
+
+fn register<T: binder::FromIBinder + ?Sized>(name: &str, service: binder::Strong<T>) -> Result<()> {
+    register_lazy_service(name, service.as_binder())
+        .with_context(|| format!("Failed to register {name}"))?;
+    info!("Registered Binder service {name}.");
+    Ok(())
 }
 
 /// Remove any files under `TEMPORARY_DIRECTORY`.
diff --git a/virtualizationservice/src/maintenance.rs b/virtualizationservice/src/maintenance.rs
new file mode 100644
index 0000000..191d39a
--- /dev/null
+++ b/virtualizationservice/src/maintenance.rs
@@ -0,0 +1,44 @@
+// Copyright 2024 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.
+
+use android_system_virtualizationmaintenance::aidl::android::system::virtualizationmaintenance;
+use anyhow::anyhow;
+use binder::{BinderFeatures, ExceptionCode, Interface, IntoBinderResult, Strong};
+use virtualizationmaintenance::IVirtualizationMaintenance::{
+    BnVirtualizationMaintenance, IVirtualizationMaintenance,
+};
+
+pub(crate) fn new_binder() -> Strong<dyn IVirtualizationMaintenance> {
+    BnVirtualizationMaintenance::new_binder(
+        VirtualizationMaintenanceService {},
+        BinderFeatures::default(),
+    )
+}
+
+pub struct VirtualizationMaintenanceService;
+
+impl Interface for VirtualizationMaintenanceService {}
+
+#[allow(non_snake_case)]
+impl IVirtualizationMaintenance for VirtualizationMaintenanceService {
+    fn appRemoved(&self, _user_id: i32, _app_id: i32) -> binder::Result<()> {
+        Err(anyhow!("appRemoved not supported"))
+            .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)
+    }
+
+    fn userRemoved(&self, _user_id: i32) -> binder::Result<()> {
+        Err(anyhow!("userRemoved not supported"))
+            .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION)
+    }
+}