1 /*
2 * Copyright (C) 2022 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 "CarDisplayProxy.h"
18
19 #include <aidlcommonsupport/NativeHandle.h>
20 #include <android-base/logging.h>
21 #include <android-base/scopeguard.h>
22 #include <gui/ISurfaceComposer.h>
23 #include <gui/SurfaceComposerClient.h>
24 #include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
25 #include <gui/view/Surface.h>
26 #include <ui/Rotation.h>
27
28 #include <cinttypes>
29
30 namespace {
31
32 using ::aidl::android::frameworks::automotive::display::DisplayDesc;
33 using ::aidl::android::frameworks::automotive::display::Rotation;
34 using ::aidl::android::hardware::common::NativeHandle;
35 using ::android::SurfaceComposerClient;
36 using ::ndk::ScopedAStatus;
37
38 // We're using the highest Z-order.
39 constexpr int32_t kSurfaceZOrder = 0x7FFFFFFF;
40
convert(::android::ui::Rotation uiRotation)41 Rotation convert(::android::ui::Rotation uiRotation) {
42 switch (uiRotation) {
43 case ::android::ui::ROTATION_0:
44 return Rotation::ROTATION_0;
45 case ::android::ui::ROTATION_90:
46 return Rotation::ROTATION_90;
47 case ::android::ui::ROTATION_180:
48 return Rotation::ROTATION_180;
49 case ::android::ui::ROTATION_270:
50 return Rotation::ROTATION_270;
51 }
52 }
53
54 constexpr size_t kMaxWindowSize = 256;
55
convertHalTokenToNativeHandle(const::android::HalToken & halToken)56 native_handle_t* convertHalTokenToNativeHandle(const ::android::HalToken& halToken) {
57 // Attempts to store halToken in the ints of the native_handle_t after its
58 // size.
59 auto nhDataByteSize = halToken.size();
60 if (nhDataByteSize > kMaxWindowSize) {
61 return nullptr;
62 }
63
64 auto numInts = ceil(nhDataByteSize / sizeof(int)) + 1;
65 native_handle_t* nh = native_handle_create(/* numFds = */ 0, numInts);
66 if (nh == nullptr) {
67 return nullptr;
68 }
69
70 // Stores the size of the token in the first int
71 nh->data[0] = nhDataByteSize;
72 memcpy(&(nh->data[1]), halToken.data(), nhDataByteSize);
73 return nh;
74 }
75
76 } // namespace
77
78 namespace aidl::android::frameworks::automotive::display::implementation {
79
getDisplayIdList(std::vector<int64_t> * _aidl_return)80 ScopedAStatus CarDisplayProxy::getDisplayIdList(std::vector<int64_t>* _aidl_return) {
81 std::vector<::android::PhysicalDisplayId> displayIds =
82 SurfaceComposerClient::getPhysicalDisplayIds();
83 std::for_each(displayIds.begin(), displayIds.end(),
84 [_aidl_return](const ::android::PhysicalDisplayId& id) {
85 _aidl_return->push_back(id.value);
86 });
87
88 return ScopedAStatus::ok();
89 }
90
getDisplayInfo(int64_t id,DisplayDesc * _aidl_return)91 ScopedAStatus CarDisplayProxy::getDisplayInfo(int64_t id, DisplayDesc* _aidl_return) {
92 ::android::ui::DisplayMode displayMode;
93 ::android::ui::DisplayState displayState;
94 if (!getDisplayInfoFromSurfaceComposerClient(id, &displayMode, &displayState)) {
95 LOG(ERROR) << "Invalid display id = " << id;
96 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
97 }
98
99 DisplayDesc desc = {
100 .width = displayMode.resolution.width,
101 .height = displayMode.resolution.height,
102 .layer = displayState.layerStack.id,
103 .orientation = convert(displayState.orientation),
104 };
105 *_aidl_return = std::move(desc);
106 return ScopedAStatus::ok();
107 }
108
getDisplayInfoFromSurfaceComposerClient(int64_t id,::android::ui::DisplayMode * displayMode,::android::ui::DisplayState * displayState)109 ::android::sp<::android::IBinder> CarDisplayProxy::getDisplayInfoFromSurfaceComposerClient(
110 int64_t id, ::android::ui::DisplayMode* displayMode,
111 ::android::ui::DisplayState* displayState) {
112 ::android::sp<::android::IBinder> displayToken;
113 std::optional<::android::PhysicalDisplayId> displayId =
114 ::android::DisplayId::fromValue<::android::PhysicalDisplayId>(id);
115 if (!displayId) {
116 LOG(ERROR) << "Failed to get a valid display name";
117 return nullptr;
118 }
119
120 displayToken = SurfaceComposerClient::getPhysicalDisplayToken(*displayId);
121 if (!displayToken) {
122 LOG(ERROR) << "Failed to get a valid display token";
123 return nullptr;
124 }
125
126 ::android::status_t status =
127 SurfaceComposerClient::getActiveDisplayMode(displayToken, displayMode);
128 if (status != ::android::NO_ERROR) {
129 LOG(WARNING) << "Failed to read current mode of the display " << id;
130 }
131
132 status = SurfaceComposerClient::getDisplayState(displayToken, displayState);
133 if (status != ::android::NO_ERROR) {
134 LOG(WARNING) << "Failed to read current state of the display " << id;
135 }
136
137 return displayToken;
138 }
139
getHGraphicBufferProducer(int64_t id,NativeHandle * _aidl_return)140 ScopedAStatus CarDisplayProxy::getHGraphicBufferProducer(int64_t id, NativeHandle* _aidl_return) {
141 ::android::sp<::android::IBinder> displayToken;
142 ::android::sp<::android::SurfaceControl> surfaceControl;
143
144 if (auto status = getDisplayRecord(id, &displayToken, &surfaceControl); !status.isOk()) {
145 return status;
146 }
147
148 // SurfaceControl::getSurface() is guaranteed to be non-null.
149 auto targetSurface = surfaceControl->getSurface();
150 auto igbp = targetSurface->getIGraphicBufferProducer();
151 auto hgbp =
152 new ::android::hardware::graphics::bufferqueue::V2_0::utils::B2HGraphicBufferProducer(
153 igbp);
154
155 ::android::HalToken halToken;
156 if (!::android::createHalToken(hgbp, &halToken)) {
157 LOG(ERROR) << "Failed to create a hal token";
158 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
159 }
160
161 native_handle_t* handle = convertHalTokenToNativeHandle(halToken);
162 auto scope_guard = ::android::base::make_scope_guard([handle]() {
163 native_handle_close(handle);
164 native_handle_delete(handle);
165 });
166 if (handle == nullptr) {
167 LOG(ERROR) << "Failed to create a handle, errno = " << strerror(errno);
168 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
169 }
170
171 *_aidl_return = ::android::dupToAidl(handle);
172 return ScopedAStatus::ok();
173 }
174
getSurface(int64_t id,android::view::Surface * _aidl_return)175 ScopedAStatus CarDisplayProxy::getSurface(int64_t id, android::view::Surface* _aidl_return) {
176 if (_aidl_return == nullptr) {
177 LOG(ERROR) << __FUNCTION__
178 << " is called with an invalid aidl::android::view::Surface object.";
179 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
180 }
181
182 auto it = mSurfaceList.find(id);
183 if (it != mSurfaceList.end() && ::android::Surface::isValid(mSurfaceList[id])) {
184 LOG(DEBUG) << __FUNCTION__ << " reuses an existing surface for a display " << id;
185 _aidl_return->reset(mSurfaceList[id].get());
186 return ScopedAStatus::ok();
187 }
188
189 ::android::sp<::android::IBinder> displayToken;
190 ::android::sp<::android::SurfaceControl> surfaceControl;
191 if (auto status = getDisplayRecord(id, &displayToken, &surfaceControl); !status.isOk()) {
192 return status;
193 }
194
195 // Retrieve a Surface associated with a target display.
196 ::android::sp<::android::Surface> targetSurface = surfaceControl->getSurface();
197 if (!targetSurface || !::android::Surface::isValid(targetSurface)) {
198 LOG(ERROR) << "Created Surface is invalid, " << targetSurface.get();
199 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
200 }
201
202 // Bookkeep a retrieved Surface object and pass it to the client as a format
203 // of ::aidl::android::view::Surface, which is alias to
204 // ::aidl::android::hardware::NativeWindow.
205 mSurfaceList.insert_or_assign(id, targetSurface);
206 _aidl_return->reset(targetSurface.get());
207 return ScopedAStatus::ok();
208 }
209
hideWindow(int64_t id)210 ScopedAStatus CarDisplayProxy::hideWindow(int64_t id) {
211 auto it = mDisplays.find(id);
212 if (it == mDisplays.end()) {
213 LOG(DEBUG) << __FUNCTION__ << ": Invalid display id, " << id;
214 return ScopedAStatus::ok();
215 }
216
217 auto status = SurfaceComposerClient::Transaction{}.hide(it->second.surfaceControl).apply();
218 if (status != ::android::NO_ERROR) {
219 LOG(DEBUG) << __FUNCTION__
220 << ": Failed to hide a surface, status = " << ::android::statusToString(status);
221 }
222
223 return ScopedAStatus::ok();
224 }
225
showWindow(int64_t id)226 ScopedAStatus CarDisplayProxy::showWindow(int64_t id) {
227 auto it = mDisplays.find(id);
228 if (it == mDisplays.end()) {
229 LOG(ERROR) << __FUNCTION__ << ": Invalid display id, " << id;
230 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
231 }
232
233 const auto& displayToken = it->second.token;
234 const auto& surfaceControl = it->second.surfaceControl;
235 ::android::ui::DisplayState displayState;
236 ::android::status_t status =
237 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
238 if (status != ::android::NO_ERROR) {
239 LOG(ERROR) << "Failed to read current state of the display " << id
240 << ", status = " << ::android::statusToString(status);
241 return ScopedAStatus::fromStatus(status);
242 }
243
244 SurfaceComposerClient::Transaction t;
245 t.setDisplayLayerStack(displayToken, displayState.layerStack);
246 t.setLayerStack(surfaceControl, displayState.layerStack);
247
248 status = t.setLayer(surfaceControl, kSurfaceZOrder).show(surfaceControl).apply();
249 if (status != ::android::NO_ERROR) {
250 LOG(ERROR) << "Failed to set a layer";
251 return ScopedAStatus::fromStatus(status);
252 }
253
254 return ScopedAStatus::ok();
255 }
256
getDisplayRecord(int64_t id,::android::sp<::android::IBinder> * outDisplayToken,::android::sp<::android::SurfaceControl> * outSurfaceControl)257 ScopedAStatus CarDisplayProxy::getDisplayRecord(
258 int64_t id, ::android::sp<::android::IBinder>* outDisplayToken,
259 ::android::sp<::android::SurfaceControl>* outSurfaceControl) {
260 auto it = mDisplays.find(id);
261 if (it != mDisplays.end()) {
262 *outDisplayToken = mDisplays[id].token;
263 *outSurfaceControl = mDisplays[id].surfaceControl;
264 return ScopedAStatus::ok();
265 }
266
267 ::android::sp<::android::IBinder> displayToken;
268 ::android::sp<::android::SurfaceControl> surfaceControl;
269 ::android::ui::DisplayMode displayMode;
270 ::android::ui::DisplayState displayState;
271 displayToken = getDisplayInfoFromSurfaceComposerClient(id, &displayMode, &displayState);
272 if (!displayToken) {
273 LOG(ERROR) << "Failed to read display information from SurfaceComposerClient.";
274 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
275 }
276
277 auto displayWidth = displayMode.resolution.getWidth();
278 auto displayHeight = displayMode.resolution.getHeight();
279 if ((displayState.orientation != ::android::ui::ROTATION_0) &&
280 (displayState.orientation != ::android::ui::ROTATION_180)) {
281 std::swap(displayWidth, displayHeight);
282 }
283
284 ::android::sp<SurfaceComposerClient> client = new SurfaceComposerClient();
285 ::android::status_t status = client->initCheck();
286 if (status != ::android::NO_ERROR) {
287 LOG(ERROR) << "SurfaceComposerClient::initCheck() fails, error = "
288 << ::android::statusToString(status);
289 return ScopedAStatus::fromStatus(status);
290 }
291
292 surfaceControl =
293 client->createSurface(::android::String8::format("CarDisplayProxy::%" PRIx64, id),
294 displayWidth, displayHeight, ::android::PIXEL_FORMAT_RGBX_8888,
295 ::android::ISurfaceComposerClient::eOpaque);
296 if (!surfaceControl || !surfaceControl->isValid()) {
297 LOG(ERROR) << "Failed to create a SurfaceControl";
298 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
299 }
300
301 *outDisplayToken = displayToken;
302 *outSurfaceControl = surfaceControl;
303
304 DisplayRecord rec = {displayToken, surfaceControl};
305 mDisplays.insert_or_assign(id, std::move(rec));
306
307 return ScopedAStatus::ok();
308 }
309
310 } // namespace aidl::android::frameworks::automotive::display::implementation
311