Add noop implementation of the IVmCapabilitiesService

This implementation will be used in cuttlefish for mixed device testing.

Unfortunately, cuttlefish can't use the default implementation, since
it neither doesn't support pVMs not runs pkvm as hypervisor.

Additionally, this patch adds a simple VTS test to ensure that HAL was
setup correctly.

Bug: 360102915
Test: atest VtsVmCapabilitiesServiceTest
Test: presubmit
Test: manually add noop service to husky
Change-Id: I03ed5b10063ba942704ae5599435a2378a4df63d
diff --git a/virtualization/capabilities_service/README.md b/virtualization/capabilities_service/README.md
new file mode 100644
index 0000000..7dc141e
--- /dev/null
+++ b/virtualization/capabilities_service/README.md
@@ -0,0 +1,9 @@
+The IVmCapabilitiesService HAL is used in a flow to grant a pVM a capability to
+issue vendor-specific SMCs. For more information see: TODO(ioffe): link the docs
+
+Here is a brief overview of the subdirectories structure:
+
+* default/ - a reference implementation of the HAL that partners can integrate
+    in their products.
+* noop/ - a no-op implementation is used in cuttlefish for mixed build testing.
+* vts/ - VTS tests for this HAL.
diff --git a/virtualization/capabilities_service/noop/Android.bp b/virtualization/capabilities_service/noop/Android.bp
new file mode 100644
index 0000000..37dc1a7
--- /dev/null
+++ b/virtualization/capabilities_service/noop/Android.bp
@@ -0,0 +1,34 @@
+package {
+    default_team: "trendy_team_virtualization",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_binary {
+    name: "android.hardware.virtualization.capabilities.capabilities_service-noop",
+    relative_install_path: "hw",
+    vendor: true,
+    installable: true,
+    prefer_rlib: true,
+    rustlibs: [
+        "android.hardware.virtualization.capabilities.capabilities_service-V1-rust",
+        "libanyhow",
+        "libandroid_logger",
+        "libbinder_rs",
+        "liblog_rust",
+    ],
+    srcs: [
+        "src/main.rs",
+    ],
+    init_rc: [
+        "android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.rc",
+    ],
+    vintf_fragment_modules: [
+        "android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml",
+    ],
+}
+
+vintf_fragment {
+    name: "android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml",
+    src: "android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml",
+    vendor: true,
+}
diff --git a/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.rc b/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.rc
new file mode 100644
index 0000000..58c554a
--- /dev/null
+++ b/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.rc
@@ -0,0 +1,6 @@
+service vendor.vm_capabilities_service /vendor/bin/hw/android.hardware.virtualization.capabilities.capabilities_service-noop
+  interface aidl android.hardware.virtualization.capabilities.IVmCapabilitiesService/noop
+  class hal
+  disabled
+  user nobody
+  group nobody
diff --git a/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml b/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml
new file mode 100644
index 0000000..8ecb9e5
--- /dev/null
+++ b/virtualization/capabilities_service/noop/android.hardware.virtualization.capabilities.no_op_vm_capabilities_service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.virtualization.capabilities</name>
+        <version>1</version>
+        <interface>
+            <name>IVmCapabilitiesService</name>
+            <instance>noop</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/virtualization/capabilities_service/noop/src/aidl.rs b/virtualization/capabilities_service/noop/src/aidl.rs
new file mode 100644
index 0000000..8d85e49
--- /dev/null
+++ b/virtualization/capabilities_service/noop/src/aidl.rs
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+//! No-op implementation of the IVmCapabilitiesService AIDL interface.
+
+use android_hardware_virtualization_capabilities_capabilities_service::aidl::android::hardware::virtualization::capabilities::IVmCapabilitiesService::IVmCapabilitiesService;
+use binder::{Interface, ParcelFileDescriptor};
+use log::info;
+
+/// No-op implementation of IVmCapabilitiesService
+pub struct NoOpVmCapabilitiesService {}
+
+impl NoOpVmCapabilitiesService {
+    pub fn init() -> NoOpVmCapabilitiesService {
+        let service = NoOpVmCapabilitiesService {};
+        service
+    }
+}
+
+impl Interface for NoOpVmCapabilitiesService {}
+
+impl IVmCapabilitiesService for NoOpVmCapabilitiesService {
+
+    fn grantAccessToVendorTeeServices(&self,
+        vm_fd: &ParcelFileDescriptor, tee_services: &[String]) -> binder::Result<()> {
+        info!("received {vm_fd:?} {tee_services:?}");
+        Ok(())
+    }
+}
diff --git a/virtualization/capabilities_service/noop/src/main.rs b/virtualization/capabilities_service/noop/src/main.rs
new file mode 100644
index 0000000..9440996
--- /dev/null
+++ b/virtualization/capabilities_service/noop/src/main.rs
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 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.
+ */
+
+//! No-op implementation of the IVmCapabilitiesService.
+
+mod aidl;
+
+use crate::aidl::NoOpVmCapabilitiesService;
+use anyhow::{bail, Context, Result};
+use log::{error, info, LevelFilter};
+use binder::{register_lazy_service, BinderFeatures, ProcessState};
+use android_hardware_virtualization_capabilities_capabilities_service::aidl::android::hardware::virtualization::capabilities::IVmCapabilitiesService::BnVmCapabilitiesService;
+
+const SERVICE_NAME: &str = "android.hardware.virtualization.capabilities.IVmCapabilitiesService/noop";
+
+fn try_main() -> Result<()> {
+    // Initialize Android logging.
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag("NoOpIVmCapabilitiesService")
+            .with_max_level(LevelFilter::Info)
+            .with_log_buffer(android_logger::LogId::System),
+    );
+
+    ProcessState::start_thread_pool();
+    let service_impl = NoOpVmCapabilitiesService::init();
+    let service = BnVmCapabilitiesService::new_binder(service_impl, BinderFeatures::default());
+    register_lazy_service(SERVICE_NAME, service.as_binder())
+        .with_context(|| format!("failed to register {SERVICE_NAME}"))?;
+    info!("Registered Binder service, joining threadpool.");
+    ProcessState::join_thread_pool();
+    bail!("thread pool unexpectedly ended");
+}
+
+fn main() {
+    if let Err(e) = try_main() {
+        error!("failed with {e:?}");
+        std::process::exit(1);
+    }
+}
diff --git a/virtualization/capabilities_service/vts/Android.bp b/virtualization/capabilities_service/vts/Android.bp
new file mode 100644
index 0000000..1aa21c9
--- /dev/null
+++ b/virtualization/capabilities_service/vts/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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 {
+    default_team: "trendy_team_virtualization",
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_test {
+    name: "VtsVmCapabilitiesServiceTest",
+    srcs: ["tests.rs"],
+    defaults: [
+        "rdroidtest.defaults",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    test_config: "AndroidTest.xml",
+    rustlibs: [
+        "android.hardware.virtualization.capabilities.capabilities_service-V1-rust",
+        "libbinder_rs",
+    ],
+    require_root: true,
+}
diff --git a/virtualization/capabilities_service/vts/AndroidTest.xml b/virtualization/capabilities_service/vts/AndroidTest.xml
new file mode 100644
index 0000000..2c3dadb
--- /dev/null
+++ b/virtualization/capabilities_service/vts/AndroidTest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 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.
+-->
+<configuration description="Config for VmCapabilitiesService VTS tests.">
+  <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
+  <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+    <option name="push-file" key="VtsVmCapabilitiesServiceTest"
+      value="/data/local/tmp/VtsVmCapabilitiesServiceTest" />
+  </target_preparer>
+
+  <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+    <option name="test-device-path" value="/data/local/tmp" />
+    <option name="module-name" value="VtsVmCapabilitiesServiceTest" />
+  </test>
+</configuration>
diff --git a/virtualization/capabilities_service/vts/tests.rs b/virtualization/capabilities_service/vts/tests.rs
new file mode 100644
index 0000000..9f0b7fd
--- /dev/null
+++ b/virtualization/capabilities_service/vts/tests.rs
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2025 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_hardware_virtualization_capabilities_capabilities_service::aidl::android::hardware::virtualization::capabilities::IVmCapabilitiesService::IVmCapabilitiesService;
+use rdroidtest::rdroidtest;
+use std::fs::File;
+
+const VM_CAPABILITIES_SERVICE: &str =
+    "android.hardware.virtualization.capabilities.IVmCapabilitiesService";
+
+/// Returns all available instances of VmCapabilitiesService.
+/// Note: it actually returns a pair of (<instance_name>, <instance_name)). This is a requirement
+/// of the rdroidtest framework for parameterized tests. See
+/// platform_testing/libraries/rdroidtest/README.md for more information.
+fn get_instances() -> Vec<(String, String)> {
+    binder::get_declared_instances(VM_CAPABILITIES_SERVICE)
+        .unwrap_or_default()
+        .into_iter()
+        .map(|v| (v.clone(), v))
+        .collect()
+}
+
+fn connect(instance: &str) -> binder::Strong<dyn IVmCapabilitiesService> {
+    let name = format!("{VM_CAPABILITIES_SERVICE}/{instance}");
+    binder::wait_for_interface(&name).unwrap()
+}
+
+/// A very basic test that simply connects to the service and send bogus data.
+#[rdroidtest(get_instances())]
+fn connect_to_service(instance: String) {
+    let service = connect(&instance);
+    let dev_null = File::open("/dev/null").expect("failed to open /dev/null");
+    let fd = binder::ParcelFileDescriptor::new(dev_null);
+    // In this test we don't care what service returns.
+    let _ = service.grantAccessToVendorTeeServices(&fd, &[]);
+}
+
+rdroidtest::test_main!();