Add com.android.virt.accessor_demo
This provides a reference IAccessor implementation.
Test: atest vm_accessor_test
Bug: 349578050
Change-Id: I956e8e7706c43c8e672ec669d4c8912f09635061
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 3651dfa..762c3bf 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -64,6 +64,9 @@
{
"name": "AvfRkpdAppGoogleIntegrationTests",
"keywords": ["internal"]
+ },
+ {
+ "name": "vm_accessor_test"
}
],
"ferrochrome-postsubmit": [
diff --git a/demo_accessor/README.md b/demo_accessor/README.md
new file mode 100644
index 0000000..a3959a5
--- /dev/null
+++ b/demo_accessor/README.md
@@ -0,0 +1,40 @@
+# Demo for serving a service in a VM
+
+You can implement a service in a VM, and let client in the Android can use it
+as if it's in the Android. To do so, implement IAccessor.
+
+IAccessor allows AIDL service in a VM can be accessed via servicemanager.
+To do so, VM owners should also provide IAccessor implementation. servicemanager
+will connect to the IAccessor and get the binder of the service in a VM with it.
+
+com.android.virt.accessor_demo apex contains the minimum setup for IAccessor as
+follows:
+ - accessor_demo: Sample implementation of IAccessor, which is expected to
+ launch VM and returns the Vsock connection of service in the VM.
+ - AccessorVmApp: Sample app that conatins VM payload. Provides the actual
+ implementation of service in a VM.
+
+## Build
+
+You need to do envsetup.sh
+```shell
+m com.android.virt.accessor_demo
+```
+
+## Install (requires userdebug build)
+
+For very first install,
+
+```shell
+adb remount -R || adb wait-for-device # Remount to push apex to /system_ext
+adb root && adb remount # Ensure it's rebooted.
+adb push $ANDROID_PRODUCT_OUT/system_ext/com.android.virt.accessor_demo.apex /system_ext/apex
+adb reboot && adb wait-for-device # Ensure that newly pushed apex at /system_ext is installed
+```
+
+Once it's installed, you can simply use `adb install` for update.
+
+```shell
+adb install $ANDROID_PRODUCT_OUT/system_ext/com.android.virt.accessor_demo.apex
+adb reboot
+```
diff --git a/demo_accessor/accessor/Android.bp b/demo_accessor/accessor/Android.bp
new file mode 100644
index 0000000..d9d1026
--- /dev/null
+++ b/demo_accessor/accessor/Android.bp
@@ -0,0 +1,31 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_binary {
+ name: "accessor_demo",
+ crate_name: "accessor_demo",
+ srcs: ["src/main.rs"],
+ edition: "2021",
+ prefer_rlib: true,
+ defaults: ["avf_build_flags_rust"], // for reading llvm_changes
+ apex_available: [
+ "com.android.virt.accessor_demo",
+ ],
+ rustlibs: [
+ "android.system.virtualizationservice-rust",
+ "android.os.accessor-rust",
+ "libanyhow",
+ "libandroid_logger",
+ "libbinder_rs",
+ "libenv_logger",
+ "libglob",
+ "libhypervisor_props",
+ "liblibc",
+ "liblog_rust",
+ "libmicrodroid_payload_config",
+ "librand",
+ "libvmconfig",
+ "libvmclient",
+ ],
+}
diff --git a/demo_accessor/accessor/src/accessor.rs b/demo_accessor/accessor/src/accessor.rs
new file mode 100644
index 0000000..6a9ced6
--- /dev/null
+++ b/demo_accessor/accessor/src/accessor.rs
@@ -0,0 +1,52 @@
+// 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.
+
+//! IAcessor implementation.
+//! TODO: Keep this in proper places, so other pVMs can use this.
+//! TODO: Allows to customize VMs for launching. (e.g. port, ...)
+
+use android_os_accessor::aidl::android::os::IAccessor::IAccessor;
+use binder::{self, Interface, ParcelFileDescriptor};
+use log::info;
+use std::time::Duration;
+use vmclient::VmInstance;
+
+// Note: Do not use LazyServiceGuard here, to make this service and VM are quit
+// when nobody references it.
+// TODO(b/353492849): Do not use IAccessor directly.
+#[derive(Debug)]
+pub struct Accessor {
+ // Note: we can't simply keep reference by specifying lifetime to Accessor,
+ // because 'trait Interface' requires 'static.
+ vm: VmInstance,
+ port: i32,
+}
+
+impl Accessor {
+ pub fn new(vm: VmInstance, port: i32) -> Self {
+ Self { vm, port }
+ }
+}
+
+impl Interface for Accessor {}
+
+impl IAccessor for Accessor {
+ fn addConnection(&self) -> binder::Result<ParcelFileDescriptor> {
+ self.vm.wait_until_ready(Duration::from_secs(10)).unwrap();
+
+ info!("VM is ready. Connecting to service via port {}", self.port);
+
+ self.vm.vm.connectVsock(self.port)
+ }
+}
diff --git a/demo_accessor/accessor/src/main.rs b/demo_accessor/accessor/src/main.rs
new file mode 100644
index 0000000..27ce415
--- /dev/null
+++ b/demo_accessor/accessor/src/main.rs
@@ -0,0 +1,55 @@
+// 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.
+
+//! Android VM control tool.
+
+mod accessor;
+mod run;
+
+use accessor::Accessor;
+use android_os_accessor::aidl::android::os::IAccessor::BnAccessor;
+use anyhow::Error;
+use anyhow::{anyhow, bail};
+use binder::{BinderFeatures, ProcessState};
+use log::info;
+use run::run_vm;
+
+// Private contract between IAccessor impl and VM service.
+const PORT: i32 = 5678;
+
+// MUST match with VINTF and init.rc
+// TODO(b/354632613): Get this from VINTF
+const SERVICE_NAME: &str = "android.os.IAccessor/IAccessorVmService/default";
+
+fn main() -> Result<(), Error> {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("accessor_demo")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+
+ let vm = run_vm()?;
+
+ // If you want to serve multiple services in a VM, then register Accessor impls multiple times.
+ let accessor = Accessor::new(vm, PORT);
+ let accessor_binder = BnAccessor::new_binder(accessor, BinderFeatures::default());
+ binder::register_lazy_service(SERVICE_NAME, accessor_binder.as_binder()).map_err(|e| {
+ anyhow!("Failed to register lazy service, service={SERVICE_NAME}, err={e:?}",)
+ })?;
+ info!("service {SERVICE_NAME} is registered as lazy service");
+
+ ProcessState::join_thread_pool();
+
+ bail!("Thread pool unexpectedly ended")
+}
diff --git a/demo_accessor/accessor/src/run.rs b/demo_accessor/accessor/src/run.rs
new file mode 100644
index 0000000..03aa80d
--- /dev/null
+++ b/demo_accessor/accessor/src/run.rs
@@ -0,0 +1,175 @@
+// 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.
+
+//! Command to run a VM.
+
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+ IVirtualizationService::IVirtualizationService,
+ PartitionType::PartitionType,
+ VirtualMachineAppConfig::{DebugLevel::DebugLevel, Payload::Payload, VirtualMachineAppConfig},
+ VirtualMachineConfig::VirtualMachineConfig,
+ VirtualMachinePayloadConfig::VirtualMachinePayloadConfig,
+};
+use anyhow::{bail, Context, Error};
+use binder::{ParcelFileDescriptor, Strong};
+use glob::glob;
+use log::{error, info};
+use rand::{distributions::Alphanumeric, Rng};
+use std::fs;
+use std::fs::File;
+use std::io;
+use std::os::unix::io::{AsRawFd, FromRawFd};
+use std::path::PathBuf;
+use vmclient::{ErrorCode, VmInstance};
+use vmconfig::open_parcel_file;
+
+// These are private contract between IAccessor impl and VM service.
+const PAYLOAD_BINARY_NAME: &str = "libaccessor_vm_payload.so";
+const VM_OS_NAME: &str = "microdroid";
+
+const INSTANCE_FILE_SIZE: u64 = 10 * 1024 * 1024;
+
+fn get_service() -> Result<Strong<dyn IVirtualizationService>, Error> {
+ let virtmgr =
+ vmclient::VirtualizationService::new().context("Failed to spawn VirtualizationService")?;
+ virtmgr.connect().context("Failed to connect to VirtualizationService")
+}
+
+fn find_vm_apk_path() -> Result<PathBuf, Error> {
+ const GLOB_PATTERN: &str = "/apex/com.android.virt.accessor_demo/app/**/AccessorVmApp*.apk";
+ let mut entries: Vec<PathBuf> =
+ glob(GLOB_PATTERN).context("failed to glob")?.filter_map(|e| e.ok()).collect();
+ if entries.len() > 1 {
+ bail!("Found more than one apk matching {}", GLOB_PATTERN);
+ }
+ if let Some(path) = entries.pop() {
+ info!("Found accessor apk at {path:?}");
+ Ok(path)
+ } else {
+ bail!("No apks match {}", GLOB_PATTERN)
+ }
+}
+
+fn create_work_dir() -> Result<PathBuf, Error> {
+ let s: String =
+ rand::thread_rng().sample_iter(&Alphanumeric).take(17).map(char::from).collect();
+ let work_dir = PathBuf::from("/data/local/tmp/microdroid").join(s);
+ info!("creating work dir {}", work_dir.display());
+ fs::create_dir_all(&work_dir).context("failed to mkdir")?;
+ Ok(work_dir)
+}
+
+/// Run a VM with Microdroid
+pub fn run_vm() -> Result<VmInstance, Error> {
+ let service = get_service()?;
+
+ let apk = File::open(find_vm_apk_path()?).context("Failed to open APK file")?;
+ let apk_fd = ParcelFileDescriptor::new(apk);
+
+ let work_dir = create_work_dir()?;
+ info!("work dir: {}", work_dir.display());
+
+ let idsig =
+ File::create_new(work_dir.join("apk.idsig")).context("Failed to create idsig file")?;
+ let idsig_fd = ParcelFileDescriptor::new(idsig);
+ service.createOrUpdateIdsigFile(&apk_fd, &idsig_fd)?;
+
+ let instance_img_path = work_dir.join("instance.img");
+ let instance_img =
+ File::create_new(&instance_img_path).context("Failed to create instance.img file")?;
+ service.initializeWritablePartition(
+ &ParcelFileDescriptor::new(instance_img),
+ INSTANCE_FILE_SIZE.try_into()?,
+ PartitionType::ANDROID_VM_INSTANCE,
+ )?;
+ info!("created instance image at: {instance_img_path:?}");
+
+ let instance_id = if cfg!(llpvm_changes) {
+ let id = service.allocateInstanceId().context("Failed to allocate instance_id")?;
+ fs::write(work_dir.join("instance_id"), id)?;
+ id
+ } else {
+ // if llpvm feature flag is disabled, instance_id is not used.
+ [0u8; 64]
+ };
+
+ let payload = Payload::PayloadConfig(VirtualMachinePayloadConfig {
+ payloadBinaryName: PAYLOAD_BINARY_NAME.to_owned(),
+ extraApks: Default::default(),
+ });
+
+ let vm_config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
+ name: String::from("AccessorVm"),
+ apk: apk_fd.into(),
+ idsig: idsig_fd.into(),
+ extraIdsigs: Default::default(),
+ instanceImage: open_parcel_file(&instance_img_path, true /* writable */)?.into(),
+ instanceId: instance_id,
+ payload,
+ osName: VM_OS_NAME.to_owned(),
+ debugLevel: DebugLevel::FULL,
+ ..Default::default()
+ });
+
+ info!("creating VM");
+ let vm = VmInstance::create(
+ service.as_ref(),
+ &vm_config,
+ Some(duplicate_fd(io::stdout())?), /* console_out */
+ None, /* console_in */
+ Some(duplicate_fd(io::stdout())?), /* log */
+ Some(Box::new(Callback {})),
+ )
+ .context("Failed to create VM")?;
+ vm.start().context("Failed to start VM")?;
+
+ info!("started IAccessor VM with CID {}", vm.cid());
+
+ Ok(vm)
+}
+
+struct Callback {}
+
+impl vmclient::VmCallback for Callback {
+ fn on_payload_started(&self, _cid: i32) {
+ info!("payload started");
+ }
+
+ fn on_payload_ready(&self, _cid: i32) {
+ info!("payload is ready");
+ }
+
+ fn on_payload_finished(&self, _cid: i32, exit_code: i32) {
+ info!("payload finished with exit code {}", exit_code);
+ }
+
+ fn on_error(&self, _cid: i32, error_code: ErrorCode, message: &str) {
+ error!("VM encountered an error: code={:?}, message={}", error_code, message);
+ }
+}
+
+/// Safely duplicate the file descriptor.
+fn duplicate_fd<T: AsRawFd>(file: T) -> io::Result<File> {
+ let fd = file.as_raw_fd();
+ // SAFETY: This just duplicates a file descriptor which we know to be valid, and we check for an
+ // an error.
+ let dup_fd = unsafe { libc::dup(fd) };
+ if dup_fd < 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ // SAFETY: We have just duplicated the file descriptor so we own it, and `from_raw_fd` takes
+ // ownership of it.
+ Ok(unsafe { File::from_raw_fd(dup_fd) })
+ }
+}
diff --git a/demo_accessor/accessor_vm/Android.bp b/demo_accessor/accessor_vm/Android.bp
new file mode 100644
index 0000000..bbd15bd
--- /dev/null
+++ b/demo_accessor/accessor_vm/Android.bp
@@ -0,0 +1,30 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_app {
+ name: "AccessorVmApp",
+ installable: true,
+ jni_libs: ["libaccessor_vm_payload"],
+ jni_uses_platform_apis: true,
+ use_embedded_native_libs: true,
+ sdk_version: "system_current",
+ compile_multilib: "first",
+ apex_available: ["com.android.virt.accessor_demo"],
+}
+
+rust_ffi {
+ name: "libaccessor_vm_payload",
+ crate_name: "accessor_vm_payload",
+ defaults: ["avf_build_flags_rust"],
+ srcs: ["src/main.rs"],
+ prefer_rlib: true,
+ rustlibs: [
+ "com.android.virt.accessor_demo.vm_service-rust",
+ "libandroid_logger",
+ "libanyhow",
+ "liblog_rust",
+ "libvm_payload_rs",
+ ],
+ apex_available: ["com.android.virt.accessor_demo"],
+}
diff --git a/demo_accessor/accessor_vm/AndroidManifest.xml b/demo_accessor/accessor_vm/AndroidManifest.xml
new file mode 100644
index 0000000..429e08a
--- /dev/null
+++ b/demo_accessor/accessor_vm/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.virtualization.accessor_demo">
+ <application android:hasCode="false"/>
+</manifest>
diff --git a/demo_accessor/accessor_vm/assets/config.json b/demo_accessor/accessor_vm/assets/config.json
new file mode 100644
index 0000000..f921ec1
--- /dev/null
+++ b/demo_accessor/accessor_vm/assets/config.json
@@ -0,0 +1,10 @@
+{
+ "os": {
+ "name": "microdroid"
+ },
+ "task": {
+ "type": "microdroid_launcher",
+ "command": "accessor_vm.so"
+ },
+ "export_tombstones": true
+}
diff --git a/demo_accessor/accessor_vm/src/main.rs b/demo_accessor/accessor_vm/src/main.rs
new file mode 100644
index 0000000..bd83cc1
--- /dev/null
+++ b/demo_accessor/accessor_vm/src/main.rs
@@ -0,0 +1,64 @@
+// 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.
+
+//! VM with the simplest service for IAccessor demo
+
+use anyhow::Result;
+use com_android_virt_accessor_demo_vm_service::{
+ aidl::com::android::virt::accessor_demo::vm_service::IAccessorVmService::{
+ BnAccessorVmService, IAccessorVmService,
+ },
+ binder::{self, BinderFeatures, Interface, Strong},
+};
+use log::{error, info};
+
+// Private contract between IAccessor impl and VM service.
+const PORT: u32 = 5678;
+
+vm_payload::main!(main);
+
+// Entry point of the Service VM client.
+fn main() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag("accessor_vm")
+ .with_max_level(log::LevelFilter::Debug),
+ );
+ if let Err(e) = try_main() {
+ error!("failed with {:?}", e);
+ std::process::exit(1);
+ }
+}
+
+fn try_main() -> Result<()> {
+ info!("Starting stub payload for IAccessor demo");
+
+ vm_payload::run_single_vsock_service(AccessorVmService::new_binder(), PORT)
+}
+
+struct AccessorVmService {}
+
+impl Interface for AccessorVmService {}
+
+impl AccessorVmService {
+ fn new_binder() -> Strong<dyn IAccessorVmService> {
+ BnAccessorVmService::new_binder(AccessorVmService {}, BinderFeatures::default())
+ }
+}
+
+impl IAccessorVmService for AccessorVmService {
+ fn add(&self, a: i32, b: i32) -> binder::Result<i32> {
+ Ok(a + b)
+ }
+}
diff --git a/demo_accessor/aidl/Android.bp b/demo_accessor/aidl/Android.bp
new file mode 100644
index 0000000..0078608
--- /dev/null
+++ b/demo_accessor/aidl/Android.bp
@@ -0,0 +1,20 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+aidl_interface {
+ name: "com.android.virt.accessor_demo.vm_service",
+ srcs: ["**/*.aidl"],
+ unstable: true,
+ backend: {
+ java: {
+ gen_rpc: true,
+ },
+ rust: {
+ enabled: true,
+ apex_available: [
+ "com.android.virt.accessor_demo",
+ ],
+ },
+ },
+}
diff --git a/demo_accessor/aidl/com/android/virt/accessor_demo/vm_service/IAccessorVmService.aidl b/demo_accessor/aidl/com/android/virt/accessor_demo/vm_service/IAccessorVmService.aidl
new file mode 100644
index 0000000..29bf979
--- /dev/null
+++ b/demo_accessor/aidl/com/android/virt/accessor_demo/vm_service/IAccessorVmService.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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 com.android.virt.accessor_demo.vm_service;
+
+/** {@hide} */
+// TODO(b/349578050): Add more methods that take or return another binder.
+interface IAccessorVmService {
+ int add(int a, int b);
+}
diff --git a/demo_accessor/apex/Android.bp b/demo_accessor/apex/Android.bp
new file mode 100644
index 0000000..e954572
--- /dev/null
+++ b/demo_accessor/apex/Android.bp
@@ -0,0 +1,52 @@
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+// apex_test allows to skips apex_available checks for dependencies.
+// However, real apex should add itself to apex_available for all dependencies.
+apex_test {
+ name: "com.android.virt.accessor_demo",
+ manifest: "manifest.json",
+ file_contexts: "accessor_demo-file_contexts",
+
+ // You probably need your own key
+ key: "com.android.virt.key",
+
+ updatable: false,
+ future_updatable: false,
+ platform_apis: true,
+ system_ext_specific: true,
+
+ binaries: ["accessor_demo"],
+ apps: ["AccessorVmApp"],
+ prebuilts: [
+ "accessor_demo.init.rc",
+ "accessor_demo.xml",
+ ],
+}
+
+prebuilt_etc {
+ name: "accessor_demo.init.rc",
+ src: "accessor_demo.init.rc",
+ installable: false,
+}
+
+prebuilt_etc {
+ name: "accessor_demo.xml",
+ src: "accessor_demo.xml",
+ sub_dir: "vintf",
+ installable: false,
+}
diff --git a/demo_accessor/apex/accessor_demo-file_contexts b/demo_accessor/apex/accessor_demo-file_contexts
new file mode 100644
index 0000000..2007157
--- /dev/null
+++ b/demo_accessor/apex/accessor_demo-file_contexts
@@ -0,0 +1,3 @@
+# TODO: Give proper label
+(/.*)? u:object_r:system_file:s0
+/bin/accessor_demo u:object_r:virtualizationservice_exec:s0
diff --git a/demo_accessor/apex/accessor_demo.init.rc b/demo_accessor/apex/accessor_demo.init.rc
new file mode 100644
index 0000000..f3dfae9
--- /dev/null
+++ b/demo_accessor/apex/accessor_demo.init.rc
@@ -0,0 +1,20 @@
+# 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.
+
+# Register this as lazy service, so IAccessor and its VM is only running while binder is in use.
+service accessor_demo /apex/com.android.virt.accessor_demo/bin/accessor_demo
+ disabled
+ oneshot
+ # MUST match with VINTF and accessor/src/main.rs
+ interface aidl android.os.IAccessor/IAccessorVmService/default
diff --git a/demo_accessor/apex/accessor_demo.xml b/demo_accessor/apex/accessor_demo.xml
new file mode 100644
index 0000000..e9df3df
--- /dev/null
+++ b/demo_accessor/apex/accessor_demo.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest version="1.0" type="framework">
+ <hal format="aidl">
+ <name>com.android.virt.accessor_demo.vm_service</name>
+ <version>1</version>
+ <fqname>IAccessorVmService/default</fqname>
+ <!-- MUST match with init.rc and accessor/src/main.rs -->
+ <accessor>android.os.IAccessor/IAccessorVmService/default</accessor>
+ </hal>
+</manifest>
\ No newline at end of file
diff --git a/demo_accessor/apex/manifest.json b/demo_accessor/apex/manifest.json
new file mode 100644
index 0000000..a09523a
--- /dev/null
+++ b/demo_accessor/apex/manifest.json
@@ -0,0 +1,4 @@
+{
+ "name": "com.android.virt.accessor_demo",
+ "version": 1
+}
diff --git a/demo_accessor/test/Android.bp b/demo_accessor/test/Android.bp
new file mode 100644
index 0000000..71746c7
--- /dev/null
+++ b/demo_accessor/test/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * 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 {
+ default_team: "trendy_team_virtualization",
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_test {
+ name: "vm_accessor_test",
+ srcs: ["src/test.rs"],
+ defaults: [
+ "rdroidtest.defaults",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ test_config: "AndroidTest.xml",
+ rustlibs: [
+ "com.android.virt.accessor_demo.vm_service-rust",
+ "libbinder_rs",
+ "liblog_rust",
+ ],
+ data: [":com.android.virt.accessor_demo"],
+ compile_multilib: "first",
+}
diff --git a/demo_accessor/test/AndroidTest.xml b/demo_accessor/test/AndroidTest.xml
new file mode 100644
index 0000000..4cbf9ec
--- /dev/null
+++ b/demo_accessor/test/AndroidTest.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Accessor demo test">
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+
+ <!-- Install APEX begins -->
+ <!-- Step 0: adb reboot, so PushFilePreparer can remount system if needed -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ <option name="force-root" value="true"/>
+ </target_preparer>
+ <!-- Step 1: Push for the very first install. -->
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="abort-on-push-failure" value="true" />
+ <!-- Disclaimer: 'remount-system' remounts all partitions (adb remount),
+ but do so after checking the verity of /system partition.
+ This works for now, but may misbehave in the future. -->
+ <option name="remount-system" value="true" />
+ <option name="push-file" key="com.android.virt.accessor_demo.apex" value="/system_ext/apex/com.android.virt.accessor_demo.apex" />
+ </target_preparer>
+ <!-- Step 2: Reboot for pushed APEX to be installed. -->
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="abort-on-push-failure" value="true" />
+ <option name="push-file" key="vm_accessor_test" value="/data/local/tmp/vm_accessor_test" />
+ </target_preparer>
+
+ <!-- TODO(b/346763236): Remove this -->
+ <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.rust.RustBinaryTest" >
+ <option name="test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vm_accessor_test" />
+ </test>
+</configuration>
diff --git a/demo_accessor/test/src/test.rs b/demo_accessor/test/src/test.rs
new file mode 100644
index 0000000..d521acf
--- /dev/null
+++ b/demo_accessor/test/src/test.rs
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+//! Test end-to-end IAccessor implementation with accessor_demo.
+
+use com_android_virt_accessor_demo_vm_service::aidl::com::android::virt::accessor_demo::vm_service::IAccessorVmService::IAccessorVmService;
+use binder::{Strong, ProcessState};
+use rdroidtest::rdroidtest;
+
+const VM_SERVICE: &str = "com.android.virt.accessor_demo.vm_service.IAccessorVmService/default";
+
+fn init() {
+ ProcessState::set_thread_pool_max_thread_count(5);
+ ProcessState::start_thread_pool();
+}
+
+fn wait_for_interface() -> Strong<dyn IAccessorVmService> {
+ binder::wait_for_interface(VM_SERVICE).unwrap()
+}
+
+fn get_interface() -> Strong<dyn IAccessorVmService> {
+ binder::get_interface(VM_SERVICE).unwrap()
+}
+
+fn check_interface() -> Strong<dyn IAccessorVmService> {
+ binder::check_interface(VM_SERVICE).unwrap()
+}
+
+#[rdroidtest]
+fn test_wait_for_interface() {
+ init();
+
+ let service = wait_for_interface();
+ let sum = service.add(11, 12).unwrap();
+
+ assert_eq!(sum, 23);
+}
+
+#[rdroidtest]
+fn test_wait_for_interface_twice() {
+ init();
+
+ let service1 = wait_for_interface();
+ let service2 = wait_for_interface();
+
+ assert_eq!(service1.add(11, 12).unwrap(), 23);
+ assert_eq!(service2.add(11, 12).unwrap(), 23);
+}
+
+#[rdroidtest]
+fn test_wait_and_get_interface() {
+ init();
+
+ let service1 = wait_for_interface();
+ let service2 = get_interface();
+
+ assert_eq!(service1.add(11, 12).unwrap(), 23);
+ assert_eq!(service2.add(11, 12).unwrap(), 23);
+}
+
+#[rdroidtest]
+fn test_wait_and_check_interface() {
+ init();
+
+ let service1 = wait_for_interface();
+ let service2 = check_interface();
+
+ assert_eq!(service1.add(11, 12).unwrap(), 23);
+ assert_eq!(service2.add(11, 12).unwrap(), 23);
+}
+
+rdroidtest::test_main!();