• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "VirtualMachine"
18 
19 #include <aidl/android/system/virtualizationservice/IVirtualMachine.h>
20 #include <android-base/scopeguard.h>
21 #include <android-base/strings.h>
22 #include <android/binder_auto_utils.h>
23 #include <android/binder_ibinder_jni.h>
24 #include <fcntl.h>
25 #include <jni.h>
26 #include <log/log.h>
27 #include <nativehelper/JNIHelp.h>
28 #include <nativehelper/JNIPlatformHelp.h>
29 #include <nativehelper/ScopedLocalRef.h>
30 #include <pty.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <termios.h>
34 #include <unistd.h>
35 
36 #include <binder_rpc_unstable.hpp>
37 #include <string>
38 #include <tuple>
39 
40 #include "common.h"
41 
42 namespace {
43 
throwIOException(JNIEnv * env,const std::string & msg)44 void throwIOException(JNIEnv *env, const std::string &msg) {
45     jniThrowException(env, "java/io/IOException", msg.c_str());
46 }
47 
48 } // namespace
49 
50 extern "C" JNIEXPORT jobject JNICALL
Java_android_system_virtualmachine_VirtualMachine_nativeBinderFromPreconnectedClient(JNIEnv * env,jclass clazz,jobject provider)51 Java_android_system_virtualmachine_VirtualMachine_nativeBinderFromPreconnectedClient(
52         [[maybe_unused]] JNIEnv *env, [[maybe_unused]] jclass clazz, jobject provider) {
53     ScopedLocalRef<jclass> callback_class(env, env->GetObjectClass(provider));
54     jmethodID mid = env->GetMethodID(callback_class.get(), "connect", "()I");
55     LOG_ALWAYS_FATAL_IF(mid == nullptr, "Could not find method");
56 
57     // TODO(b/398890208): make this memory owned by the connection
58     struct State {
59         JNIEnv *mEnv;
60         jobject mProvider;
61         jmethodID mMid;
62     };
63 
64     auto state = std::make_unique<State>(env, provider, mid);
65 
66     using RequestFun = int (*)(void *);
67     RequestFun requestFunc = [](void *param) -> int {
68         State *state = static_cast<State *>(param);
69         int ownedFd = state->mEnv->CallIntMethod(state->mProvider, state->mMid);
70         // FD is owned by PFD in Java layer, need to dupe it so that
71         // ARpcSession_setupPreconnectedClient can take ownership when it calls unique_fd internally
72         return fcntl(ownedFd, F_DUPFD_CLOEXEC, 0);
73     };
74 
75     auto paramDeleteFunc = [](void *param) { delete static_cast<State *>(param); };
76 
77     RpcSessionHandle session;
78     // We need a thread pool to be able to support linkToDeath, or callbacks
79     // (b/268335700). These threads are currently created eagerly, so we don't
80     // want too many. The number 1 is chosen after some discussion, and to match
81     // the server-side default (mMaxThreads on RpcServer).
82     ARpcSession_setMaxIncomingThreads(session.get(), 1);
83     auto client = ARpcSession_setupPreconnectedClient(session.get(), requestFunc, state.release(),
84                                                       paramDeleteFunc);
85     return AIBinder_toJavaBinder(env, client);
86 }
87 
88 extern "C" JNIEXPORT void JNICALL
Java_android_system_virtualmachine_VirtualMachine_nativeOpenPtyRawNonblock(JNIEnv * env,jclass clazz,jobject resultCallback)89 Java_android_system_virtualmachine_VirtualMachine_nativeOpenPtyRawNonblock(
90         JNIEnv *env, [[maybe_unused]] jclass clazz, jobject resultCallback) {
91     int pm, ps;
92     // man openpty says: "Nobody knows how much space should be reserved for name."
93     // but on modern Linux the format of the pts name is always `/dev/pts/[0-9]+`
94     // Realistically speaking, a buffer of 32 bytes leaves us with 22 digits for the pts number,
95     // which should be more than enough.
96     // NOTE: bionic implements openpty() with internal name buffer of size 32, musl 20.
97     char name[32];
98     if (openpty(&pm, &ps, name, nullptr, nullptr)) {
99         throwIOException(env, "openpty(): " + android::base::ErrnoNumberAsString(errno));
100         return;
101     }
102     fcntl(pm, F_SETFD, FD_CLOEXEC);
103     fcntl(ps, F_SETFD, FD_CLOEXEC);
104     name[sizeof(name) - 1] = '\0';
105     // Set world RW so adb shell can talk to the pts.
106     chmod(name, 0666);
107 
108     if (int flags = fcntl(pm, F_GETFL, 0); flags < 0) {
109         throwIOException(env, "fcntl(F_GETFL): " + android::base::ErrnoNumberAsString(errno));
110         return;
111     } else if (fcntl(pm, F_SETFL, flags | O_NONBLOCK) < 0) {
112         throwIOException(env, "fcntl(F_SETFL): " + android::base::ErrnoNumberAsString(errno));
113         return;
114     }
115 
116     android::base::ScopeGuard cleanup_handler([=] {
117         close(ps);
118         close(pm);
119     });
120 
121     struct termios tio;
122     if (tcgetattr(pm, &tio)) {
123         throwIOException(env, "tcgetattr(): " + android::base::ErrnoNumberAsString(errno));
124         return;
125     }
126     cfmakeraw(&tio);
127     if (tcsetattr(pm, TCSANOW, &tio)) {
128         throwIOException(env, "tcsetattr(): " + android::base::ErrnoNumberAsString(errno));
129         return;
130     }
131 
132     jobject mfd = jniCreateFileDescriptor(env, pm);
133     if (mfd == nullptr) {
134         return;
135     }
136     jobject sfd = jniCreateFileDescriptor(env, ps);
137     if (sfd == nullptr) {
138         return;
139     }
140     size_t len = strlen(name);
141     ScopedLocalRef<jbyteArray> ptsName(env, env->NewByteArray(len));
142     if (ptsName.get() != nullptr) {
143         env->SetByteArrayRegion(ptsName.get(), 0, len, (jbyte *)name);
144     }
145     ScopedLocalRef<jclass> callback_class(env, env->GetObjectClass(resultCallback));
146     jmethodID mid = env->GetMethodID(callback_class.get(), "apply",
147                                      "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;[B)V");
148     if (mid == nullptr) {
149         return;
150     }
151 
152     env->CallVoidMethod(resultCallback, mid, mfd, sfd, ptsName.get());
153     // FD ownership is transferred to the callback, reset the auto-close hander.
154     cleanup_handler.Disable();
155 }
156