1 /*
2 * Copyright 2024 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 <aidl/android/crosvm/BnCrosvmAndroidDisplayService.h>
18 #include <aidl/android/system/virtualizationservice_internal/IVirtualizationServiceInternal.h>
19 #include <android-base/result.h>
20 #include <android/binder_manager.h>
21 #include <android/binder_process.h>
22 #include <system/graphics.h> // for HAL_PIXEL_FORMAT_*
23
24 #include <condition_variable>
25 #include <memory>
26 #include <mutex>
27
28 using aidl::android::crosvm::BnCrosvmAndroidDisplayService;
29 using aidl::android::system::virtualizationservice_internal::IVirtualizationServiceInternal;
30 using aidl::android::view::Surface;
31
32 using android::base::Error;
33 using android::base::Result;
34
35 namespace {
36
37 class SinkANativeWindow_Buffer {
38 public:
configure(uint32_t width,uint32_t height,int format)39 Result<void> configure(uint32_t width, uint32_t height, int format) {
40 if (format != HAL_PIXEL_FORMAT_BGRA_8888) {
41 return Error() << "Pixel format " << format << " is not BGRA_8888.";
42 }
43
44 mBufferBits.resize(width * height * 4);
45 mBuffer = ANativeWindow_Buffer{
46 .width = static_cast<int32_t>(width),
47 .height = static_cast<int32_t>(height),
48 .stride = static_cast<int32_t>(width),
49 .format = format,
50 .bits = mBufferBits.data(),
51 };
52 return {};
53 }
54
operator ANativeWindow_Buffer&()55 operator ANativeWindow_Buffer&() { return mBuffer; }
56
57 private:
58 ANativeWindow_Buffer mBuffer;
59 std::vector<uint8_t> mBufferBits;
60 };
61
copyBuffer(ANativeWindow_Buffer & from,ANativeWindow_Buffer & to)62 static Result<void> copyBuffer(ANativeWindow_Buffer& from, ANativeWindow_Buffer& to) {
63 if (from.width != to.width || from.height != to.height) {
64 return Error() << "dimension mismatch. from=(" << from.width << ", " << from.height << ") "
65 << "to=(" << to.width << ", " << to.height << ")";
66 }
67 uint32_t* dst = reinterpret_cast<uint32_t*>(to.bits);
68 uint32_t* src = reinterpret_cast<uint32_t*>(from.bits);
69 size_t bytes_on_line = to.width * 4; // 4 bytes per pixel
70 for (int32_t h = 0; h < to.height; h++) {
71 memcpy(dst + (h * to.stride), src + (h * from.stride), bytes_on_line);
72 }
73 return {};
74 }
75
76 // Wrapper which contains the latest available Surface/ANativeWindow from the DisplayService, if
77 // available. A Surface/ANativeWindow may not always be available if, for example, the VmLauncherApp
78 // on the other end of the DisplayService is not in the foreground / is paused.
79 class AndroidDisplaySurface {
80 public:
AndroidDisplaySurface(const std::string & name)81 AndroidDisplaySurface(const std::string& name) : mName(name) {}
82
setNativeSurface(Surface * surface)83 void setNativeSurface(Surface* surface) {
84 {
85 std::lock_guard lk(mSurfaceMutex);
86 mNativeSurface = std::make_unique<Surface>(surface->release());
87 mNativeSurfaceNeedsConfiguring = true;
88 }
89
90 mNativeSurfaceReady.notify_one();
91 }
92
removeSurface()93 void removeSurface() {
94 {
95 std::lock_guard lk(mSurfaceMutex);
96 mNativeSurface = nullptr;
97 }
98 mNativeSurfaceReady.notify_one();
99 }
100
getSurface()101 Surface* getSurface() {
102 std::unique_lock lk(mSurfaceMutex);
103 return mNativeSurface.get();
104 }
105
configure(uint32_t width,uint32_t height)106 Result<void> configure(uint32_t width, uint32_t height) {
107 std::unique_lock lk(mSurfaceMutex);
108
109 mRequestedSurfaceDimensions = Rect{
110 .width = width,
111 .height = height,
112 };
113
114 if (auto ret = mSinkBuffer.configure(width, height, kFormat); !ret.ok()) {
115 return Error() << "Failed to configure sink buffer: " << ret.error();
116 }
117 if (auto ret = mSavedFrameBuffer.configure(width, height, kFormat); !ret.ok()) {
118 return Error() << "Failed to configure saved frame buffer: " << ret.error();
119 }
120 return {};
121 }
122
waitForNativeSurface()123 void waitForNativeSurface() {
124 std::unique_lock lk(mSurfaceMutex);
125 mNativeSurfaceReady.wait(lk, [this] { return mNativeSurface != nullptr; });
126 }
127
lock(ANativeWindow_Buffer * out_buffer)128 Result<void> lock(ANativeWindow_Buffer* out_buffer) {
129 std::unique_lock lk(mSurfaceMutex);
130
131 Surface* surface = mNativeSurface.get();
132 if (surface == nullptr) {
133 // Surface not currently available but not necessarily an error
134 // if, for example, the VmLauncherApp is not in the foreground.
135 *out_buffer = mSinkBuffer;
136 return {};
137 }
138
139 ANativeWindow* anw = surface->get();
140 if (anw == nullptr) {
141 return Error() << "Failed to get ANativeWindow";
142 }
143
144 if (mNativeSurfaceNeedsConfiguring) {
145 if (!mRequestedSurfaceDimensions) {
146 return Error() << "Surface dimension is not configured yet!";
147 }
148 const auto& dims = *mRequestedSurfaceDimensions;
149
150 // Ensure locked buffers have our desired format.
151 if (ANativeWindow_setBuffersGeometry(anw, dims.width, dims.height, kFormat) != 0) {
152 return Error() << "Failed to set buffer geometry.";
153 }
154
155 mNativeSurfaceNeedsConfiguring = false;
156 }
157
158 if (ANativeWindow_lock(anw, out_buffer, nullptr) != 0) {
159 return Error() << "Failed to lock window";
160 }
161 mLastBuffer = *out_buffer;
162 return {};
163 }
164
unlockAndPost()165 Result<void> unlockAndPost() {
166 std::unique_lock lk(mSurfaceMutex);
167
168 Surface* surface = mNativeSurface.get();
169 if (surface == nullptr) {
170 // Surface not currently available but not necessarily an error
171 // if, for example, the VmLauncherApp is not in the foreground.
172 return {};
173 }
174
175 ANativeWindow* anw = surface->get();
176 if (anw == nullptr) {
177 return Error() << "Failed to get ANativeWindow";
178 }
179
180 if (ANativeWindow_unlockAndPost(anw) != 0) {
181 return Error() << "Failed to unlock and post window";
182 }
183 return {};
184 }
185
186 // Saves the last frame drawn
saveFrame()187 Result<void> saveFrame() {
188 std::unique_lock lk(mSurfaceMutex);
189 if (auto ret = copyBuffer(mLastBuffer, mSavedFrameBuffer); !ret.ok()) {
190 return Error() << "Failed to copy frame: " << ret.error();
191 }
192 return {};
193 }
194
195 // Draws the saved frame
drawSavedFrame()196 Result<void> drawSavedFrame() {
197 std::unique_lock lk(mSurfaceMutex);
198 Surface* surface = mNativeSurface.get();
199 if (surface == nullptr) {
200 return Error() << "Surface not ready";
201 }
202
203 ANativeWindow* anw = surface->get();
204 if (anw == nullptr) {
205 return Error() << "Failed to get ANativeWindow";
206 }
207
208 // TODO: dedup this and the one in lock(...)
209 if (mNativeSurfaceNeedsConfiguring) {
210 if (!mRequestedSurfaceDimensions) {
211 return Error() << "Surface dimension is not configured yet!";
212 }
213 const auto& dims = *mRequestedSurfaceDimensions;
214
215 // Ensure locked buffers have our desired format.
216 if (ANativeWindow_setBuffersGeometry(anw, dims.width, dims.height, kFormat) != 0) {
217 return Error() << "Failed to set buffer geometry.";
218 }
219
220 mNativeSurfaceNeedsConfiguring = false;
221 }
222
223 ANativeWindow_Buffer buf;
224 if (ANativeWindow_lock(anw, &buf, nullptr) != 0) {
225 return Error() << "Failed to lock window";
226 }
227
228 if (auto ret = copyBuffer(mSavedFrameBuffer, buf); !ret.ok()) {
229 return Error() << "Failed to copy frame: " << ret.error();
230 }
231
232 if (ANativeWindow_unlockAndPost(anw) != 0) {
233 return Error() << "Failed to unlock and post window";
234 }
235 return {};
236 }
237
name() const238 const std::string& name() const { return mName; }
239
240 private:
241 // Note: crosvm always uses BGRA8888 or BGRX8888. See devices/src/virtio/gpu/mod.rs in
242 // crosvm where the SetScanoutBlob command is handled. Let's use BGRA not BGRX with a hope
243 // that we will need alpha blending for the cursor surface.
244 static constexpr const int kFormat = HAL_PIXEL_FORMAT_BGRA_8888;
245
246 std::string mName;
247
248 std::mutex mSurfaceMutex;
249 std::unique_ptr<Surface> mNativeSurface;
250 std::condition_variable mNativeSurfaceReady;
251 bool mNativeSurfaceNeedsConfiguring = true;
252
253 // Buffer which crosvm uses when in background. This is just to not fail crosvm even when
254 // Android-side Surface doesn't exist. The content drawn here is never displayed on the physical
255 // screen.
256 SinkANativeWindow_Buffer mSinkBuffer;
257
258 // Buffer which is currently allocated for crosvm to draw onto. This holds the last frame. This
259 // is what gets displayed on the physical screen.
260 ANativeWindow_Buffer mLastBuffer;
261
262 // Copy of mLastBuffer made by the call saveFrameForSurface. This holds the last good (i.e.
263 // non-blank) frame before the VM goes background. When the VM is brought up to foreground,
264 // this is drawn to the physical screen until the VM starts to emit actual frames.
265 SinkANativeWindow_Buffer mSavedFrameBuffer;
266
267 struct Rect {
268 uint32_t width = 0;
269 uint32_t height = 0;
270 };
271 std::optional<Rect> mRequestedSurfaceDimensions;
272 };
273
274 class DisplayService : public BnCrosvmAndroidDisplayService {
275 public:
276 DisplayService() = default;
277 virtual ~DisplayService() = default;
278
setSurface(Surface * surface,bool forCursor)279 ndk::ScopedAStatus setSurface(Surface* surface, bool forCursor) override {
280 getSurface(forCursor).setNativeSurface(surface);
281 return ::ndk::ScopedAStatus::ok();
282 }
283
removeSurface(bool forCursor)284 ndk::ScopedAStatus removeSurface(bool forCursor) override {
285 getSurface(forCursor).removeSurface();
286 return ::ndk::ScopedAStatus::ok();
287 }
288
getCursorStream()289 ndk::ScopedFileDescriptor& getCursorStream() { return mCursorStream; }
setCursorStream(const ndk::ScopedFileDescriptor & in_stream)290 ndk::ScopedAStatus setCursorStream(const ndk::ScopedFileDescriptor& in_stream) {
291 mCursorStream = ndk::ScopedFileDescriptor(dup(in_stream.get()));
292 return ::ndk::ScopedAStatus::ok();
293 }
294
saveFrameForSurface(bool forCursor)295 ndk::ScopedAStatus saveFrameForSurface(bool forCursor) override {
296 if (auto ret = getSurface(forCursor).saveFrame(); !ret.ok()) {
297 std::string msg = std::format("Failed to save frame: {}", ret.error().message());
298 return ::ndk::ScopedAStatus(
299 AStatus_fromServiceSpecificErrorWithMessage(-1, msg.c_str()));
300 }
301 return ::ndk::ScopedAStatus::ok();
302 }
303
drawSavedFrameForSurface(bool forCursor)304 ndk::ScopedAStatus drawSavedFrameForSurface(bool forCursor) override {
305 if (auto ret = getSurface(forCursor).drawSavedFrame(); !ret.ok()) {
306 std::string msg = std::format("Failed to draw saved frame: {}", ret.error().message());
307 return ::ndk::ScopedAStatus(
308 AStatus_fromServiceSpecificErrorWithMessage(-1, msg.c_str()));
309 }
310 return ::ndk::ScopedAStatus::ok();
311 }
312
getSurface(bool forCursor)313 AndroidDisplaySurface& getSurface(bool forCursor) {
314 if (forCursor) {
315 return mCursor;
316 } else {
317 return mScanout;
318 }
319 }
320
321 private:
322 AndroidDisplaySurface mScanout{"scanout"};
323 AndroidDisplaySurface mCursor{"cursor"};
324 ndk::ScopedFileDescriptor mCursorStream;
325 };
326
327 } // namespace
328
329 typedef void (*ErrorCallback)(const char* message);
330
331 struct AndroidDisplayContext {
332 std::shared_ptr<IVirtualizationServiceInternal> virt_service;
333 std::shared_ptr<DisplayService> disp_service;
334 ErrorCallback error_callback;
335
AndroidDisplayContextAndroidDisplayContext336 AndroidDisplayContext(ErrorCallback cb) : error_callback(cb) {
337 auto disp_service = ::ndk::SharedRefBase::make<DisplayService>();
338
339 // Creates DisplayService and register it to the virtualizationservice. This is needed
340 // because this code is executed inside of crosvm which runs as an app. Apps are not allowed
341 // to register a service to the service manager.
342 auto virt_service = IVirtualizationServiceInternal::fromBinder(ndk::SpAIBinder(
343 AServiceManager_waitForService("android.system.virtualizationservice")));
344 if (virt_service == nullptr) {
345 errorf("Failed to find virtualization service");
346 return;
347 }
348 auto status = virt_service->setDisplayService(disp_service->asBinder());
349 if (!status.isOk()) {
350 errorf("Failed to register display service");
351 return;
352 }
353
354 this->virt_service = virt_service;
355 this->disp_service = disp_service;
356 ABinderProcess_startThreadPool();
357 }
358
~AndroidDisplayContextAndroidDisplayContext359 ~AndroidDisplayContext() {
360 if (virt_service == nullptr) {
361 errorf("Not connected to virtualization service");
362 return;
363 }
364 auto status = this->virt_service->clearDisplayService();
365 if (!status.isOk()) {
366 errorf("Failed to clear display service");
367 }
368 }
369
errorfAndroidDisplayContext370 void errorf(const char* format, ...) {
371 char buffer[1024];
372
373 va_list vararg;
374 va_start(vararg, format);
375 vsnprintf(buffer, sizeof(buffer), format, vararg);
376 va_end(vararg);
377
378 error_callback(buffer);
379 }
380 };
381
create_android_display_context(const char *,ErrorCallback error_callback)382 extern "C" struct AndroidDisplayContext* create_android_display_context(
383 const char*, ErrorCallback error_callback) {
384 return new AndroidDisplayContext(error_callback);
385 }
386
destroy_android_display_context(struct AndroidDisplayContext * ctx)387 extern "C" void destroy_android_display_context(struct AndroidDisplayContext* ctx) {
388 delete ctx;
389 }
390
create_android_surface(struct AndroidDisplayContext * ctx,uint32_t width,uint32_t height,bool forCursor)391 extern "C" AndroidDisplaySurface* create_android_surface(struct AndroidDisplayContext* ctx,
392 uint32_t width, uint32_t height,
393 bool forCursor) {
394 if (ctx->disp_service == nullptr) {
395 ctx->errorf("Display service was not created");
396 return nullptr;
397 }
398
399 AndroidDisplaySurface& surface = ctx->disp_service->getSurface(forCursor);
400 if (auto ret = surface.configure(width, height); !ret.ok()) {
401 ctx->errorf("Failed to configure surface %s: %s", surface.name().c_str(),
402 ret.error().message().c_str());
403 }
404
405 surface.waitForNativeSurface(); // this can block
406
407 // TODO(b/332785161): if we know that surface can get destroyed dynamically while VM is running,
408 // consider calling ANativeWindow_acquire here and _release in destroy_android_surface, so that
409 // crosvm doesn't hold a dangling pointer.
410 return &surface;
411 }
412
destroy_android_surface(struct AndroidDisplayContext *,ANativeWindow *)413 extern "C" void destroy_android_surface(struct AndroidDisplayContext*, ANativeWindow*) {
414 // NOT IMPLEMENTED
415 }
416
get_android_surface_buffer(struct AndroidDisplayContext * ctx,AndroidDisplaySurface * surface,ANativeWindow_Buffer * out_buffer)417 extern "C" bool get_android_surface_buffer(struct AndroidDisplayContext* ctx,
418 AndroidDisplaySurface* surface,
419 ANativeWindow_Buffer* out_buffer) {
420 if (out_buffer == nullptr) {
421 ctx->errorf("out_buffer is null");
422 return false;
423 }
424
425 if (surface == nullptr) {
426 ctx->errorf("Invalid AndroidDisplaySurface provided");
427 return false;
428 }
429
430 auto ret = surface->lock(out_buffer);
431 if (!ret.ok()) {
432 ctx->errorf("Failed to lock surface %s: %s", surface->name().c_str(),
433 ret.error().message().c_str());
434 return false;
435 }
436
437 return true;
438 }
439
set_android_surface_position(struct AndroidDisplayContext * ctx,uint32_t x,uint32_t y)440 extern "C" void set_android_surface_position(struct AndroidDisplayContext* ctx, uint32_t x,
441 uint32_t y) {
442 if (ctx->disp_service == nullptr) {
443 ctx->errorf("Display service was not created");
444 return;
445 }
446 auto fd = ctx->disp_service->getCursorStream().get();
447 if (fd == -1) {
448 ctx->errorf("Invalid fd");
449 return;
450 }
451 uint32_t pos[] = {x, y};
452 write(fd, pos, sizeof(pos));
453 }
454
post_android_surface_buffer(struct AndroidDisplayContext * ctx,AndroidDisplaySurface * surface)455 extern "C" void post_android_surface_buffer(struct AndroidDisplayContext* ctx,
456 AndroidDisplaySurface* surface) {
457 if (surface == nullptr) {
458 ctx->errorf("Invalid AndroidDisplaySurface provided");
459 return;
460 }
461
462 auto ret = surface->unlockAndPost();
463 if (!ret.ok()) {
464 ctx->errorf("Failed to unlock and post for surface %s: %s", surface->name().c_str(),
465 ret.error().message().c_str());
466 }
467 return;
468 }
469