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 = SurfaceComposerClient::getPhysicalDisplayToken(
113 ::android::PhysicalDisplayId::fromValue(id));
114 if (!displayToken) {
115 LOG(ERROR) << "Failed to get a valid display token";
116 return nullptr;
117 }
118
119 ::android::status_t status =
120 SurfaceComposerClient::getActiveDisplayMode(displayToken, displayMode);
121 if (status != ::android::NO_ERROR) {
122 LOG(WARNING) << "Failed to read current mode of the display " << id;
123 }
124
125 status = SurfaceComposerClient::getDisplayState(displayToken, displayState);
126 if (status != ::android::NO_ERROR) {
127 LOG(WARNING) << "Failed to read current state of the display " << id;
128 }
129
130 return displayToken;
131 }
132
getHGraphicBufferProducer(int64_t id,NativeHandle * _aidl_return)133 ScopedAStatus CarDisplayProxy::getHGraphicBufferProducer(int64_t id, NativeHandle* _aidl_return) {
134 ::android::sp<::android::IBinder> displayToken;
135 ::android::sp<::android::SurfaceControl> surfaceControl;
136
137 if (auto status = getDisplayRecord(id, &displayToken, &surfaceControl); !status.isOk()) {
138 return status;
139 }
140
141 // SurfaceControl::getSurface() is guaranteed to be non-null.
142 auto targetSurface = surfaceControl->getSurface();
143 auto igbp = targetSurface->getIGraphicBufferProducer();
144 auto hgbp =
145 new ::android::hardware::graphics::bufferqueue::V2_0::utils::B2HGraphicBufferProducer(
146 igbp);
147
148 ::android::HalToken halToken;
149 if (!::android::createHalToken(hgbp, &halToken)) {
150 LOG(ERROR) << "Failed to create a hal token";
151 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
152 }
153
154 native_handle_t* handle = convertHalTokenToNativeHandle(halToken);
155 auto scope_guard = ::android::base::make_scope_guard([handle]() {
156 native_handle_close(handle);
157 native_handle_delete(handle);
158 });
159 if (handle == nullptr) {
160 LOG(ERROR) << "Failed to create a handle, errno = " << strerror(errno);
161 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
162 }
163
164 *_aidl_return = ::android::dupToAidl(handle);
165 return ScopedAStatus::ok();
166 }
167
getSurface(int64_t id,android::view::Surface * _aidl_return)168 ScopedAStatus CarDisplayProxy::getSurface(int64_t id, android::view::Surface* _aidl_return) {
169 if (_aidl_return == nullptr) {
170 LOG(ERROR) << __FUNCTION__
171 << " is called with an invalid aidl::android::view::Surface object.";
172 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
173 }
174
175 auto it = mSurfaceList.find(id);
176 if (it != mSurfaceList.end() && ::android::Surface::isValid(mSurfaceList[id])) {
177 LOG(DEBUG) << __FUNCTION__ << " reuses an existing surface for a display " << id;
178 _aidl_return->reset(mSurfaceList[id].get());
179 return ScopedAStatus::ok();
180 }
181
182 ::android::sp<::android::IBinder> displayToken;
183 ::android::sp<::android::SurfaceControl> surfaceControl;
184 if (auto status = getDisplayRecord(id, &displayToken, &surfaceControl); !status.isOk()) {
185 return status;
186 }
187
188 // Retrieve a Surface associated with a target display.
189 ::android::sp<::android::Surface> targetSurface = surfaceControl->getSurface();
190 if (!targetSurface || !::android::Surface::isValid(targetSurface)) {
191 LOG(ERROR) << "Created Surface is invalid, " << targetSurface.get();
192 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
193 }
194
195 // Bookkeep a retrieved Surface object and pass it to the client as a format
196 // of ::aidl::android::view::Surface, which is alias to
197 // ::aidl::android::hardware::NativeWindow.
198 mSurfaceList.insert_or_assign(id, targetSurface);
199 _aidl_return->reset(targetSurface.get());
200 return ScopedAStatus::ok();
201 }
202
hideWindow(int64_t id)203 ScopedAStatus CarDisplayProxy::hideWindow(int64_t id) {
204 auto it = mDisplays.find(id);
205 if (it == mDisplays.end()) {
206 LOG(DEBUG) << __FUNCTION__ << ": Invalid display id, " << id;
207 return ScopedAStatus::ok();
208 }
209
210 auto status = SurfaceComposerClient::Transaction{}.hide(it->second.surfaceControl).apply();
211 if (status != ::android::NO_ERROR) {
212 LOG(DEBUG) << __FUNCTION__
213 << ": Failed to hide a surface, status = " << ::android::statusToString(status);
214 }
215
216 return ScopedAStatus::ok();
217 }
218
showWindow(int64_t id)219 ScopedAStatus CarDisplayProxy::showWindow(int64_t id) {
220 auto it = mDisplays.find(id);
221 if (it == mDisplays.end()) {
222 LOG(ERROR) << __FUNCTION__ << ": Invalid display id, " << id;
223 return ScopedAStatus::fromStatus(STATUS_BAD_VALUE);
224 }
225
226 const auto& displayToken = it->second.token;
227 const auto& surfaceControl = it->second.surfaceControl;
228 ::android::ui::DisplayState displayState;
229 ::android::status_t status =
230 SurfaceComposerClient::getDisplayState(displayToken, &displayState);
231 if (status != ::android::NO_ERROR) {
232 LOG(ERROR) << "Failed to read current state of the display " << id
233 << ", status = " << ::android::statusToString(status);
234 return ScopedAStatus::fromStatus(status);
235 }
236
237 SurfaceComposerClient::Transaction t;
238 t.setDisplayLayerStack(displayToken, displayState.layerStack);
239 t.setLayerStack(surfaceControl, displayState.layerStack);
240
241 status = t.setLayer(surfaceControl, kSurfaceZOrder).show(surfaceControl).apply();
242 if (status != ::android::NO_ERROR) {
243 LOG(ERROR) << "Failed to set a layer";
244 return ScopedAStatus::fromStatus(status);
245 }
246
247 return ScopedAStatus::ok();
248 }
249
getDisplayRecord(int64_t id,::android::sp<::android::IBinder> * outDisplayToken,::android::sp<::android::SurfaceControl> * outSurfaceControl)250 ScopedAStatus CarDisplayProxy::getDisplayRecord(
251 int64_t id, ::android::sp<::android::IBinder>* outDisplayToken,
252 ::android::sp<::android::SurfaceControl>* outSurfaceControl) {
253 auto it = mDisplays.find(id);
254 if (it != mDisplays.end()) {
255 *outDisplayToken = mDisplays[id].token;
256 *outSurfaceControl = mDisplays[id].surfaceControl;
257 return ScopedAStatus::ok();
258 }
259
260 ::android::sp<::android::IBinder> displayToken;
261 ::android::sp<::android::SurfaceControl> surfaceControl;
262 ::android::ui::DisplayMode displayMode;
263 ::android::ui::DisplayState displayState;
264 displayToken = getDisplayInfoFromSurfaceComposerClient(id, &displayMode, &displayState);
265 if (!displayToken) {
266 LOG(ERROR) << "Failed to read display information from SurfaceComposerClient.";
267 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
268 }
269
270 auto displayWidth = displayMode.resolution.getWidth();
271 auto displayHeight = displayMode.resolution.getHeight();
272 if ((displayState.orientation != ::android::ui::ROTATION_0) &&
273 (displayState.orientation != ::android::ui::ROTATION_180)) {
274 std::swap(displayWidth, displayHeight);
275 }
276
277 ::android::sp<SurfaceComposerClient> client = new SurfaceComposerClient();
278 ::android::status_t status = client->initCheck();
279 if (status != ::android::NO_ERROR) {
280 LOG(ERROR) << "SurfaceComposerClient::initCheck() fails, error = "
281 << ::android::statusToString(status);
282 return ScopedAStatus::fromStatus(status);
283 }
284
285 surfaceControl =
286 client->createSurface(::android::String8::format("CarDisplayProxy::%" PRIx64, id),
287 displayWidth, displayHeight, ::android::PIXEL_FORMAT_RGBX_8888,
288 ::android::ISurfaceComposerClient::eOpaque);
289 if (!surfaceControl || !surfaceControl->isValid()) {
290 LOG(ERROR) << "Failed to create a SurfaceControl";
291 return ScopedAStatus::fromStatus(STATUS_FAILED_TRANSACTION);
292 }
293
294 *outDisplayToken = displayToken;
295 *outSurfaceControl = surfaceControl;
296
297 DisplayRecord rec = {displayToken, surfaceControl};
298 mDisplays.insert_or_assign(id, std::move(rec));
299
300 return ScopedAStatus::ok();
301 }
302
303 } // namespace aidl::android::frameworks::automotive::display::implementation
304