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(Connection::CONNECTED, pendingEvents[0].connection);
45 EXPECT_EQ(hwcDisplayId2, pendingEvents[1].hwcDisplayId);
46 EXPECT_EQ(Connection::DISCONNECTED, pendingEvents[1].connection);
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,ignoresDuplicateDisconnection)62 TEST_F(HotplugTest, ignoresDuplicateDisconnection) {
63 // Inject a primary display.
64 PrimaryDisplayVariant::injectHwcDisplay(this);
65
66 using ExternalDisplay = ExternalDisplayVariant;
67 ExternalDisplay::setupHwcHotplugCallExpectations(this);
68 ExternalDisplay::setupHwcGetActiveConfigCallExpectations(this);
69
70 // TODO(b/241286146): Remove this unnecessary call.
71 EXPECT_CALL(*mComposer,
72 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
73 .WillOnce(Return(Error::NONE));
74
75 // A single commit should be scheduled for both configure calls.
76 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
77
78 ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
79 mFlinger.configure();
80
81 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
82
83 // Disconnecting a display that was already disconnected should be a no-op.
84 ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
85 ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
86 ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
87 mFlinger.configure();
88
89 // The display should be scheduled for removal during the next commit. At this point, it should
90 // still exist but be marked as disconnected.
91 EXPECT_TRUE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
92 EXPECT_FALSE(mFlinger.getHwComposer().isConnected(ExternalDisplay::DISPLAY_ID::get()));
93 }
94
TEST_F(HotplugTest,rejectsHotplugIfFailedToLoadDisplayModes)95 TEST_F(HotplugTest, rejectsHotplugIfFailedToLoadDisplayModes) {
96 SET_FLAG_FOR_TEST(flags::connected_display, true);
97
98 // Inject a primary display.
99 PrimaryDisplayVariant::injectHwcDisplay(this);
100
101 using ExternalDisplay = ExternalDisplayVariant;
102 constexpr bool kFailedHotplug = true;
103 ExternalDisplay::setupHwcHotplugCallExpectations<kFailedHotplug>(this);
104
105 EXPECT_CALL(*mEventThread,
106 onHotplugConnectionError(static_cast<int32_t>(DisplayHotplugEvent::ERROR_UNKNOWN)))
107 .Times(1);
108
109 // Simulate a connect event that fails to load display modes due to HWC already having
110 // disconnected the display but SF yet having to process the queued disconnect event.
111 EXPECT_CALL(*mComposer, getActiveConfig(ExternalDisplay::HWC_DISPLAY_ID, _))
112 .WillRepeatedly(Return(Error::BAD_DISPLAY));
113
114 // TODO(b/241286146): Remove this unnecessary call.
115 EXPECT_CALL(*mComposer,
116 setVsyncEnabled(ExternalDisplay::HWC_DISPLAY_ID, IComposerClient::Vsync::DISABLE))
117 .WillOnce(Return(Error::NONE));
118
119 EXPECT_CALL(*mFlinger.scheduler(), scheduleFrame()).Times(1);
120
121 ExternalDisplay::injectPendingHotplugEvent(this, Connection::CONNECTED);
122 mFlinger.configure();
123
124 // The hotplug should be rejected, so no HWComposer::DisplayData should be created.
125 EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
126
127 // Disconnecting a display that does not exist should be a no-op.
128 ExternalDisplay::injectPendingHotplugEvent(this, Connection::DISCONNECTED);
129 mFlinger.configure();
130
131 EXPECT_FALSE(hasPhysicalHwcDisplay(ExternalDisplay::HWC_DISPLAY_ID));
132 }
133
134 } // namespace android
135