1 /*
2 * Copyright 2020 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 #undef LOG_TAG
18 #define LOG_TAG "LibSurfaceFlingerUnittests"
19
20 #include <aidl/android/hardware/graphics/common/DisplayHotplugEvent.h>
21 #include <com_android_graphics_surfaceflinger_flags.h>
22 #include <common/test/FlagUtils.h>
23 #include "DisplayTransactionTestHelpers.h"
24
25 using namespace com::android::graphics::surfaceflinger;
26 using ::aidl::android::hardware::graphics::common::DisplayHotplugEvent;
27
28 namespace android {
29
30 class HotplugTest : public DisplayTransactionTest {};
31
TEST_F(HotplugTest,schedulesConfigureToProcessHotplugEvents)32 TEST_F(HotplugTest, schedulesConfigureToProcessHotplugEvents) {
33 EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(2);
34
35 constexpr HWDisplayId hwcDisplayId1 = 456;
36 mFlinger.onComposerHalHotplugEvent(hwcDisplayId1, DisplayHotplugEvent::CONNECTED);
37
38 constexpr HWDisplayId hwcDisplayId2 = 654;
39 mFlinger.onComposerHalHotplugEvent(hwcDisplayId2, DisplayHotplugEvent::DISCONNECTED);
40
41 const auto& pendingEvents = mFlinger.mutablePendingHotplugEvents();
42 ASSERT_EQ(2u, pendingEvents.size());
43 EXPECT_EQ(hwcDisplayId1, pendingEvents[0].hwcDisplayId);
44 EXPECT_EQ(HWComposer::HotplugEvent::Connected, pendingEvents[0].event);
45 EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
46 EXPECT_EQ(HWComposer::HotplugEvent::Disconnected, pendingEvents[1].event);
47 }
48
TEST_F(HotplugTest,schedulesFrameToCommitDisplayTransaction)49 TEST_F(HotplugTest, schedulesFrameToCommitDisplayTransaction) {
50 EXPECT_CALL(*mFlinger.scheduler(), scheduleConfigure()).Times(1);
51 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
52
53 constexpr HWDisplayId displayId1 = 456;
54 mFlinger.onComposerHalHotplugEvent(displayId1, DisplayHotplugEvent::DISCONNECTED);
55 mFlinger.configure();
56
57 // The configure stage should consume the hotplug queue and produce a display transaction.
58 EXPECT_TRUE(mFlinger.mutablePendingHotplugEvents().empty());
59 EXPECT_TRUE(hasTransactionFlagSet(eDisplayTransactionNeeded));
60 }
61
TEST_F(HotplugTest,createsDisplaySnapshotsForDisplaysWithIdentificationData)62 TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithIdentificationData) {
63 // Configure a primary display with identification data.
64 using PrimaryDisplay = InnerDisplayVariant;
65 PrimaryDisplay::setupHwcHotplugCallExpectations(this);
66 PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
67 PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
68
69 // TODO: b/241286146 - Remove this unnecessary call.
70 EXPECT_CALL(*mComposer,
71 setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
72 .WillOnce(Return(Error::NONE));
73
74 // A single commit should be scheduled.
75 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
76
77 mFlinger.configure();
78
79 // Configure an external display with identification info.
80 using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
81 ExternalDisplay::setupHwcHotplugCallExpectations(this);
82 ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
83 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
84
85 // TODO: b/241286146 - Remove this unnecessary call.
86 EXPECT_CALL(*mComposer,
87 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
88 .WillOnce(Return(Error::NONE));
89
90 mFlinger.configure();
91
92 EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
93 const auto primaryDisplayId = asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get());
94 ASSERT_TRUE(primaryDisplayId);
95 EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*primaryDisplayId));
96 const auto primaryDisplayIdOpt =
97 mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
98 ASSERT_TRUE(primaryDisplayIdOpt.has_value());
99 const auto primaryPhysicalDisplayOpt =
100 mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
101 ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
102 const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
103 EXPECT_EQ(*primaryDisplayId, primaryDisplaySnapshotRef.get().displayId());
104 EXPECT_EQ(PrimaryDisplay::PORT::value, primaryDisplaySnapshotRef.get().port());
105 EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
106 primaryDisplaySnapshotRef.get().connectionType());
107
108 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
109 const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
110 ASSERT_TRUE(externalDisplayId);
111 EXPECT_TRUE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
112 const auto externalDisplayIdOpt =
113 mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
114 ASSERT_TRUE(externalDisplayIdOpt.has_value());
115 const auto externalPhysicalDisplayOpt =
116 mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
117 ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
118 const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
119 EXPECT_EQ(*externalDisplayId, externalDisplaySnapshotRef.get().displayId());
120 EXPECT_EQ(ExternalDisplay::PORT::value, externalDisplaySnapshotRef.get().port());
121 EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
122 externalDisplaySnapshotRef.get().connectionType());
123 }
124
TEST_F(HotplugTest,createsDisplaySnapshotsForDisplaysWithoutIdentificationData)125 TEST_F(HotplugTest, createsDisplaySnapshotsForDisplaysWithoutIdentificationData) {
126 // Configure a primary display without identification data.
127 using PrimaryDisplay = PrimaryDisplayVariant;
128 PrimaryDisplay::setupHwcHotplugCallExpectations(this);
129 PrimaryDisplay::setupHwcGetActiveConfigCallExpectations(this);
130 PrimaryDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
131
132 // TODO: b/241286146 - Remove this unnecessary call.
133 EXPECT_CALL(*mComposer,
134 setVsyncEnabled(PrimaryDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
135 .WillOnce(Return(Error::NONE));
136
137 // A single commit should be scheduled.
138 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
139
140 mFlinger.configure();
141
142 // Configure an external display with identification info.
143 using ExternalDisplay = ExternalDisplayWithIdentificationVariant<>;
144 ExternalDisplay::setupHwcHotplugCallExpectations(this);
145 ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
146 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
147
148 // TODO: b/241286146 - Remove this unnecessary call.
149 EXPECT_CALL(*mComposer,
150 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
151 .WillOnce(Return(Error::NONE));
152
153 mFlinger.configure();
154
155 // Both ID and port are expected to be equal to 0 for primary internal display due to no
156 // identification data.
157 constexpr uint8_t primaryInternalDisplayPort = 0u;
158 constexpr PhysicalDisplayId primaryInternalDisplayId =
159 PhysicalDisplayId::fromPort(primaryInternalDisplayPort);
160 EXPECT_TRUE(hasPhysicalHwcDisplay(PrimaryDisplay::HWC_DISPLAY_ID));
161 ASSERT_EQ(primaryInternalDisplayId, asPhysicalDisplayId(PrimaryDisplay::DISPLAY_ID::get()));
162 EXPECT_TRUE(mFlinger.getHwComposer().isConnected(primaryInternalDisplayId));
163 const auto primaryDisplayIdOpt =
164 mFlinger.getHwComposer().toPhysicalDisplayId(PrimaryDisplay::HWC_DISPLAY_ID);
165 ASSERT_TRUE(primaryDisplayIdOpt.has_value());
166 const auto primaryPhysicalDisplayOpt =
167 mFlinger.physicalDisplays().get(primaryDisplayIdOpt.value());
168 ASSERT_TRUE(primaryPhysicalDisplayOpt.has_value());
169 const auto primaryDisplaySnapshotRef = primaryPhysicalDisplayOpt->get().snapshotRef();
170 EXPECT_EQ(primaryInternalDisplayId, primaryDisplaySnapshotRef.get().displayId());
171 EXPECT_EQ(primaryInternalDisplayPort, primaryDisplaySnapshotRef.get().port());
172 EXPECT_EQ(PrimaryDisplay::CONNECTION_TYPE::value,
173 primaryDisplaySnapshotRef.get().connectionType());
174
175 // Even though the external display has identification data available, the lack of data for the
176 // internal display has set of the legacy multi-display mode in SF and therefore the external
177 // display's identification data will be ignored.
178 // Both ID and port are expected to be equal to 1 for external internal display.
179 constexpr uint8_t externalDisplayPort = 1u;
180 constexpr PhysicalDisplayId externalDisplayId =
181 PhysicalDisplayId::fromPort(externalDisplayPort);
182 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
183 EXPECT_TRUE(mFlinger.getHwComposer().isConnected(externalDisplayId));
184 const auto externalDisplayIdOpt =
185 mFlinger.getHwComposer().toPhysicalDisplayId(ExternalDisplay::HWC_DISPLAY_ID);
186 ASSERT_TRUE(externalDisplayIdOpt.has_value());
187 const auto externalPhysicalDisplayOpt =
188 mFlinger.physicalDisplays().get(externalDisplayIdOpt.value());
189 ASSERT_TRUE(externalPhysicalDisplayOpt.has_value());
190 const auto externalDisplaySnapshotRef = externalPhysicalDisplayOpt->get().snapshotRef();
191 EXPECT_EQ(externalDisplayId, externalDisplaySnapshotRef.get().displayId());
192 EXPECT_EQ(externalDisplayPort, externalDisplaySnapshotRef.get().port());
193 EXPECT_EQ(ExternalDisplay::CONNECTION_TYPE::value,
194 externalDisplaySnapshotRef.get().connectionType());
195 }
196
TEST_F(HotplugTest,ignoresDuplicateDisconnection)197 TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
198 // Inject a primary display.
199 PrimaryDisplayVariant::injectHwcDisplay(this);
200
201 using ExternalDisplay = ExternalDisplayVariant;
202 ExternalDisplay::setupHwcHotplugCallExpectations(this);
203 ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
204
205 // TODO: b/241286146 - Remove this unnecessary call.
206 EXPECT_CALL(*mComposer,
207 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
208 .WillOnce(Return(Error::NONE));
209
210 // A single commit should be scheduled for both configure calls.
211 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
212
213 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
214 mFlinger.configure();
215
216 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
217
218 // Disconnecting a display that was already disconnected should be a no-op.
219 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
220 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
221 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
222 mFlinger.configure();
223
224 // The display should be scheduled for removal during the next commit. At this point, it should
225 // still exist but be marked as disconnected.
226 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
227 const auto externalDisplayId = asPhysicalDisplayId(ExternalDisplay::DISPLAY_ID::get());
228 ASSERT_TRUE(externalDisplayId);
229 EXPECT_FALSE(mFlinger.getHwComposer().isConnected(*externalDisplayId));
230 }
231
TEST_F(HotplugTest,rejectsHotplugIfFailedToLoadDisplayModes)232 TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
233 // Inject a primary display.
234 PrimaryDisplayVariant::injectHwcDisplay(this);
235
236 using ExternalDisplay = ExternalDisplayVariant;
237 constexpr bool kFailedHotplug = true;
238 ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
239
240 EXPECT_CALL(*mEventThread,
241 onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)))
242 .Times(1);
243
244 // Simulate a connect event that fails to load display modes due to HWC already having
245 // disconnected the display but SF yet having to process the queued disconnect event.
246 EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
247 .WillRepeatedly(Return(Error::BAD_DISPLAY));
248
249 // TODO: b/241286146 - Remove this unnecessary call.
250 EXPECT_CALL(*mComposer,
251 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
252 .WillOnce(Return(Error::NONE));
253
254 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
255
256 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
257 mFlinger.configure();
258
259 // The hotplug should be rejected, so no HWComposer::DisplayData should be created.
260 EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
261
262 // Disconnecting a display that does not exist should be a no-op.
263 ExternalDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
264 mFlinger.configure();
265
266 EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
267 }
268
TEST_F(HotplugTest,rejectsHotplugOnActivePortsDuplicate)269 TEST_F(HotplugTest, rejectsHotplugOnActivePortsDuplicate) {
270 // Inject a primary display.
271 PrimaryDisplayVariant::injectHwcDisplay(this);
272
273 // Second display should come up properly.
274 using SecondDisplay = ExternalDisplayWithIdentificationVariant<>;
275 SecondDisplay::setupHwcHotplugCallExpectations(this);
276 SecondDisplay::setupHwcGetActiveConfigCallExpectations(this);
277
278 // TODO: b/241286146 - Remove this unnecessary call.
279 EXPECT_CALL(*mComposer,
280 setVsyncEnabled(SecondDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
281 .WillOnce(Return(Error::NONE));
282
283 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame(_)).Times(1);
284
285 SecondDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
286 mFlinger.configure();
287
288 EXPECT_TRUE(hasPhysicalHwcDisplay(SecondDisplay::HWC_DISPLAY_ID));
289
290 // Third display will return the same port ID as the second, and the hotplug
291 // should fail.
292 constexpr HWDisplayId kHwDisplayId = 1234;
293 using DuplicatePortDisplay = ExternalDisplayWithIdentificationVariant<kHwDisplayId>;
294
295 // We expect display identification to be fetched correctly, since EDID and
296 // port are available and successfully retrieved from HAL.
297 EXPECT_CALL(*mComposer,
298 getDisplayIdentificationData(DuplicatePortDisplay::HWC_DISPLAY_ID, _, _))
299 .WillOnce(DoAll(SetArgPointee<1>(*DuplicatePortDisplay::PORT::value),
300 SetArgPointee<2>(getExternalEedid()), Return(Error::NONE)));
301
302 DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Connected);
303 mFlinger.configure();
304
305 // The hotplug should be rejected due to an attempt to connect a display to an already active
306 // port. No HWComposer::DisplayData should be created.
307 EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
308
309 // Disconnecting a display that was not successfully configured should be a no-op.
310 DuplicatePortDisplay::injectPendingHotplugEvent(this, HWComposer::HotplugEvent::Disconnected);
311 mFlinger.configure();
312
313 EXPECT_FALSE(hasPhysicalHwcDisplay(DuplicatePortDisplay::HWC_DISPLAY_ID));
314 }
315
316 } // namespace android
317