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