blob: 084bfa2e0872550ae01f9a9d76de33c45909d245 [file] [log] [blame]
Steve Kondik95027ea2017-06-14 17:22:58 -07001//
2// vncflinger - Copyright (C) 2017 Steve Kondik
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//
17
Steve Kondik55db0532017-06-12 11:27:18 -070018#define LOG_TAG "VNCFlinger"
19#include <utils/Log.h>
20
21#include <binder/IPCThreadState.h>
Steve Kondik55db0532017-06-12 11:27:18 -070022#include <binder/IServiceManager.h>
Steve Kondik2c9d0cf2017-06-15 23:39:29 -070023#include <binder/ProcessState.h>
Steve Kondik55db0532017-06-12 11:27:18 -070024
Steve Kondik2c9d0cf2017-06-15 23:39:29 -070025#include <gui/IGraphicBufferProducer.h>
Steve Kondik55db0532017-06-12 11:27:18 -070026#include <gui/ISurfaceComposer.h>
27#include <gui/SurfaceComposerClient.h>
Steve Kondik55db0532017-06-12 11:27:18 -070028
Steve Kondik107d2e52017-06-13 17:34:56 -070029#include "InputDevice.h"
Steve Kondik55db0532017-06-12 11:27:18 -070030#include "VNCFlinger.h"
31
32using namespace android;
33
Steve Kondik55db0532017-06-12 11:27:18 -070034status_t VNCFlinger::start() {
Steve Kondik77754522017-06-14 17:00:33 -070035 sp<ProcessState> self = ProcessState::self();
36 self->startThreadPool();
Steve Kondik55db0532017-06-12 11:27:18 -070037
Steve Kondik2c9d0cf2017-06-15 23:39:29 -070038 mMainDpy = SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain);
Steve Kondik77754522017-06-14 17:00:33 -070039
Steve Kondik6ec5bc82017-06-15 01:31:51 -070040 updateDisplayProjection();
41
42 status_t err = createVNCServer();
Steve Kondik55db0532017-06-12 11:27:18 -070043 if (err != NO_ERROR) {
44 ALOGE("Failed to start VNCFlinger: err=%d", err);
45 return err;
46 }
47
Steve Kondik55db0532017-06-12 11:27:18 -070048 rfbRunEventLoop(mVNCScreen, -1, true);
Steve Kondik7225c7f2017-06-14 00:06:16 -070049
Steve Kondik46798992017-06-15 23:58:54 -070050 ALOGD("VNCFlinger ready to fling");
Steve Kondik6ec5bc82017-06-15 01:31:51 -070051
Steve Kondik77754522017-06-14 17:00:33 -070052 eventLoop();
Steve Kondik55db0532017-06-12 11:27:18 -070053
Steve Kondik46798992017-06-15 23:58:54 -070054 ALOGI("VNCFlinger has left the building");
55
56 return NO_ERROR;
57}
58
59status_t VNCFlinger::stop() {
60 Mutex::Autolock _L(mEventMutex);
61
62 ALOGV("Shutting down");
63
64 destroyVirtualDisplayLocked();
65 mClientCount = 0;
66 mRunning = false;
67
68 mEventCond.signal();
Steve Kondik5f7b0a82017-06-16 09:04:43 -070069 delete[] mVNCScreen->frameBuffer;
Steve Kondik55db0532017-06-12 11:27:18 -070070 return NO_ERROR;
71}
72
Steve Kondik77754522017-06-14 17:00:33 -070073void VNCFlinger::eventLoop() {
74 mRunning = true;
75
76 Mutex::Autolock _l(mEventMutex);
77 while (mRunning) {
78 mEventCond.wait(mEventMutex);
79
80 // spurious wakeup? never.
81 if (mClientCount == 0) {
82 continue;
83 }
84
85 // this is a new client, so fire everything up
86 status_t err = createVirtualDisplay();
87 if (err != NO_ERROR) {
88 ALOGE("Failed to create virtual display: err=%d", err);
89 }
90
91 // loop while clients are connected and process frames
92 // on the main thread when signalled
93 while (mClientCount > 0) {
94 mEventCond.wait(mEventMutex);
95 if (mFrameAvailable) {
Steve Kondik6ec5bc82017-06-15 01:31:51 -070096 if (!updateDisplayProjection()) {
97 processFrame();
98 }
Steve Kondik77754522017-06-14 17:00:33 -070099 mFrameAvailable = false;
100 }
101 }
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700102 Mutex::Autolock _l(mUpdateMutex);
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700103 memset(mVNCScreen->frameBuffer, 0, mFrameSize);
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700104 destroyVirtualDisplayLocked();
Steve Kondik77754522017-06-14 17:00:33 -0700105 }
106}
107
108status_t VNCFlinger::createVirtualDisplay() {
109 sp<IGraphicBufferConsumer> consumer;
110 BufferQueue::createBufferQueue(&mProducer, &consumer);
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700111 mCpuConsumer = new CpuConsumer(consumer, NUM_BUFS);
Steve Kondik77754522017-06-14 17:00:33 -0700112 mCpuConsumer->setName(String8("vds-to-cpu"));
113 mCpuConsumer->setDefaultBufferSize(mWidth, mHeight);
114 mProducer->setMaxDequeuedBufferCount(4);
115
116 mListener = new FrameListener(this);
117 mCpuConsumer->setFrameAvailableListener(mListener);
118
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700119 mDpy = SurfaceComposerClient::createDisplay(String8("VNC-VirtualDisplay"), false /*secure*/);
Steve Kondik77754522017-06-14 17:00:33 -0700120
121 SurfaceComposerClient::openGlobalTransaction();
122 SurfaceComposerClient::setDisplaySurface(mDpy, mProducer);
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700123 // setDisplayProjection(mDpy, mainDpyInfo);
124 SurfaceComposerClient::setDisplayLayerStack(mDpy, 0); // default stack
Steve Kondik77754522017-06-14 17:00:33 -0700125 SurfaceComposerClient::closeGlobalTransaction();
126
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700127 mVDSActive = true;
128
129 ALOGV("Virtual display (%dx%d) created", mWidth, mHeight);
130
Steve Kondik77754522017-06-14 17:00:33 -0700131 return NO_ERROR;
132}
133
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700134status_t VNCFlinger::destroyVirtualDisplayLocked() {
Steve Kondik46798992017-06-15 23:58:54 -0700135 if (!mVDSActive) {
136 return NO_INIT;
137 }
138
Steve Kondik77754522017-06-14 17:00:33 -0700139 mCpuConsumer.clear();
140 mProducer.clear();
141 SurfaceComposerClient::destroyDisplay(mDpy);
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700142
143 mVDSActive = false;
144
145 ALOGV("Virtual display destroyed");
146
Steve Kondik77754522017-06-14 17:00:33 -0700147 return NO_ERROR;
148}
149
150status_t VNCFlinger::createVNCServer() {
Steve Kondik55db0532017-06-12 11:27:18 -0700151 status_t err = NO_ERROR;
152
Steve Kondik55db0532017-06-12 11:27:18 -0700153 rfbLog = VNCFlinger::rfbLogger;
154 rfbErr = VNCFlinger::rfbLogger;
155
156 // 32-bit color
157 mVNCScreen = rfbGetScreen(&mArgc, mArgv, mWidth, mHeight, 8, 3, 4);
158 if (mVNCScreen == NULL) {
159 ALOGE("Unable to create VNCScreen");
160 return NO_INIT;
161 }
162
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700163 mFrameNumber = 0;
164 mFrameSize = mWidth * mHeight * 4;
165 mVNCScreen->frameBuffer = (char*)new uint8_t[mFrameSize];
166 memset(mVNCScreen->frameBuffer, 0, mFrameSize);
167
Steve Kondik55db0532017-06-12 11:27:18 -0700168 mVNCScreen->desktopName = "VNCFlinger";
Steve Kondik55db0532017-06-12 11:27:18 -0700169 mVNCScreen->alwaysShared = TRUE;
170 mVNCScreen->httpDir = NULL;
171 mVNCScreen->port = VNC_PORT;
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700172 mVNCScreen->newClientHook = (rfbNewClientHookPtr)VNCFlinger::onNewClient;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700173 mVNCScreen->kbdAddEvent = InputDevice::onKeyEvent;
174 mVNCScreen->ptrAddEvent = InputDevice::onPointerEvent;
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700175 mVNCScreen->displayHook = (rfbDisplayHookPtr)VNCFlinger::onFrameStart;
176 mVNCScreen->displayFinishedHook = (rfbDisplayFinishedHookPtr)VNCFlinger::onFrameDone;
Steve Kondik55db0532017-06-12 11:27:18 -0700177 mVNCScreen->serverFormat.trueColour = true;
178 mVNCScreen->serverFormat.bitsPerPixel = 32;
179 mVNCScreen->handleEventsEagerly = true;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700180 mVNCScreen->deferUpdateTime = 1;
Steve Kondik7225c7f2017-06-14 00:06:16 -0700181 mVNCScreen->screenData = this;
Steve Kondik55db0532017-06-12 11:27:18 -0700182 rfbInitServer(mVNCScreen);
183
184 /* Mark as dirty since we haven't sent any updates at all yet. */
185 rfbMarkRectAsModified(mVNCScreen, 0, 0, mWidth, mHeight);
186
187 return err;
188}
189
Steve Kondik7225c7f2017-06-14 00:06:16 -0700190size_t VNCFlinger::addClient() {
Steve Kondik77754522017-06-14 17:00:33 -0700191 Mutex::Autolock _l(mEventMutex);
Steve Kondik7225c7f2017-06-14 00:06:16 -0700192 if (mClientCount == 0) {
Steve Kondik77754522017-06-14 17:00:33 -0700193 mClientCount++;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700194 InputDevice::getInstance().start(mWidth, mHeight);
Steve Kondik77754522017-06-14 17:00:33 -0700195 mEventCond.signal();
Steve Kondik55db0532017-06-12 11:27:18 -0700196 }
Steve Kondik7225c7f2017-06-14 00:06:16 -0700197
198 ALOGI("Client connected (%zu)", mClientCount);
199
200 return mClientCount;
Steve Kondik55db0532017-06-12 11:27:18 -0700201}
202
Steve Kondik7225c7f2017-06-14 00:06:16 -0700203size_t VNCFlinger::removeClient() {
Steve Kondik77754522017-06-14 17:00:33 -0700204 Mutex::Autolock _l(mEventMutex);
Steve Kondik7225c7f2017-06-14 00:06:16 -0700205 if (mClientCount > 0) {
206 mClientCount--;
207 if (mClientCount == 0) {
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700208 InputDevice::getInstance().stop();
Steve Kondik77754522017-06-14 17:00:33 -0700209 mEventCond.signal();
Steve Kondik7225c7f2017-06-14 00:06:16 -0700210 }
211 }
212
213 ALOGI("Client disconnected (%zu)", mClientCount);
214
215 return mClientCount;
216}
217
Steve Kondik77754522017-06-14 17:00:33 -0700218void VNCFlinger::processFrame() {
Steve Kondik77754522017-06-14 17:00:33 -0700219 // Take the update mutex. This ensures that we don't dequeue
220 // a new buffer and blow away the one being sent to a client.
221 // The BufferQueue is self-regulating and will drop frames
222 // automatically for us.
223 Mutex::Autolock _l(mUpdateMutex);
224
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700225 // get a frame from the virtual display
Steve Kondik77754522017-06-14 17:00:33 -0700226 CpuConsumer::LockedBuffer imgBuffer;
227 status_t res = mCpuConsumer->lockNextBuffer(&imgBuffer);
228 if (res != OK) {
229 ALOGE("Failed to lock next buffer: %s (%d)", strerror(-res), res);
230 return;
231 }
232
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700233 mFrameNumber = imgBuffer.frameNumber;
234 ALOGV("processFrame: [%lu] format: %x (%dx%d, stride=%d)", mFrameNumber, imgBuffer.format,
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700235 imgBuffer.width, imgBuffer.height, imgBuffer.stride);
Steve Kondik77754522017-06-14 17:00:33 -0700236
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700237 // we don't know if there was a stride change until we get
238 // a buffer from the queue. if it changed, we need to resize
239 updateFBSize(imgBuffer);
Steve Kondik77754522017-06-14 17:00:33 -0700240
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700241 // performance is extremely bad if the gpu memory is used
242 // directly without copying because it is likely uncached
243 memcpy(mVNCScreen->frameBuffer, imgBuffer.data, mFrameSize);
Steve Kondik77754522017-06-14 17:00:33 -0700244
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700245 // update clients
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700246 rfbMarkRectAsModified(mVNCScreen, 0, 0, imgBuffer.width, imgBuffer.height);
Steve Kondik77754522017-06-14 17:00:33 -0700247
248 mCpuConsumer->unlockBuffer(imgBuffer);
249}
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700250
251/*
252 * Returns "true" if the device is rotated 90 degrees.
253 */
254bool VNCFlinger::isDeviceRotated(int orientation) {
Steve Kondik2c9d0cf2017-06-15 23:39:29 -0700255 return orientation != DISPLAY_ORIENTATION_0 && orientation != DISPLAY_ORIENTATION_180;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700256}
257
258/*
259 * Sets the display projection, based on the display dimensions, video size,
260 * and device orientation.
261 */
262bool VNCFlinger::updateDisplayProjection() {
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700263 DisplayInfo info;
264 status_t err = SurfaceComposerClient::getDisplayInfo(mMainDpy, &info);
265 if (err != NO_ERROR) {
266 ALOGE("Failed to get display characteristics\n");
267 return true;
268 }
269
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700270 // Set the region of the layer stack we're interested in, which in our
271 // case is "all of it". If the app is rotated (so that the width of the
272 // app is based on the height of the display), reverse width/height.
273 bool deviceRotated = isDeviceRotated(info.orientation);
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700274 uint32_t sourceWidth, sourceHeight;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700275 if (!deviceRotated) {
276 sourceWidth = info.w;
277 sourceHeight = info.h;
278 } else {
279 ALOGV("using rotated width/height");
280 sourceHeight = info.w;
281 sourceWidth = info.h;
282 }
283
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700284 if (mWidth == sourceWidth && mHeight == sourceHeight && mOrientation == info.orientation) {
285 return false;
286 }
287
288 ALOGD("Display dimensions: %dx%d orientation=%d", sourceWidth, sourceHeight, info.orientation);
289
290 // orientation change
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700291 mWidth = sourceWidth;
292 mHeight = sourceHeight;
293 mOrientation = info.orientation;
294
295 if (!mVDSActive) {
296 return true;
297 }
298
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700299 // it does not appear to be possible to reconfigure the virtual display
300 // on the fly without forcing surfaceflinger to tear it down
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700301 destroyVirtualDisplayLocked();
302 createVirtualDisplay();
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700303 return false;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700304}
305
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700306bool VNCFlinger::updateFBSize(CpuConsumer::LockedBuffer& buf) {
307 uint32_t stride = (uint32_t)mVNCScreen->paddedWidthInBytes / 4;
308 uint32_t width = (uint32_t)mVNCScreen->width;
309 uint32_t height = (uint32_t)mVNCScreen->height;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700310
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700311 uint64_t newSize = buf.stride * buf.height * 4;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700312
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700313 if (stride != buf.stride || height != buf.height || width != buf.width) {
314 ALOGD("updateFBSize: old=[%dx%d %d] new=[%dx%d %d]", width, height, stride, buf.width,
315 buf.height, buf.stride);
316
317 if (mFrameSize != newSize) {
318 mFrameSize = newSize;
319 delete[] mVNCScreen->frameBuffer;
320 rfbNewFramebuffer(mVNCScreen, (char*)new uint8_t[newSize], buf.width, buf.height, 8, 3,
321 4);
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700322 }
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700323 mVNCScreen->paddedWidthInBytes = buf.stride * 4;
Steve Kondik6ec5bc82017-06-15 01:31:51 -0700324 }
325 return NO_ERROR;
326}
Steve Kondik5f7b0a82017-06-16 09:04:43 -0700327
328// ------------------------------------------------------------------------ //
329
330// libvncserver logger
331void VNCFlinger::rfbLogger(const char* format, ...) {
332 va_list args;
333 char buf[256];
334
335 va_start(args, format);
336 vsprintf(buf, format, args);
337 ALOGI("%s", buf);
338 va_end(args);
339}
340
341// libvncserver callbacks
342ClientGoneHookPtr VNCFlinger::onClientGone(rfbClientPtr cl) {
343 ALOGV("onClientGone");
344 VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
345 vf->removeClient();
346 return 0;
347}
348
349enum rfbNewClientAction VNCFlinger::onNewClient(rfbClientPtr cl) {
350 ALOGV("onNewClient");
351 cl->clientGoneHook = (ClientGoneHookPtr)VNCFlinger::onClientGone;
352 VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
353 vf->addClient();
354 return RFB_CLIENT_ACCEPT;
355}
356
357void VNCFlinger::onFrameStart(rfbClientPtr cl) {
358 VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
359 vf->mUpdateMutex.lock();
360
361 vf->mFrameStartWhen = systemTime(CLOCK_MONOTONIC);
362 ALOGV("frame start [%lu]", vf->mFrameNumber);
363}
364
365void VNCFlinger::onFrameDone(rfbClientPtr cl, int /* status */) {
366 VNCFlinger* vf = (VNCFlinger*)cl->screen->screenData;
367
368 float timing = (systemTime(CLOCK_MONOTONIC) - vf->mFrameStartWhen) / 1000000.0;
369 ALOGV("onFrameDone [%lu] (%.3f ms)", vf->mFrameNumber, timing);
370
371 vf->mUpdateMutex.unlock();
372}
373
374// cpuconsumer frame listener
375void VNCFlinger::FrameListener::onFrameAvailable(const BufferItem& item) {
376 Mutex::Autolock _l(mVNC->mEventMutex);
377 mVNC->mFrameAvailable = true;
378 mVNC->mEventCond.signal();
379 ALOGV("onFrameAvailable: [%lu] mTimestamp=%ld", item.mFrameNumber, item.mTimestamp);
380}