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/binder_manager.h>
20 #include <android/binder_process.h>
21 #include <system/graphics.h> // for HAL_PIXEL_FORMAT_*
22
23 #include <condition_variable>
24 #include <memory>
25 #include <mutex>
26
27 using aidl::android::crosvm::BnCrosvmAndroidDisplayService;
28 using aidl::android::system::virtualizationservice_internal::IVirtualizationServiceInternal;
29 using aidl::android::view::Surface;
30
31 namespace {
32
33 class DisplayService : public BnCrosvmAndroidDisplayService {
34 public:
35 DisplayService() = default;
36 virtual ~DisplayService() = default;
37
setSurface(Surface * surface,bool forCursor)38 ndk::ScopedAStatus setSurface(Surface* surface, bool forCursor) override {
39 {
40 std::lock_guard lk(mSurfaceReadyMutex);
41 if (forCursor) {
42 mCursorSurface = std::make_unique<Surface>(surface->release());
43 } else {
44 mSurface = std::make_unique<Surface>(surface->release());
45 }
46 }
47 mSurfaceReady.notify_all();
48 return ::ndk::ScopedAStatus::ok();
49 }
50
removeSurface(bool forCursor)51 ndk::ScopedAStatus removeSurface(bool forCursor) override {
52 {
53 std::lock_guard lk(mSurfaceReadyMutex);
54 if (forCursor) {
55 mCursorSurface = nullptr;
56 } else {
57 mSurface = nullptr;
58 }
59 }
60 mSurfaceReady.notify_all();
61 return ::ndk::ScopedAStatus::ok();
62 }
63
getSurface(bool forCursor)64 Surface* getSurface(bool forCursor) {
65 std::unique_lock lk(mSurfaceReadyMutex);
66 if (forCursor) {
67 mSurfaceReady.wait(lk, [this] { return mCursorSurface != nullptr; });
68 return mCursorSurface.get();
69 } else {
70 mSurfaceReady.wait(lk, [this] { return mSurface != nullptr; });
71 return mSurface.get();
72 }
73 }
getCursorStream()74 ndk::ScopedFileDescriptor& getCursorStream() { return mCursorStream; }
setCursorStream(const ndk::ScopedFileDescriptor & in_stream)75 ndk::ScopedAStatus setCursorStream(const ndk::ScopedFileDescriptor& in_stream) {
76 mCursorStream = ndk::ScopedFileDescriptor(dup(in_stream.get()));
77 return ::ndk::ScopedAStatus::ok();
78 }
79
80 private:
81 std::condition_variable mSurfaceReady;
82 std::mutex mSurfaceReadyMutex;
83 std::unique_ptr<Surface> mSurface;
84 std::unique_ptr<Surface> mCursorSurface;
85 ndk::ScopedFileDescriptor mCursorStream;
86 };
87
88 } // namespace
89
90 typedef void (*ErrorCallback)(const char* message);
91
92 struct AndroidDisplayContext {
93 std::shared_ptr<IVirtualizationServiceInternal> virt_service;
94 std::shared_ptr<DisplayService> disp_service;
95 ErrorCallback error_callback;
96
AndroidDisplayContextAndroidDisplayContext97 AndroidDisplayContext(ErrorCallback cb) : error_callback(cb) {
98 auto disp_service = ::ndk::SharedRefBase::make<DisplayService>();
99
100 // Creates DisplayService and register it to the virtualizationservice. This is needed
101 // because this code is executed inside of crosvm which runs as an app. Apps are not allowed
102 // to register a service to the service manager.
103 auto virt_service = IVirtualizationServiceInternal::fromBinder(ndk::SpAIBinder(
104 AServiceManager_waitForService("android.system.virtualizationservice")));
105 if (virt_service == nullptr) {
106 errorf("Failed to find virtualization service");
107 return;
108 }
109 auto status = virt_service->setDisplayService(disp_service->asBinder());
110 if (!status.isOk()) {
111 errorf("Failed to register display service");
112 return;
113 }
114
115 this->virt_service = virt_service;
116 this->disp_service = disp_service;
117 ABinderProcess_startThreadPool();
118 }
119
~AndroidDisplayContextAndroidDisplayContext120 ~AndroidDisplayContext() {
121 if (virt_service == nullptr) {
122 errorf("Not connected to virtualization service");
123 return;
124 }
125 auto status = this->virt_service->clearDisplayService();
126 if (!status.isOk()) {
127 errorf("Failed to clear display service");
128 }
129 }
130
errorfAndroidDisplayContext131 void errorf(const char* format, ...) {
132 char buffer[1024];
133
134 va_list vararg;
135 va_start(vararg, format);
136 vsnprintf(buffer, sizeof(buffer), format, vararg);
137 va_end(vararg);
138
139 error_callback(buffer);
140 }
141 };
142
create_android_display_context(const char *,ErrorCallback error_callback)143 extern "C" struct AndroidDisplayContext* create_android_display_context(
144 const char*, ErrorCallback error_callback) {
145 return new AndroidDisplayContext(error_callback);
146 }
147
destroy_android_display_context(struct AndroidDisplayContext * ctx)148 extern "C" void destroy_android_display_context(struct AndroidDisplayContext* ctx) {
149 delete ctx;
150 }
151
create_android_surface(struct AndroidDisplayContext * ctx,uint32_t width,uint32_t height,bool for_cursor)152 extern "C" ANativeWindow* create_android_surface(struct AndroidDisplayContext* ctx, uint32_t width,
153 uint32_t height, bool for_cursor) {
154 if (ctx->disp_service == nullptr) {
155 ctx->errorf("Display service was not created");
156 return nullptr;
157 }
158 // Note: crosvm always uses BGRA8888 or BGRX8888. See devices/src/virtio/gpu/mod.rs in crosvm
159 // where the SetScanoutBlob command is handled. Let's use BGRA not BGRX with a hope that we will
160 // need alpha blending for the cursor surface.
161 int format = HAL_PIXEL_FORMAT_BGRA_8888;
162 ANativeWindow* surface = ctx->disp_service->getSurface(for_cursor)->get(); // this can block
163 if (ANativeWindow_setBuffersGeometry(surface, width, height, format) != 0) {
164 ctx->errorf("Failed to set buffer gemoetry");
165 return nullptr;
166 }
167 // TODO(b/332785161): if we know that surface can get destroyed dynamically while VM is running,
168 // consider calling ANativeWindow_acquire here and _release in destroy_android_surface, so that
169 // crosvm doesn't hold a dangling pointer.
170 return surface;
171 }
172
destroy_android_surface(struct AndroidDisplayContext *,ANativeWindow *)173 extern "C" void destroy_android_surface(struct AndroidDisplayContext*, ANativeWindow*) {
174 // NOT IMPLEMENTED
175 }
176
get_android_surface_buffer(struct AndroidDisplayContext * ctx,ANativeWindow * surface,ANativeWindow_Buffer * out_buffer)177 extern "C" bool get_android_surface_buffer(struct AndroidDisplayContext* ctx,
178 ANativeWindow* surface,
179 ANativeWindow_Buffer* out_buffer) {
180 if (out_buffer == nullptr) {
181 ctx->errorf("out_buffer is null");
182 return false;
183 }
184 if (ANativeWindow_lock(surface, out_buffer, nullptr) != 0) {
185 ctx->errorf("Failed to lock buffer");
186 return false;
187 }
188 return true;
189 }
190
set_android_surface_position(struct AndroidDisplayContext * ctx,uint32_t x,uint32_t y)191 extern "C" void set_android_surface_position(struct AndroidDisplayContext* ctx, uint32_t x,
192 uint32_t y) {
193 if (ctx->disp_service == nullptr) {
194 ctx->errorf("Display service was not created");
195 return;
196 }
197 auto fd = ctx->disp_service->getCursorStream().get();
198 if (fd == -1) {
199 ctx->errorf("Invalid fd");
200 return;
201 }
202 uint32_t pos[] = {x, y};
203 write(fd, pos, sizeof(pos));
204 }
205
post_android_surface_buffer(struct AndroidDisplayContext * ctx,ANativeWindow * surface)206 extern "C" void post_android_surface_buffer(struct AndroidDisplayContext* ctx,
207 ANativeWindow* surface) {
208 if (ANativeWindow_unlockAndPost(surface) != 0) {
209 ctx->errorf("Failed to unlock and post surface.");
210 return;
211 }
212 }
213