Add API to connect to VM's binder server
Each guest VM is supposed to open its binder servers in payload. After
payload is ready, the host app can use connectToVsockServer to establish
an RPC session and get IBinder object.
Bug: 195381416
Test: atest MicrodroidHostTestCases
Change-Id: I417c20d150b87b17b57a26038ac7d48044e712ed
diff --git a/apex/Android.bp b/apex/Android.bp
index 0e2d2d4..c568ae2 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -50,6 +50,9 @@
java_libs: [
"android.system.virtualmachine",
],
+ jni_libs: [
+ "libvirtualmachine_jni",
+ ],
apps: [
"android.system.virtualmachine.res",
],
diff --git a/javalib/jni/Android.bp b/javalib/jni/Android.bp
new file mode 100644
index 0000000..e141297
--- /dev/null
+++ b/javalib/jni/Android.bp
@@ -0,0 +1,12 @@
+cc_library_shared {
+ name: "libvirtualmachine_jni",
+ srcs: ["android_system_virtualmachine_VirtualMachine.cpp"],
+ apex_available: ["com.android.virt"],
+ shared_libs: [
+ "android.system.virtualizationservice-ndk",
+ "libbinder_ndk",
+ "libbinder_rpc_unstable",
+ "liblog",
+ "libnativehelper",
+ ],
+}
diff --git a/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
new file mode 100644
index 0000000..7234dad
--- /dev/null
+++ b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 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.
+ */
+
+#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>
+
+JNIEXPORT jobject JNICALL android_system_virtualmachine_VirtualMachine_connectToVsockServer(
+ JNIEnv* env, [[maybe_unused]] jclass clazz, jobject vmBinder, jint port) {
+ using aidl::android::system::virtualizationservice::IVirtualMachine;
+ using ndk::ScopedFileDescriptor;
+ using ndk::SpAIBinder;
+
+ auto vm = IVirtualMachine::fromBinder(SpAIBinder{AIBinder_fromJavaBinder(env, vmBinder)});
+
+ std::tuple args{env, vm.get(), port};
+ using Args = decltype(args);
+
+ auto requestFunc = [](void* param) {
+ auto [env, vm, port] = *static_cast<Args*>(param);
+
+ ScopedFileDescriptor fd;
+ if (auto status = vm->connectVsock(port, &fd); !status.isOk()) {
+ env->ThrowNew(env->FindClass("android/system/virtualmachine/VirtualMachineException"),
+ ("Failed to connect vsock: " + status.getDescription()).c_str());
+ return -1;
+ }
+
+ // take ownership
+ int ret = fd.get();
+ *fd.getR() = -1;
+
+ return ret;
+ };
+
+ return AIBinder_toJavaBinder(env, RpcPreconnectedClient(requestFunc, &args));
+}
+
+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/VirtualMachine");
+ if (c == nullptr) {
+ ALOGE("%s: Failed to find class android.system.virtualmachine.VirtualMachine",
+ __FUNCTION__);
+ return JNI_ERR;
+ }
+
+ // Register your class' native methods.
+ static const JNINativeMethod methods[] = {
+ {"nativeConnectToVsockServer", "(Landroid/os/IBinder;I)Landroid/os/IBinder;",
+ reinterpret_cast<void*>(
+ android_system_virtualmachine_VirtualMachine_connectToVsockServer)},
+ };
+ 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/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index f4ac467..c4c9383 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -41,6 +41,9 @@
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.util.Optional;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
/**
* A handle to the virtual machine. The virtual machine is local to the app which created the
@@ -109,6 +112,12 @@
private @Nullable ParcelFileDescriptor mConsoleReader;
private @Nullable ParcelFileDescriptor mConsoleWriter;
+ private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
+
+ static {
+ System.loadLibrary("virtualmachine_jni");
+ }
+
private VirtualMachine(
@NonNull Context context, @NonNull String name, @NonNull VirtualMachineConfig config) {
mPackageName = context.getPackageName();
@@ -430,6 +439,25 @@
return oldConfig;
}
+ private static native IBinder nativeConnectToVsockServer(IBinder vmBinder, int port);
+
+ /**
+ * Connects to a VM's RPC server via vsock, and returns a root IBinder object. Guest VMs are
+ * expected to set up vsock servers in their payload. After the host app receives onPayloadReady
+ * callback, the host app can use this method to establish an RPC session to the guest VMs.
+ *
+ * <p>If the connection succeeds, the root IBinder object will be returned via {@link
+ * VirtualMachineCallback.onVsockServerReady()}. If the connection fails, {@link
+ * VirtualMachineCallback.onVsockServerConnectionFailed()} will be called.
+ */
+ public Future<IBinder> connectToVsockServer(int port) throws VirtualMachineException {
+ if (getStatus() != Status.RUNNING) {
+ throw new VirtualMachineException("VM is not running");
+ }
+ return mExecutorService.submit(
+ () -> nativeConnectToVsockServer(mVirtualMachine.asBinder(), port));
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index 8096cf7..571cc5d 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -18,6 +18,7 @@
},
ndk: {
apex_available: [
+ "com.android.virt",
"com.android.compos",
],
},