• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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