javalib: Spawn child virtmgr and connect to it
Instead of connecting to the global virtualizationservice, make
VirtualMachine spawn a child virtmgr process and connect to it via
RpcBinder.
Bug: 245727626
Test: atest -p packages/modules/Virtualization:avf-presubmit
Change-Id: Ia79adbf7502c33a9a904363f1c75cfbfe8b6b4c7
diff --git a/apex/Android.bp b/apex/Android.bp
index d5f485b..dce8edd 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -57,6 +57,7 @@
"com.android.virt-bootclasspath-fragment",
],
jni_libs: [
+ "libvirtualizationservice_jni",
"libvirtualmachine_jni",
],
}
diff --git a/javalib/jni/Android.bp b/javalib/jni/Android.bp
index 2939db5..e82b2ce 100644
--- a/javalib/jni/Android.bp
+++ b/javalib/jni/Android.bp
@@ -3,11 +3,29 @@
}
cc_library_shared {
+ name: "libvirtualizationservice_jni",
+ srcs: [
+ "android_system_virtualmachine_VirtualizationService.cpp",
+ ],
+ apex_available: ["com.android.virt"],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "libbinder_rpc_unstable",
+ "liblog",
+ "libnativehelper",
+ ],
+}
+
+cc_library_shared {
name: "libvirtualmachine_jni",
- srcs: ["android_system_virtualmachine_VirtualMachine.cpp"],
+ srcs: [
+ "android_system_virtualmachine_VirtualMachine.cpp",
+ ],
apex_available: ["com.android.virt"],
shared_libs: [
"android.system.virtualizationservice-ndk",
+ "libbase",
"libbinder_ndk",
"libbinder_rpc_unstable",
"liblog",
diff --git a/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
index a949d46..3230af4 100644
--- a/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
+++ b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
@@ -16,16 +16,16 @@
#define LOG_TAG "VirtualMachine"
-#include <tuple>
-
-#include <log/log.h>
-
#include <aidl/android/system/virtualizationservice/IVirtualMachine.h>
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder_jni.h>
-#include <binder_rpc_unstable.hpp>
-
#include <jni.h>
+#include <log/log.h>
+
+#include <binder_rpc_unstable.hpp>
+#include <tuple>
+
+#include "common.h"
JNIEXPORT jobject JNICALL android_system_virtualmachine_VirtualMachine_connectToVsockServer(
JNIEnv* env, [[maybe_unused]] jclass clazz, jobject vmBinder, jint port) {
@@ -55,12 +55,9 @@
return ret;
};
- auto session = ARpcSession_new();
- auto client = ARpcSession_setupPreconnectedClient(session, requestFunc, &args);
- auto obj = AIBinder_toJavaBinder(env, client);
- // Free the NDK handle. The underlying RpcSession object remains alive.
- ARpcSession_free(session);
- return obj;
+ RpcSessionHandle session;
+ auto client = ARpcSession_setupPreconnectedClient(session.get(), requestFunc, &args);
+ return AIBinder_toJavaBinder(env, client);
}
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
diff --git a/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp b/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp
new file mode 100644
index 0000000..b9d2ca4
--- /dev/null
+++ b/javalib/jni/android_system_virtualmachine_VirtualizationService.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VirtualizationService"
+
+#include <android-base/unique_fd.h>
+#include <android/binder_ibinder_jni.h>
+#include <jni.h>
+#include <log/log.h>
+
+#include <string>
+
+#include "common.h"
+
+using namespace android::base;
+
+static constexpr const char VIRTMGR_PATH[] = "/apex/com.android.virt/bin/virtmgr";
+static constexpr size_t VIRTMGR_THREADS = 16;
+
+JNIEXPORT jint JNICALL android_system_virtualmachine_VirtualizationService_spawn(
+ JNIEnv* env, [[maybe_unused]] jclass clazz) {
+ unique_fd serverFd, clientFd;
+ if (!Socketpair(SOCK_STREAM, &serverFd, &clientFd)) {
+ env->ThrowNew(env->FindClass("android/system/virtualmachine/VirtualMachineException"),
+ ("Failed to create socketpair: " + std::string(strerror(errno))).c_str());
+ return -1;
+ }
+
+ unique_fd waitFd, readyFd;
+ if (!Pipe(&waitFd, &readyFd, 0)) {
+ env->ThrowNew(env->FindClass("android/system/virtualmachine/VirtualMachineException"),
+ ("Failed to create pipe: " + std::string(strerror(errno))).c_str());
+ return -1;
+ }
+
+ if (fork() == 0) {
+ // Close client's FDs.
+ clientFd.reset();
+ waitFd.reset();
+
+ auto strServerFd = std::to_string(serverFd.get());
+ auto strReadyFd = std::to_string(readyFd.get());
+
+ execl(VIRTMGR_PATH, VIRTMGR_PATH, "--rpc-server-fd", strServerFd.c_str(), "--ready-fd",
+ strReadyFd.c_str(), NULL);
+ }
+
+ // Close virtmgr's FDs.
+ serverFd.reset();
+ readyFd.reset();
+
+ // Wait for the server to signal its readiness by closing its end of the pipe.
+ char buf;
+ if (read(waitFd.get(), &buf, sizeof(buf)) < 0) {
+ env->ThrowNew(env->FindClass("android/system/virtualmachine/VirtualMachineException"),
+ "Failed to wait for VirtualizationService to be ready");
+ return -1;
+ }
+
+ return clientFd.release();
+}
+
+JNIEXPORT jobject JNICALL android_system_virtualmachine_VirtualizationService_connect(
+ JNIEnv* env, [[maybe_unused]] jobject obj, int clientFd) {
+ RpcSessionHandle session;
+ ARpcSession_setFileDescriptorTransportMode(session.get(),
+ ARpcSession_FileDescriptorTransportMode::Unix);
+ ARpcSession_setMaxIncomingThreads(session.get(), VIRTMGR_THREADS);
+ ARpcSession_setMaxOutgoingThreads(session.get(), VIRTMGR_THREADS);
+ // SAFETY - ARpcSession_setupUnixDomainBootstrapClient does not take ownership of clientFd.
+ auto client = ARpcSession_setupUnixDomainBootstrapClient(session.get(), clientFd);
+ return AIBinder_toJavaBinder(env, client);
+}
+
+JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
+ JNIEnv* env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("%s: Failed to get the environment", __FUNCTION__);
+ return JNI_ERR;
+ }
+
+ jclass c = env->FindClass("android/system/virtualmachine/VirtualizationService");
+ if (c == nullptr) {
+ ALOGE("%s: Failed to find class android.system.virtualmachine.VirtualizationService",
+ __FUNCTION__);
+ return JNI_ERR;
+ }
+
+ // Register your class' native methods.
+ static const JNINativeMethod methods[] = {
+ {"nativeSpawn", "()I",
+ reinterpret_cast<void*>(android_system_virtualmachine_VirtualizationService_spawn)},
+ {"nativeConnect", "(I)Landroid/os/IBinder;",
+ reinterpret_cast<void*>(android_system_virtualmachine_VirtualizationService_connect)},
+ };
+ int rc = env->RegisterNatives(c, methods, sizeof(methods) / sizeof(JNINativeMethod));
+ if (rc != JNI_OK) {
+ ALOGE("%s: Failed to register natives", __FUNCTION__);
+ return rc;
+ }
+
+ return JNI_VERSION_1_6;
+}
diff --git a/javalib/jni/common.h b/javalib/jni/common.h
new file mode 100644
index 0000000..c70ba76
--- /dev/null
+++ b/javalib/jni/common.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#include <binder_rpc_unstable.hpp>
+
+// Wrapper around ARpcSession handle that automatically frees the handle when
+// it goes out of scope.
+class RpcSessionHandle {
+public:
+ RpcSessionHandle() : mHandle(ARpcSession_new()) {}
+ ~RpcSessionHandle() { ARpcSession_free(mHandle); }
+
+ ARpcSession* get() { return mHandle; }
+
+private:
+ ARpcSession* mHandle;
+};
diff --git a/javalib/src/android/system/virtualmachine/VirtualizationService.java b/javalib/src/android/system/virtualmachine/VirtualizationService.java
new file mode 100644
index 0000000..78d0c9c
--- /dev/null
+++ b/javalib/src/android/system/virtualmachine/VirtualizationService.java
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.system.virtualmachine;
+
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.system.virtualizationservice.IVirtualizationService;
+
+/** A running instance of virtmgr that is hosting a VirtualizationService AIDL service. */
+class VirtualizationService {
+ static {
+ System.loadLibrary("virtualizationservice_jni");
+ }
+
+ /*
+ * Client FD for UDS connection to virtmgr's RpcBinder server. Closing it
+ * will make virtmgr shut down.
+ */
+ private ParcelFileDescriptor mClientFd;
+
+ private static native int nativeSpawn();
+
+ private native IBinder nativeConnect(int clientFd);
+
+ /*
+ * Spawns a new virtmgr subprocess that will host a VirtualizationService
+ * AIDL service.
+ */
+ public VirtualizationService() throws VirtualMachineException {
+ int clientFd = nativeSpawn();
+ if (clientFd < 0) {
+ throw new VirtualMachineException("Could not spawn VirtualizationService");
+ }
+ mClientFd = ParcelFileDescriptor.adoptFd(clientFd);
+ }
+
+ /* Connects to the VirtualizationService AIDL service. */
+ public IVirtualizationService connect() throws VirtualMachineException {
+ IBinder binder = nativeConnect(mClientFd.getFd());
+ if (binder == null) {
+ throw new VirtualMachineException("Could not connect to VirtualizationService");
+ }
+ return IVirtualizationService.Stub.asInterface(binder);
+ }
+}