1 /*
2 * Copyright (C) 2016 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 #include "ProcessPipe.h"
18 #include "renderControl_enc.h"
19
20 #include <qemu_pipe_bp.h>
21
22 #if PLATFORM_SDK_VERSION < 26
23 #include <cutils/log.h>
24 #else
25 #include <log/log.h>
26 #endif
27 #include <pthread.h>
28 #include <errno.h>
29
30 #ifdef __Fuchsia__
31 #include <fuchsia/hardware/goldfish/cpp/fidl.h>
32 #include <lib/zx/vmo.h>
33
34 #include "services/service_connector.h"
35
36 static QEMU_PIPE_HANDLE sProcDevice = 0;
37 #else // __Fuchsia__
38
39 #include "VirtioGpuPipeStream.h"
40 static VirtioGpuPipeStream* sVirtioGpuPipeStream = 0;
41
42 #endif // !__Fuchsia__
43
44 static QEMU_PIPE_HANDLE sProcPipe = 0;
45 static pthread_once_t sProcPipeOnce = PTHREAD_ONCE_INIT;
46 // sProcUID is a unique ID per process assigned by the host.
47 // It is different from getpid().
48 static uint64_t sProcUID = 0;
49 static volatile HostConnectionType sConnType = HOST_CONNECTION_VIRTIO_GPU_PIPE;
50
51 // processPipeInitOnce is used to generate a process unique ID (puid).
52 // processPipeInitOnce will only be called at most once per process.
53 // Use it with pthread_once for thread safety.
54 // The host associates resources with process unique ID (puid) for memory cleanup.
55 // It will fallback to the default path if the host does not support it.
56 // Processes are identified by acquiring a per-process 64bit unique ID from the
57 // host.
58 #ifdef __Fuchsia__
processPipeInitOnce()59 static void processPipeInitOnce() {
60 zx::channel channel(GetConnectToServiceFunction()(QEMU_PIPE_PATH));
61 if (!channel) {
62 ALOGE("%s: failed to open " QEMU_PIPE_PATH,
63 __FUNCTION__);
64 return;
65 }
66
67 fuchsia::hardware::goldfish::PipeDeviceSyncPtr device;
68 device.Bind(std::move(channel));
69
70 fuchsia::hardware::goldfish::PipeSyncPtr pipe;
71 device->OpenPipe(pipe.NewRequest());
72
73 zx_status_t status, status2 = ZX_OK;
74 zx::vmo vmo;
75 status = pipe->GetBuffer(&status2, &vmo);
76 if (status != ZX_OK || status2 != ZX_OK) {
77 ALOGE("%s: failed to get buffer: %d:%d", __FUNCTION__, status, status2);
78 return;
79 }
80
81 size_t len = strlen("pipe:GLProcessPipe");
82 status = vmo.write("pipe:GLProcessPipe", 0, len + 1);
83 if (status != ZX_OK) {
84 ALOGE("%s: failed write pipe name", __FUNCTION__);
85 return;
86 }
87 uint64_t actual;
88 status = pipe->Write(len + 1, 0, &status2, &actual);
89 if (status != ZX_OK || status2 != ZX_OK) {
90 ALOGD("%s: connecting to pipe service failed: %d:%d", __FUNCTION__,
91 status, status2);
92 return;
93 }
94
95 // Send a confirmation int to the host and get per-process unique ID back
96 int32_t confirmInt = 100;
97 status = vmo.write(&confirmInt, 0, sizeof(confirmInt));
98 if (status != ZX_OK) {
99 ALOGE("%s: failed write confirm int", __FUNCTION__);
100 return;
101 }
102 status = pipe->DoCall(sizeof(confirmInt), 0, sizeof(sProcUID), 0, &status2, &actual);
103 if (status != ZX_OK || status2 != ZX_OK) {
104 ALOGD("%s: failed to get per-process ID: %d:%d", __FUNCTION__,
105 status, status2);
106 return;
107 }
108 status = vmo.read(&sProcUID, 0, sizeof(sProcUID));
109 if (status != ZX_OK) {
110 ALOGE("%s: failed read per-process ID: %d", __FUNCTION__, status);
111 return;
112 }
113 sProcDevice = device.Unbind().TakeChannel().release();
114 sProcPipe = pipe.Unbind().TakeChannel().release();
115 }
116 #else // __Fuchsia__
117
sQemuPipeInit()118 static void sQemuPipeInit() {
119 sProcPipe = qemu_pipe_open("GLProcessPipe");
120 if (!qemu_pipe_valid(sProcPipe)) {
121 sProcPipe = 0;
122 ALOGW("Process pipe failed");
123 return;
124 }
125 // Send a confirmation int to the host
126 int32_t confirmInt = 100;
127 ssize_t stat = 0;
128 do {
129 stat =
130 qemu_pipe_write(sProcPipe, (const char*)&confirmInt,
131 sizeof(confirmInt));
132 } while (stat < 0 && errno == EINTR);
133
134 if (stat != sizeof(confirmInt)) { // failed
135 qemu_pipe_close(sProcPipe);
136 sProcPipe = 0;
137 ALOGW("Process pipe failed");
138 return;
139 }
140
141 // Ask the host for per-process unique ID
142 do {
143 stat =
144 qemu_pipe_read(sProcPipe, (char*)&sProcUID,
145 sizeof(sProcUID));
146 } while (stat < 0 && (errno == EINTR || errno == EAGAIN));
147
148 if (stat != sizeof(sProcUID)) {
149 qemu_pipe_close(sProcPipe);
150 sProcPipe = 0;
151 sProcUID = 0;
152 ALOGW("Process pipe failed");
153 return;
154 }
155 }
156
processPipeInitOnce()157 static void processPipeInitOnce() {
158 #if defined(HOST_BUILD) || !defined(GOLDFISH_VULKAN)
159 sQemuPipeInit();
160 #else // HOST_BUILD
161 switch (sConnType) {
162 // TODO: Move those over too
163 case HOST_CONNECTION_QEMU_PIPE:
164 case HOST_CONNECTION_ADDRESS_SPACE:
165 case HOST_CONNECTION_TCP:
166 case HOST_CONNECTION_VIRTIO_GPU:
167 sQemuPipeInit();
168 break;
169 case HOST_CONNECTION_VIRTIO_GPU_PIPE: {
170 sVirtioGpuPipeStream = new VirtioGpuPipeStream(4096);
171 sProcUID = sVirtioGpuPipeStream->initProcessPipe();
172 break;
173 }
174 }
175 #endif // !HOST_BUILD
176 }
177 #endif // !__Fuchsia__
178
processPipeInit(HostConnectionType connType,renderControl_encoder_context_t * rcEnc)179 bool processPipeInit(HostConnectionType connType, renderControl_encoder_context_t *rcEnc) {
180 sConnType = connType;
181 pthread_once(&sProcPipeOnce, processPipeInitOnce);
182 bool pipeHandleInvalid = !sProcPipe;
183 #ifndef __Fuchsia__
184 pipeHandleInvalid = pipeHandleInvalid && !sVirtioGpuPipeStream;
185 #endif // !__Fuchsia__
186 if (pipeHandleInvalid) return false;
187 rcEnc->rcSetPuid(rcEnc, sProcUID);
188 return true;
189 }
190