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);
+    }
+}