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