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