Loading include/input/DisplayTopologyGraph.h +17 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <android-base/result.h> #include <ftl/enum.h> #include <ui/LogicalDisplayId.h> Loading Loading @@ -58,8 +59,23 @@ struct DisplayTopologyGraph { std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph; std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; bool isValid() const; DisplayTopologyGraph() = default; std::string dump() const; // Builds the topology graph from components. // Returns error if a valid graph cannot be built from the supplied components. static base::Result<const DisplayTopologyGraph> create( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap); private: DisplayTopologyGraph( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap); }; } // namespace android libs/input/DisplayTopologyGraph.cpp +70 −24 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <com_android_input_flags.h> #include <ftl/enum.h> #include <input/DisplayTopologyGraph.h> #include <input/PrintTools.h> Loading @@ -27,6 +28,8 @@ #define INDENT " " namespace input_flags = com::android::input::flags; namespace android { namespace { Loading @@ -44,16 +47,19 @@ DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) { } } bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) { return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID && displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId); bool validatePrimaryDisplay(ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { return primaryDisplayId != ui::LogicalDisplayId::INVALID && displaysDensity.contains(primaryDisplayId); } bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) { for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { bool validateTopologyGraph( const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph) { for (const auto& [sourceDisplay, adjacentDisplays] : graph) { for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) { const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId); if (adjacentGraphIt == displayTopologyGraph.graph.end()) { const auto adjacentGraphIt = graph.find(adjacentDisplay.displayId); if (adjacentGraphIt == graph.end()) { LOG(ERROR) << "Missing adjacent display in topology graph: " << adjacentDisplay.displayId << " for source " << sourceDisplay; return false; Loading Loading @@ -90,9 +96,11 @@ bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyG return true; } bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) { for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) { bool validateDensities(const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { for (const auto& [sourceDisplay, adjacentDisplays] : graph) { if (!displaysDensity.contains(sourceDisplay)) { LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay; return false; } Loading @@ -113,22 +121,23 @@ std::string adjacentDisplayVectorToString( return dumpVector(adjacentDisplays, adjacentDisplayToString); } } // namespace std::string DisplayTopologyAdjacentDisplay::dump() const { std::string dump; dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " "offsetDp: %f}", displayId.val(), ftl::enum_string(position).c_str(), offsetDp); return dump; bool areTopologyGraphComponentsValid( ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { if (!input_flags::enable_display_topology_validation()) { return true; } bool DisplayTopologyGraph::isValid() const { return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) && validateDensities(*this); return validatePrimaryDisplay(primaryDisplayId, displaysDensity) && validateTopologyGraph(graph) && validateDensities(graph, displaysDensity); } std::string DisplayTopologyGraph::dump() const { std::string dumpTopologyGraphComponents( ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { std::string dump; dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val()); dump += base::StringPrintf("TopologyGraph:\n"); Loading @@ -141,4 +150,41 @@ std::string DisplayTopologyGraph::dump() const { return dump; } } // namespace std::string DisplayTopologyAdjacentDisplay::dump() const { std::string dump; dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " "offsetDp: %f}", displayId.val(), ftl::enum_string(position).c_str(), offsetDp); return dump; } DisplayTopologyGraph::DisplayTopologyGraph( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap) : primaryDisplayId(primaryDisplay), graph(std::move(adjacencyGraph)), displaysDensity(std::move(displaysDensityMap)) {} std::string DisplayTopologyGraph::dump() const { return dumpTopologyGraphComponents(primaryDisplayId, graph, displaysDensity); } base::Result<const DisplayTopologyGraph> DisplayTopologyGraph::create( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap) { if (areTopologyGraphComponentsValid(primaryDisplay, adjacencyGraph, displaysDensityMap)) { return DisplayTopologyGraph(primaryDisplay, std::move(adjacencyGraph), std::move(displaysDensityMap)); } return base::Error() << "Invalid display topology components: " << dumpTopologyGraphComponents(primaryDisplay, adjacencyGraph, displaysDensityMap); } } // namespace android services/inputflinger/tests/DisplayTopologyGraph_test.cpp +72 −52 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. */ #include <com_android_input_flags.h> #include <gtest/gtest.h> #include <input/DisplayTopologyGraph.h> Loading @@ -21,6 +22,8 @@ #include <string_view> #include <tuple> #include "ScopedFlagOverride.h" namespace android { namespace { Loading @@ -31,87 +34,104 @@ constexpr int DENSITY_MEDIUM = 160; } // namespace using DisplayTopologyAdjacentDisplayMap = std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>; using DisplayTopologyDisplaysDensityMapVector = std::unordered_map<ui::LogicalDisplayId, int>; using DisplayTopologyGraphTestFixtureParam = std::tuple<std::string_view /*name*/, DisplayTopologyGraph, bool /*isValid*/>; std::tuple<std::string_view /*name*/, ui::LogicalDisplayId /*primaryDisplayId*/, DisplayTopologyAdjacentDisplayMap, DisplayTopologyDisplaysDensityMapVector, bool /*isValid*/>; class DisplayTopologyGraphTestFixture : public testing::Test, public testing::WithParamInterface<DisplayTopologyGraphTestFixtureParam> {}; TEST_P(DisplayTopologyGraphTestFixture, DisplayTopologyGraphTest) { const auto& [_, displayTopology, isValid] = GetParam(); EXPECT_EQ(isValid, displayTopology.isValid()); SCOPED_FLAG_OVERRIDE(enable_display_topology_validation, true); auto [_, primaryDisplayId, graph, displaysDensity, isValid] = GetParam(); auto result = DisplayTopologyGraph::create(primaryDisplayId, std::move(graph), std::move(displaysDensity)); EXPECT_EQ(isValid, result.ok()); } INSTANTIATE_TEST_SUITE_P( DisplayTopologyGraphTest, DisplayTopologyGraphTestFixture, testing::Values( std::make_tuple( "InvalidPrimaryDisplay", DisplayTopologyGraph{.primaryDisplayId = ui::LogicalDisplayId::INVALID, .graph = {}, .displaysDensity = {}}, std::make_tuple("InvalidPrimaryDisplay", /*primaryDisplayId=*/ui::LogicalDisplayId::INVALID, /*graph=*/DisplayTopologyAdjacentDisplayMap{}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("PrimaryDisplayNotInGraph", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {}, .displaysDensity = {}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("DisplayDensityMissing", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {}}}, .displaysDensity = {}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {}}}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("ValidSingleDisplayTopology", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{ {DISPLAY_ID_1, DENSITY_MEDIUM}}, true), std::make_tuple( "MissingReverseEdge", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{ {DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "IncorrectReverseEdgeDirection", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::TOP, 0}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "IncorrectReverseEdgeOffset", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 10}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::BOTTOM, 20}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, DisplayTopologyPosition::BOTTOM, 20}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "ValidMultiDisplayTopology", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 10}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::BOTTOM, -10}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, DisplayTopologyPosition::BOTTOM, -10}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, true)), [](const testing::TestParamInfo<DisplayTopologyGraphTestFixtureParam>& p) { return std::string{std::get<0>(p.param)}; Loading services/inputflinger/tests/InputDispatcher_test.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -15493,14 +15493,18 @@ INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests { constexpr static int DENSITY_MEDIUM = 160; const DisplayTopologyGraph mTopology{.primaryDisplayId = DISPLAY_ID, .graph = {{DISPLAY_ID, {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}}, const DisplayTopologyGraph mTopology = DisplayTopologyGraph::create(/*primaryDisplayId=*/DISPLAY_ID, /*adjacencyGraph=*/ {{DISPLAY_ID, {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}}, {SECOND_DISPLAY_ID, {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}}, .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM}, {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}}; /*displaysDensity=*/ {{DISPLAY_ID, DENSITY_MEDIUM}, {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}) .value(); protected: void SetUp() override { services/inputflinger/tests/PointerChoreographer_test.cpp +47 −24 Original line number Diff line number Diff line Loading @@ -150,9 +150,11 @@ protected: // setDefaultMouseDisplayId without topology. // For this reason in tests we mock this behavior by creating topology with a single // display. mChoreographer.setDisplayTopology({.primaryDisplayId = displayId, .graph{{displayId, {}}}, .displaysDensity = {{displayId, DENSITY_MEDIUM}}}); mChoreographer.setDisplayTopology( DisplayTopologyGraph::create(/*primaryDisplayId=*/displayId, /*adjacencyGraph=*/{}, /*displaysDensity=*/{{displayId, DENSITY_MEDIUM}}) .value()); } else { mChoreographer.setDefaultMouseDisplayId(displayId); } Loading Loading @@ -2784,24 +2786,42 @@ protected: createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0), }; DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID, DisplayTopologyGraph mTopology = DisplayTopologyGraph:: create(/*primaryDisplay=*/DISPLAY_CENTER_ID, /*adjacencyGraph=*/ {{DISPLAY_CENTER_ID, {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f}, // Place a high density display on the left of DISPLAY_TOP_ID with 25 dp // gap // Place a high density display on the left of DISPLAY_TOP_ID with // 25 dp gap {DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f}, {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f}, {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f}, {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f}, {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}}, {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}, // Reverse edges {DISPLAY_TOP_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::BOTTOM, -50.0f}}}, {DISPLAY_HIGH_DENSITY_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::BOTTOM, 75.0f}}}, {DISPLAY_RIGHT_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::LEFT, -10.0f}}}, {DISPLAY_BOTTOM_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::TOP, -10.0f}}}, {DISPLAY_LEFT_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::RIGHT, -10.0f}}}, {DISPLAY_TOP_RIGHT_CORNER_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::LEFT, 90.0f}}}}, /*displaysDensityMap=*/ {{DISPLAY_CENTER_ID, DENSITY_MEDIUM}, {DISPLAY_TOP_ID, DENSITY_MEDIUM}, {DISPLAY_RIGHT_ID, DENSITY_MEDIUM}, {DISPLAY_BOTTOM_ID, DENSITY_MEDIUM}, {DISPLAY_LEFT_ID, DENSITY_MEDIUM}, {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM}, {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}}; {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}) .value(); }; TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture, Loading Loading @@ -2955,7 +2975,6 @@ protected: std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> topologyGraph; topologyGraph[primaryDisplayId] = {}; std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; displaysDensity[primaryDisplayId] = DENSITY_MEDIUM; Loading @@ -2969,9 +2988,13 @@ protected: .offsetDp = 0.0f}); displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM; previousDisplay = adjacentDisplayId; } mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity}); mChoreographer.setDisplayTopology(DisplayTopologyGraph::create(primaryDisplayId, std::move(topologyGraph), std::move(displaysDensity)) .value()); } }; Loading Loading @@ -3028,7 +3051,7 @@ TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, // Change the primary display to the third display setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID}); {SECOND_DISPLAY_ID, FIRST_DISPLAY_ID}); assertPointerControllerNotCreated(); pc->assertViewportSet(SECOND_DISPLAY_ID); Loading Loading
include/input/DisplayTopologyGraph.h +17 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #pragma once #include <android-base/result.h> #include <ftl/enum.h> #include <ui/LogicalDisplayId.h> Loading Loading @@ -58,8 +59,23 @@ struct DisplayTopologyGraph { std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> graph; std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; bool isValid() const; DisplayTopologyGraph() = default; std::string dump() const; // Builds the topology graph from components. // Returns error if a valid graph cannot be built from the supplied components. static base::Result<const DisplayTopologyGraph> create( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap); private: DisplayTopologyGraph( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap); }; } // namespace android
libs/input/DisplayTopologyGraph.cpp +70 −24 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android-base/logging.h> #include <android-base/stringprintf.h> #include <com_android_input_flags.h> #include <ftl/enum.h> #include <input/DisplayTopologyGraph.h> #include <input/PrintTools.h> Loading @@ -27,6 +28,8 @@ #define INDENT " " namespace input_flags = com::android::input::flags; namespace android { namespace { Loading @@ -44,16 +47,19 @@ DisplayTopologyPosition getOppositePosition(DisplayTopologyPosition position) { } } bool validatePrimaryDisplay(const android::DisplayTopologyGraph& displayTopologyGraph) { return displayTopologyGraph.primaryDisplayId != ui::LogicalDisplayId::INVALID && displayTopologyGraph.graph.contains(displayTopologyGraph.primaryDisplayId); bool validatePrimaryDisplay(ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { return primaryDisplayId != ui::LogicalDisplayId::INVALID && displaysDensity.contains(primaryDisplayId); } bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyGraph) { for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { bool validateTopologyGraph( const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph) { for (const auto& [sourceDisplay, adjacentDisplays] : graph) { for (const DisplayTopologyAdjacentDisplay& adjacentDisplay : adjacentDisplays) { const auto adjacentGraphIt = displayTopologyGraph.graph.find(adjacentDisplay.displayId); if (adjacentGraphIt == displayTopologyGraph.graph.end()) { const auto adjacentGraphIt = graph.find(adjacentDisplay.displayId); if (adjacentGraphIt == graph.end()) { LOG(ERROR) << "Missing adjacent display in topology graph: " << adjacentDisplay.displayId << " for source " << sourceDisplay; return false; Loading Loading @@ -90,9 +96,11 @@ bool validateTopologyGraph(const android::DisplayTopologyGraph& displayTopologyG return true; } bool validateDensities(const android::DisplayTopologyGraph& displayTopologyGraph) { for (const auto& [sourceDisplay, adjacentDisplays] : displayTopologyGraph.graph) { if (!displayTopologyGraph.displaysDensity.contains(sourceDisplay)) { bool validateDensities(const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { for (const auto& [sourceDisplay, adjacentDisplays] : graph) { if (!displaysDensity.contains(sourceDisplay)) { LOG(ERROR) << "Missing density value in topology graph for display: " << sourceDisplay; return false; } Loading @@ -113,22 +121,23 @@ std::string adjacentDisplayVectorToString( return dumpVector(adjacentDisplays, adjacentDisplayToString); } } // namespace std::string DisplayTopologyAdjacentDisplay::dump() const { std::string dump; dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " "offsetDp: %f}", displayId.val(), ftl::enum_string(position).c_str(), offsetDp); return dump; bool areTopologyGraphComponentsValid( ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { if (!input_flags::enable_display_topology_validation()) { return true; } bool DisplayTopologyGraph::isValid() const { return validatePrimaryDisplay(*this) && validateTopologyGraph(*this) && validateDensities(*this); return validatePrimaryDisplay(primaryDisplayId, displaysDensity) && validateTopologyGraph(graph) && validateDensities(graph, displaysDensity); } std::string DisplayTopologyGraph::dump() const { std::string dumpTopologyGraphComponents( ui::LogicalDisplayId primaryDisplayId, const std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>& graph, const std::unordered_map<ui::LogicalDisplayId, int>& displaysDensity) { std::string dump; dump += base::StringPrintf("PrimaryDisplayId: %d\n", primaryDisplayId.val()); dump += base::StringPrintf("TopologyGraph:\n"); Loading @@ -141,4 +150,41 @@ std::string DisplayTopologyGraph::dump() const { return dump; } } // namespace std::string DisplayTopologyAdjacentDisplay::dump() const { std::string dump; dump += base::StringPrintf("DisplayTopologyAdjacentDisplay: {displayId: %d, position: %s, " "offsetDp: %f}", displayId.val(), ftl::enum_string(position).c_str(), offsetDp); return dump; } DisplayTopologyGraph::DisplayTopologyGraph( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap) : primaryDisplayId(primaryDisplay), graph(std::move(adjacencyGraph)), displaysDensity(std::move(displaysDensityMap)) {} std::string DisplayTopologyGraph::dump() const { return dumpTopologyGraphComponents(primaryDisplayId, graph, displaysDensity); } base::Result<const DisplayTopologyGraph> DisplayTopologyGraph::create( ui::LogicalDisplayId primaryDisplay, std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>&& adjacencyGraph, std::unordered_map<ui::LogicalDisplayId, int>&& displaysDensityMap) { if (areTopologyGraphComponentsValid(primaryDisplay, adjacencyGraph, displaysDensityMap)) { return DisplayTopologyGraph(primaryDisplay, std::move(adjacencyGraph), std::move(displaysDensityMap)); } return base::Error() << "Invalid display topology components: " << dumpTopologyGraphComponents(primaryDisplay, adjacencyGraph, displaysDensityMap); } } // namespace android
services/inputflinger/tests/DisplayTopologyGraph_test.cpp +72 −52 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. */ #include <com_android_input_flags.h> #include <gtest/gtest.h> #include <input/DisplayTopologyGraph.h> Loading @@ -21,6 +22,8 @@ #include <string_view> #include <tuple> #include "ScopedFlagOverride.h" namespace android { namespace { Loading @@ -31,87 +34,104 @@ constexpr int DENSITY_MEDIUM = 160; } // namespace using DisplayTopologyAdjacentDisplayMap = std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>>; using DisplayTopologyDisplaysDensityMapVector = std::unordered_map<ui::LogicalDisplayId, int>; using DisplayTopologyGraphTestFixtureParam = std::tuple<std::string_view /*name*/, DisplayTopologyGraph, bool /*isValid*/>; std::tuple<std::string_view /*name*/, ui::LogicalDisplayId /*primaryDisplayId*/, DisplayTopologyAdjacentDisplayMap, DisplayTopologyDisplaysDensityMapVector, bool /*isValid*/>; class DisplayTopologyGraphTestFixture : public testing::Test, public testing::WithParamInterface<DisplayTopologyGraphTestFixtureParam> {}; TEST_P(DisplayTopologyGraphTestFixture, DisplayTopologyGraphTest) { const auto& [_, displayTopology, isValid] = GetParam(); EXPECT_EQ(isValid, displayTopology.isValid()); SCOPED_FLAG_OVERRIDE(enable_display_topology_validation, true); auto [_, primaryDisplayId, graph, displaysDensity, isValid] = GetParam(); auto result = DisplayTopologyGraph::create(primaryDisplayId, std::move(graph), std::move(displaysDensity)); EXPECT_EQ(isValid, result.ok()); } INSTANTIATE_TEST_SUITE_P( DisplayTopologyGraphTest, DisplayTopologyGraphTestFixture, testing::Values( std::make_tuple( "InvalidPrimaryDisplay", DisplayTopologyGraph{.primaryDisplayId = ui::LogicalDisplayId::INVALID, .graph = {}, .displaysDensity = {}}, std::make_tuple("InvalidPrimaryDisplay", /*primaryDisplayId=*/ui::LogicalDisplayId::INVALID, /*graph=*/DisplayTopologyAdjacentDisplayMap{}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("PrimaryDisplayNotInGraph", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {}, .displaysDensity = {}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("DisplayDensityMissing", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {}}}, .displaysDensity = {}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {}}}, /*displaysDensity=*/DisplayTopologyDisplaysDensityMapVector{}, false), std::make_tuple("ValidSingleDisplayTopology", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{ {DISPLAY_ID_1, DENSITY_MEDIUM}}, true), std::make_tuple( "MissingReverseEdge", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{ {DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "IncorrectReverseEdgeDirection", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 0}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::TOP, 0}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "IncorrectReverseEdgeOffset", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 10}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::BOTTOM, 20}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, DisplayTopologyPosition::BOTTOM, 20}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, false), std::make_tuple( "ValidMultiDisplayTopology", DisplayTopologyGraph{.primaryDisplayId = DISPLAY_ID_1, .graph = {{DISPLAY_ID_1, /*primaryDisplayId=*/DISPLAY_ID_1, /*graph=*/ DisplayTopologyAdjacentDisplayMap{{DISPLAY_ID_1, {{DISPLAY_ID_2, DisplayTopologyPosition::TOP, 10}}}, {DISPLAY_ID_2, {{DISPLAY_ID_1, DisplayTopologyPosition::BOTTOM, -10}}}}, .displaysDensity = {{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}}, DisplayTopologyPosition::BOTTOM, -10}}}}, /*displaysDensity=*/ DisplayTopologyDisplaysDensityMapVector{{DISPLAY_ID_1, DENSITY_MEDIUM}, {DISPLAY_ID_2, DENSITY_MEDIUM}}, true)), [](const testing::TestParamInfo<DisplayTopologyGraphTestFixtureParam>& p) { return std::string{std::get<0>(p.param)}; Loading
services/inputflinger/tests/InputDispatcher_test.cpp +12 −8 Original line number Diff line number Diff line Loading @@ -15493,14 +15493,18 @@ INSTANTIATE_TEST_SUITE_P(WithAndWithoutTransfer, TransferOrDontTransferFixture, class InputDispatcherConnectedDisplayTest : public InputDispatcherDragTests { constexpr static int DENSITY_MEDIUM = 160; const DisplayTopologyGraph mTopology{.primaryDisplayId = DISPLAY_ID, .graph = {{DISPLAY_ID, {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}}, const DisplayTopologyGraph mTopology = DisplayTopologyGraph::create(/*primaryDisplayId=*/DISPLAY_ID, /*adjacencyGraph=*/ {{DISPLAY_ID, {{SECOND_DISPLAY_ID, DisplayTopologyPosition::TOP, 0.0f}}}, {SECOND_DISPLAY_ID, {{DISPLAY_ID, DisplayTopologyPosition::BOTTOM, 0.0f}}}}, .displaysDensity = {{DISPLAY_ID, DENSITY_MEDIUM}, {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}}; /*displaysDensity=*/ {{DISPLAY_ID, DENSITY_MEDIUM}, {SECOND_DISPLAY_ID, DENSITY_MEDIUM}}) .value(); protected: void SetUp() override {
services/inputflinger/tests/PointerChoreographer_test.cpp +47 −24 Original line number Diff line number Diff line Loading @@ -150,9 +150,11 @@ protected: // setDefaultMouseDisplayId without topology. // For this reason in tests we mock this behavior by creating topology with a single // display. mChoreographer.setDisplayTopology({.primaryDisplayId = displayId, .graph{{displayId, {}}}, .displaysDensity = {{displayId, DENSITY_MEDIUM}}}); mChoreographer.setDisplayTopology( DisplayTopologyGraph::create(/*primaryDisplayId=*/displayId, /*adjacencyGraph=*/{}, /*displaysDensity=*/{{displayId, DENSITY_MEDIUM}}) .value()); } else { mChoreographer.setDefaultMouseDisplayId(displayId); } Loading Loading @@ -2784,24 +2786,42 @@ protected: createViewport(DISPLAY_HIGH_DENSITY_ID, /*width*/ 200, /*height*/ 200, ui::ROTATION_0), }; DisplayTopologyGraph mTopology{DISPLAY_CENTER_ID, DisplayTopologyGraph mTopology = DisplayTopologyGraph:: create(/*primaryDisplay=*/DISPLAY_CENTER_ID, /*adjacencyGraph=*/ {{DISPLAY_CENTER_ID, {{DISPLAY_TOP_ID, DisplayTopologyPosition::TOP, 50.0f}, // Place a high density display on the left of DISPLAY_TOP_ID with 25 dp // gap // Place a high density display on the left of DISPLAY_TOP_ID with // 25 dp gap {DISPLAY_HIGH_DENSITY_ID, DisplayTopologyPosition::TOP, -75.0f}, {DISPLAY_RIGHT_ID, DisplayTopologyPosition::RIGHT, 10.0f}, {DISPLAY_BOTTOM_ID, DisplayTopologyPosition::BOTTOM, 10.0f}, {DISPLAY_LEFT_ID, DisplayTopologyPosition::LEFT, 10.0f}, {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}}, {DISPLAY_TOP_RIGHT_CORNER_ID, DisplayTopologyPosition::RIGHT, -90.0f}}}, // Reverse edges {DISPLAY_TOP_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::BOTTOM, -50.0f}}}, {DISPLAY_HIGH_DENSITY_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::BOTTOM, 75.0f}}}, {DISPLAY_RIGHT_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::LEFT, -10.0f}}}, {DISPLAY_BOTTOM_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::TOP, -10.0f}}}, {DISPLAY_LEFT_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::RIGHT, -10.0f}}}, {DISPLAY_TOP_RIGHT_CORNER_ID, {{DISPLAY_CENTER_ID, DisplayTopologyPosition::LEFT, 90.0f}}}}, /*displaysDensityMap=*/ {{DISPLAY_CENTER_ID, DENSITY_MEDIUM}, {DISPLAY_TOP_ID, DENSITY_MEDIUM}, {DISPLAY_RIGHT_ID, DENSITY_MEDIUM}, {DISPLAY_BOTTOM_ID, DENSITY_MEDIUM}, {DISPLAY_LEFT_ID, DENSITY_MEDIUM}, {DISPLAY_TOP_RIGHT_CORNER_ID, DENSITY_MEDIUM}, {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}}; {DISPLAY_HIGH_DENSITY_ID, DENSITY_HIGH}}) .value(); }; TEST_P(PointerChoreographerDisplayTopologyCursorTestFixture, Loading Loading @@ -2955,7 +2975,6 @@ protected: std::unordered_map<ui::LogicalDisplayId, std::vector<DisplayTopologyAdjacentDisplay>> topologyGraph; topologyGraph[primaryDisplayId] = {}; std::unordered_map<ui::LogicalDisplayId, int> displaysDensity; displaysDensity[primaryDisplayId] = DENSITY_MEDIUM; Loading @@ -2969,9 +2988,13 @@ protected: .offsetDp = 0.0f}); displaysDensity[adjacentDisplayId] = DENSITY_MEDIUM; previousDisplay = adjacentDisplayId; } mChoreographer.setDisplayTopology({primaryDisplayId, topologyGraph, displaysDensity}); mChoreographer.setDisplayTopology(DisplayTopologyGraph::create(primaryDisplayId, std::move(topologyGraph), std::move(displaysDensity)) .value()); } }; Loading Loading @@ -3028,7 +3051,7 @@ TEST_F(PointerChoreographerDisplayTopologyDefaultMouseDisplayTests, // Change the primary display to the third display setDisplayTopologyWithDisplays(/*primaryDisplayId=*/THIRD_DISPLAY_ID, /*adjacentDisplays=*/ {SECOND_DISPLAY_ID, THIRD_DISPLAY_ID}); {SECOND_DISPLAY_ID, FIRST_DISPLAY_ID}); assertPointerControllerNotCreated(); pc->assertViewportSet(SECOND_DISPLAY_ID); Loading