diff options
author | 2025-03-20 16:28:42 -0700 | |
---|---|---|
committer | 2025-03-20 16:28:42 -0700 | |
commit | a105c7bcfb6281b65d0384cf07a774a8235836fc (patch) | |
tree | 53fdbde17419b89304fd885886013611c93e4056 | |
parent | 1d39cd545b47c54f52ed4fbb7f447edd4225552e (diff) | |
parent | de365a6c1bfd64d2e9ef9da49e30f3565ecaf599 (diff) |
Snap for 13248265 from de365a6c1bfd64d2e9ef9da49e30f3565ecaf599 to 25Q2-release
Change-Id: Id20adfecd4960a563fc9dee13c315f50911a9b8c
33 files changed, 1811 insertions, 126 deletions
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 43ee33eb86..2d18d80678 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -50,7 +50,6 @@ static const char* native_processes_to_dump[] = { // Native processes to dump on debuggable builds. static const char* debuggable_native_processes_to_dump[] = { - "/system/bin/keystore2", "/system/bin/vold", NULL, }; diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 983bbdee6e..9e69b60f93 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -113,6 +113,16 @@ flag { } flag { + name: "allow_transfer_of_entire_gesture" + namespace: "input" + description: "When calling 'transferTouchGesture', the entire gesture (including new POINTER_DOWN events from the same device) will be automatically transferred to the destination window" + bug: "397979572" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_keyboard_classifier" namespace: "input" description: "Keyboard classifier that classifies all keyboards into alphabetic or non-alphabetic" diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 391703d506..2908c61182 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2510,6 +2510,24 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( return injectionError(InputEventInjectionResult::TARGET_MISMATCH); } + if (newTouchedWindowHandle != nullptr && + maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Check if this should be redirected to another window, in case this window previously + // called 'transferTouch' for this gesture. + const auto it = + std::find_if(tempTouchState.windows.begin(), tempTouchState.windows.end(), + [&](const TouchedWindow& touchedWindow) { + return touchedWindow.forwardingWindowToken == + newTouchedWindowHandle->getToken() && + touchedWindow.hasTouchingPointers(entry.deviceId); + }); + if (it != tempTouchState.windows.end()) { + LOG(INFO) << "Forwarding pointer from " << newTouchedWindowHandle->getName() + << " to " << it->windowHandle->getName(); + newTouchedWindowHandle = it->windowHandle; + } + } + std::vector<sp<WindowInfoHandle>> newTouchedWindows = findTouchedSpyWindowsAt(displayId, x, y, isStylus, entry.deviceId, mWindowInfos); if (newTouchedWindowHandle != nullptr) { @@ -2550,7 +2568,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( isDownOrPointerDown ? std::make_optional( entry.eventTime) - : std::nullopt); + : std::nullopt, + /*forwardingWindowToken=*/nullptr); if (!addResult.ok()) { LOG(ERROR) << "Error while processing " << entry << " for " << windowHandle->getName(); @@ -2577,7 +2596,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.addOrUpdateWindow(wallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, entry.deviceId, {pointer}, - entry.eventTime); + entry.eventTime, + /*forwardingWindowToken=*/nullptr); } } } @@ -2676,7 +2696,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, InputTarget::DispatchMode::SLIPPERY_ENTER, targetFlags, entry.deviceId, {pointer}, - entry.eventTime); + entry.eventTime, + /*forwardingWindowToken=*/nullptr); // Check if the wallpaper window should deliver the corresponding event. slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, @@ -5833,7 +5854,7 @@ void InputDispatcher::setMaximumObscuringOpacityForTouch(float opacity) { } bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) { + bool isDragDrop, bool transferEntireGesture) { if (fromToken == toToken) { LOG_IF(INFO, DEBUG_FOCUS) << "Trivial transfer to same window."; return true; @@ -5847,7 +5868,7 @@ bool InputDispatcher::transferTouchGesture(const sp<IBinder>& fromToken, const s "transferring touch from this window to another window", traceContext.getTracker()); - auto result = mTouchStates.transferTouchGesture(fromToken, toToken); + auto result = mTouchStates.transferTouchGesture(fromToken, toToken, transferEntireGesture); if (!result.has_value()) { return false; } @@ -5891,7 +5912,8 @@ std::optional<std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<Pointe std::list<InputDispatcher::DispatcherTouchState::CancellationArgs>, std::list<InputDispatcher::DispatcherTouchState::PointerDownArgs>>> InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IBinder>& fromToken, - const sp<android::IBinder>& toToken) { + const sp<android::IBinder>& toToken, + bool transferEntireGesture) { // Find the target touch state and touched window by fromToken. auto touchStateWindowAndDisplay = findTouchStateWindowAndDisplay(fromToken); if (!touchStateWindowAndDisplay.has_value()) { @@ -5934,8 +5956,12 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB } // Transferring touch focus using this API should not effect the focused window. newTargetFlags |= InputTarget::Flags::NO_FOCUS_CHANGE; + sp<IBinder> forwardingWindowToken; + if (transferEntireGesture && com::android::input::flags::allow_transfer_of_entire_gesture()) { + forwardingWindowToken = fromToken; + } state.addOrUpdateWindow(toWindowHandle, InputTarget::DispatchMode::AS_IS, newTargetFlags, - deviceId, pointers, downTimeInTarget); + deviceId, pointers, downTimeInTarget, forwardingWindowToken); // Synthesize cancel for old window and down for new window. std::shared_ptr<Connection> fromConnection = mConnectionManager.getConnection(fromToken); @@ -6017,7 +6043,8 @@ bool InputDispatcher::transferTouchOnDisplay(const sp<IBinder>& destChannelToken fromToken = from->getToken(); } // release lock - return transferTouchGesture(fromToken, destChannelToken); + return transferTouchGesture(fromToken, destChannelToken, /*isDragDrop=*/false, + /*transferEntireGesture=*/false); } void InputDispatcher::resetAndDropEverythingLocked(const char* reason) { @@ -7195,7 +7222,8 @@ void InputDispatcher::DispatcherTouchState::slipWallpaperTouch( state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::SLIPPERY_ENTER, InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED, - deviceId, pointers, entry.eventTime); + deviceId, pointers, entry.eventTime, + /*forwardingWindowToken=*/nullptr); } } @@ -7236,7 +7264,8 @@ InputDispatcher::DispatcherTouchState::transferWallpaperTouch( wallpaperFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED | InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED; state.addOrUpdateWindow(newWallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, - deviceId, pointers, downTimeInTarget); + deviceId, pointers, downTimeInTarget, + /*forwardingWindowToken=*/nullptr); std::shared_ptr<Connection> wallpaperConnection = mConnectionManager.getConnection(newWallpaper->getToken()); if (wallpaperConnection != nullptr) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index ad7e87e192..2e8f2ce04e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -127,7 +127,7 @@ public: void setMaximumObscuringOpacityForTouch(float opacity) override; bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop = false) override; + bool isDragDrop, bool transferEntireGesture) override; bool transferTouchOnDisplay(const sp<IBinder>& destChannelToken, ui::LogicalDisplayId displayId) override; @@ -440,7 +440,8 @@ private: std::optional< std::tuple<sp<gui::WindowInfoHandle>, DeviceId, std::vector<PointerProperties>, std::list<CancellationArgs>, std::list<PointerDownArgs>>> - transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); + transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, + bool transferEntireGesture); base::Result<std::list<CancellationArgs>, status_t> pilferPointers( const sp<IBinder>& token, const Connection& requestingConnection); diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index 2bf63beb05..f1fca0c317 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -74,7 +74,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( const sp<WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, - std::optional<nsecs_t> firstDownTimeInTarget) { + std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken) { if (touchingPointers.empty()) { LOG(FATAL) << __func__ << "No pointers specified for " << windowHandle->getName(); return android::base::Error(); @@ -88,6 +88,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( if (touchedWindow.windowHandle == windowHandle) { touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags |= targetFlags; + touchedWindow.forwardingWindowToken = forwardingWindowToken; // For cases like hover enter/exit or DISPATCH_AS_OUTSIDE a touch window might not have // downTime set initially. Need to update existing window when a pointer is down for the // window. @@ -103,6 +104,7 @@ android::base::Result<void> TouchState::addOrUpdateWindow( touchedWindow.windowHandle = windowHandle; touchedWindow.dispatchMode = dispatchMode; touchedWindow.targetFlags = targetFlags; + touchedWindow.forwardingWindowToken = forwardingWindowToken; touchedWindow.addTouchingPointers(deviceId, touchingPointers); if (firstDownTimeInTarget) { touchedWindow.trySetDownTimeInTarget(deviceId, *firstDownTimeInTarget); diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 451d91704c..20155f4396 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -47,7 +47,7 @@ struct TouchState { const sp<android::gui::WindowInfoHandle>& windowHandle, InputTarget::DispatchMode dispatchMode, ftl::Flags<InputTarget::Flags> targetFlags, DeviceId deviceId, const std::vector<PointerProperties>& touchingPointers, - std::optional<nsecs_t> firstDownTimeInTarget); + std::optional<nsecs_t> firstDownTimeInTarget, sp<IBinder> forwardingWindowToken); void addHoveringPointerToWindow(const sp<android::gui::WindowInfoHandle>& windowHandle, DeviceId deviceId, const PointerProperties& pointer, float x, float y); diff --git a/services/inputflinger/dispatcher/TouchedWindow.cpp b/services/inputflinger/dispatcher/TouchedWindow.cpp index fa5be1a719..053a2e2441 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.cpp +++ b/services/inputflinger/dispatcher/TouchedWindow.cpp @@ -331,9 +331,9 @@ std::string TouchedWindow::dump() const { std::string out; std::string deviceStates = dumpMap(mDeviceStates, constToString, TouchedWindow::deviceStateToString); - out += StringPrintf("name='%s', targetFlags=%s, mDeviceStates=%s\n", + out += StringPrintf("name='%s', targetFlags=%s, forwardingWindowToken=%p, mDeviceStates=%s\n", windowHandle->getName().c_str(), targetFlags.string().c_str(), - deviceStates.c_str()); + forwardingWindowToken.get(), deviceStates.c_str()); return out; } diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index c38681eef0..d27c597803 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -34,6 +34,11 @@ struct TouchedWindow { InputTarget::DispatchMode dispatchMode = InputTarget::DispatchMode::AS_IS; ftl::Flags<InputTarget::Flags> targetFlags; + // If another window has transferred touches to this window, and wants to continue sending the + // rest of the gesture to this window, store the token of the originating (transferred-from) + // window here. + sp<IBinder> forwardingWindowToken; + // Hovering bool hasHoveringPointers() const; bool hasHoveringPointers(DeviceId deviceId) const; @@ -76,7 +81,7 @@ struct TouchedWindow { }; std::vector<DeviceId> eraseHoveringPointersIf( - std::function<bool(const PointerProperties&, float /*x*/, float /*y*/)> condition); + std::function<bool(const PointerProperties&, float x, float y)> condition); private: struct DeviceState { diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h index ab039c34ef..b22ddca4e6 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h @@ -148,7 +148,7 @@ public: * Returns true on success. False if the window did not actually have an active touch gesture. */ virtual bool transferTouchGesture(const sp<IBinder>& fromToken, const sp<IBinder>& toToken, - bool isDragDrop) = 0; + bool isDragDrop, bool transferEntireGesture) = 0; /** * Transfer a touch gesture to the provided channel, no matter where the current touch is. diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index f7dcd6c6e3..298ba4209a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1176,7 +1176,9 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) { // Transfer touch from the middle window to the right window. ASSERT_TRUE(mDispatcher->transferTouchGesture(middleForegroundWindow->getToken(), - rightForegroundWindow->getToken())); + rightForegroundWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); middleForegroundWindow->consumeMotionEvent( AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB))); @@ -1208,6 +1210,148 @@ TEST_F(InputDispatcherTest, MultiDeviceTouchTransferWithWallpaperWindows) { } /** + * If a window has requested touch to be transferred, only the current pointers should be + * transferred. + * + * Subsequent pointers should still go through the normal hit testing. + * + * In this test, we are invoking 'transferTouchGesture' with the parameter 'transferEntireGesture' + * set to true, but that value doesn't make any difference, since the flag is disabled. This test + * will be removed once that flag is fully rolled out. + */ +TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture_legacy) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, false); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + // The actual value of parameter 'transferEntireGesture' doesn't matter, since the flag is off. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/true)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, and should be delivered there + // as a new pointer. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}}; + bottomWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers))); + bottomWindow->assertNoEvents(); +} + +/** + * If a window has requested touch to be transferred, the current pointers should be transferred. + * + * If the window did not request the "entire gesture" to be transferred, subsequent pointers should + * still go through the normal hit testing. + */ +TEST_F(InputDispatcherTest, TouchTransferDoesNotSendEntireGesture) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, and should be delivered there + // because "entire gesture" was not requested to be transferred when the original call to + // 'transferTouchGesture' was made. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + const std::map<int32_t, PointF> expectedPointers{{0, PointF{50, 50}}}; + bottomWindow->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_MOVE), WithPointers(expectedPointers))); + bottomWindow->assertNoEvents(); +} + +/** + * If a window has requested touch to be transferred, all subsequent pointers from the same gesture + * should be transferred if 'transferEntireGesture' was set to 'true'. + * + * In this test, there are 2 windows - one above and one below. + * First pointer goes to the top window. Then top window calls 'transferTouch' upon receiving + * ACTION_DOWN and transfers touch to the bottom window. + * Subsequent pointers from the same gesture should still be forwarded to the bottom window, + * as long as they first land into the top window. + */ +TEST_F(InputDispatcherTest, TouchTransferSendsEntireGesture) { + SCOPED_FLAG_OVERRIDE(allow_transfer_of_entire_gesture, true); + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> topWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Top window", + ui::LogicalDisplayId::DEFAULT); + + sp<FakeWindowHandle> bottomWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Bottom window", + ui::LogicalDisplayId::DEFAULT); + + mDispatcher->onWindowInfosChanged( + {{*topWindow->getInfo(), *bottomWindow->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .build()); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // Transfer touch from the top window to the bottom window. + ASSERT_TRUE(mDispatcher->transferTouchGesture(topWindow->getToken(), bottomWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/true)); + topWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); + bottomWindow->consumeMotionEvent(WithMotionAction(ACTION_DOWN)); + + // When the second pointer goes down, it will hit the top window, but since the top window has + // requested the whole gesture to be transferred, it should be redirected to the bottom window. + mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50)) + .pointer(PointerBuilder(1, ToolType::FINGER).x(60).y(60)) + .build()); + + bottomWindow->consumeMotionEvent(WithMotionAction(POINTER_1_DOWN)); +} + +/** * A single window that receives touch (on top), and a wallpaper window underneath it. * The top window gets a multitouch gesture. * Ensure that wallpaper gets the same gesture. @@ -6608,7 +6752,8 @@ TEST_F(InputDispatcherDisplayProjectionTest, SynthesizeDownWithCorrectCoordinate // The pointer is transferred to the second window, and the second window receives it in the // correct coordinate space. - mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(), + /*isDragDrop=*/false, /*transferEntireGesture=*/false); firstWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithCoords(100, 400))); secondWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithCoords(-100, -400))); } @@ -7144,7 +7289,8 @@ INSTANTIATE_TEST_SUITE_P( [&](const std::unique_ptr<InputDispatcher>& dispatcher, sp<IBinder> from, sp<IBinder> to) { return dispatcher->transferTouchGesture(from, to, - /*isDragAndDrop=*/false); + /*isDragAndDrop=*/false, + /*transferEntireGesture=*/false); })); TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { @@ -7184,7 +7330,8 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) { secondWindow->consumeMotionDown(); // Transfer touch to the second window - mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken()); + mDispatcher->transferTouchGesture(firstWindow->getToken(), secondWindow->getToken(), + /*isDragDrop=*/false, /*transferEntireGesture=*/false); // The first window gets cancel and the new gets pointer down (it already saw down) firstWindow->consumeMotionCancel(); secondWindow->consumeMotionPointerDown(1, ui::LogicalDisplayId::DEFAULT, @@ -7318,7 +7465,9 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) { // Transfer touch ASSERT_TRUE(mDispatcher->transferTouchGesture(firstWindowInPrimary->getToken(), - secondWindowInPrimary->getToken())); + secondWindowInPrimary->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false)); // The first window gets cancel. firstWindowInPrimary->consumeMotionCancel(); secondWindowInPrimary->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, @@ -12610,7 +12759,8 @@ protected: // Transfer touch focus to the drag window bool transferred = mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), - /*isDragDrop=*/true); + /*isDragDrop=*/true, + /*transferEntireGesture=*/false); if (transferred) { targetWindow->consumeMotionCancel(dragStartDisplay); mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); @@ -15182,7 +15332,9 @@ TEST_P(TransferOrDontTransferFixture, TouchDownAndMouseHover) { if (GetParam()) { // Call transferTouchGesture const bool transferred = - mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false); ASSERT_TRUE(transferred); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); @@ -15264,7 +15416,9 @@ TEST_P(TransferOrDontTransferFixture, MouseAndTouchTransferSimultaneousMultiDevi if (GetParam()) { // Call transferTouchGesture const bool transferred = - mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken()); + mDispatcher->transferTouchGesture(mFromWindow->getToken(), mToWindow->getToken(), + /*isDragDrop=*/false, + /*transferEntireGesture=*/false); ASSERT_TRUE(transferred); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_CANCEL)); mFromWindow->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); diff --git a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp index d547af98ea..27ae18fac9 100644 --- a/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/AidlComposerHal.cpp @@ -328,9 +328,7 @@ std::string AidlComposer::dumpDebugInfo() { std::string str; // Use other thread to read pipe to prevent // pipe is full, making HWC be blocked in writing. - std::thread t([&]() { - base::ReadFdToString(pipefds[0], &str); - }); + std::thread t([&]() { base::ReadFdToString(pipefds[0], &str); }); const auto status = mAidlComposer->dump(pipefds[1], /*args*/ nullptr, /*numArgs*/ 0); // Close the write-end of the pipe to make sure that when reading from the // read-end we will get eof instead of blocking forever diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 018ee6e461..ab086e4b55 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -229,14 +229,13 @@ public: virtual std::vector<IComposerClient::PerFrameMetadataKey> getPerFrameMetadataKeys( Display display) = 0; virtual Error getRenderIntents(Display display, ColorMode colorMode, - std::vector<RenderIntent>* outRenderIntents) = 0; + std::vector<RenderIntent>* outRenderIntents) = 0; virtual Error getDataspaceSaturationMatrix(Dataspace dataspace, mat4* outMatrix) = 0; // Composer HAL 2.3 virtual Error getDisplayIdentificationData(Display display, uint8_t* outPort, std::vector<uint8_t>* outData) = 0; - virtual Error setLayerColorTransform(Display display, Layer layer, - const float* matrix) = 0; + virtual Error setLayerColorTransform(Display display, Layer layer, const float* matrix) = 0; virtual Error getDisplayedContentSamplingAttributes(Display display, PixelFormat* outFormat, Dataspace* outDataspace, uint8_t* outComponentMask) = 0; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 14088a6428..787a64b089 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -560,7 +560,7 @@ status_t HWComposer::getDeviceCompositionChanges( if (!hasChangesError(error)) { RETURN_IF_HWC_ERROR_FOR("presentOrValidate", error, displayId, UNKNOWN_ERROR); } - if (state == 1) { //Present Succeeded. + if (state == 1) { // Present Succeeded. std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; error = hwcDisplay->getReleaseFences(&releaseFences); displayData.releaseFences = std::move(releaseFences); @@ -824,8 +824,8 @@ mat4 HWComposer::getDataspaceSaturationMatrix(HalDisplayId displayId, ui::Datasp RETURN_IF_INVALID_DISPLAY(displayId, {}); mat4 matrix; - auto error = mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, - &matrix); + auto error = + mDisplayData[displayId].hwcDisplay->getDataspaceSaturationMatrix(dataspace, &matrix); RETURN_IF_HWC_ERROR(error, displayId, {}); return matrix; } diff --git a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp index a010353423..3321f51026 100644 --- a/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/HidlComposerHal.cpp @@ -1230,15 +1230,16 @@ Error HidlComposer::getDisplayCapabilities(Display display, translate<DisplayCapability>(tmpCaps); }); } else { - mClient_2_3 - ->getDisplayCapabilities(display, [&](const auto& tmpError, const auto& tmpCaps) { - error = static_cast<V2_4::Error>(tmpError); - if (error != V2_4::Error::NONE) { - return; - } + mClient_2_3->getDisplayCapabilities(display, + [&](const auto& tmpError, const auto& tmpCaps) { + error = static_cast<V2_4::Error>(tmpError); + if (error != V2_4::Error::NONE) { + return; + } - *outCapabilities = translate<DisplayCapability>(tmpCaps); - }); + *outCapabilities = + translate<DisplayCapability>(tmpCaps); + }); } return static_cast<Error>(error); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index ce7a720714..6b40c98d0a 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -19,7 +19,7 @@ #pragma clang diagnostic ignored "-Wconversion" #pragma clang diagnostic ignored "-Wextra" -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "SurfaceFlinger.h" @@ -361,7 +361,7 @@ bool isExpectedPresentWithinTimeout(TimePoint expectedPresentTime, return std::abs(expectedPresentTime.ns() - (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); } -} // namespace anonymous +} // namespace // --------------------------------------------------------------------------- @@ -393,7 +393,7 @@ ui::PixelFormat SurfaceFlinger::wideColorGamutCompositionPixelFormat = ui::Pixel LatchUnsignaledConfig SurfaceFlinger::enableLatchUnsignaledConfig; std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { - switch(displayColorSetting) { + switch (displayColorSetting) { case DisplayColorSetting::kManaged: return std::string("Managed"); case DisplayColorSetting::kUnmanaged: @@ -401,8 +401,7 @@ std::string decodeDisplayColorSetting(DisplayColorSetting displayColorSetting) { case DisplayColorSetting::kEnhanced: return std::string("Enhanced"); default: - return std::string("Unknown ") + - std::to_string(static_cast<int>(displayColorSetting)); + return std::string("Unknown ") + std::to_string(static_cast<int>(displayColorSetting)); } } @@ -594,15 +593,14 @@ sp<IBinder> SurfaceFlinger::createVirtualDisplay( class DisplayToken : public BBinder { sp<SurfaceFlinger> flinger; virtual ~DisplayToken() { - // no more references, this display must be terminated - Mutex::Autolock _l(flinger->mStateLock); - flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); - flinger->setTransactionFlags(eDisplayTransactionNeeded); - } - public: - explicit DisplayToken(const sp<SurfaceFlinger>& flinger) - : flinger(flinger) { + // no more references, this display must be terminated + Mutex::Autolock _l(flinger->mStateLock); + flinger->mCurrentState.displays.removeItem(wp<IBinder>::fromExisting(this)); + flinger->setTransactionFlags(eDisplayTransactionNeeded); } + + public: + explicit DisplayToken(const sp<SurfaceFlinger>& flinger) : flinger(flinger) {} }; sp<BBinder> token = sp<DisplayToken>::make(sp<SurfaceFlinger>::fromExisting(this)); @@ -761,7 +759,7 @@ void SurfaceFlinger::bootFinished() { const nsecs_t now = systemTime(); const nsecs_t duration = now - mBootTime; - ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) ); + ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration))); mFrameTracer->initialize(); mFrameTimeline->onBootFinished(); @@ -780,8 +778,7 @@ void SurfaceFlinger::bootFinished() { property_set("service.bootanim.exit", "1"); const int LOGTAG_SF_STOP_BOOTANIM = 60110; - LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, - ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); + LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); sp<IBinder> input(defaultServiceManager()->waitForService(String16("inputflinger"))); @@ -901,8 +898,8 @@ renderengine::RenderEngine::BlurAlgorithm chooseBlurAlgorithm(bool supportsBlur) void SurfaceFlinger::init() FTL_FAKE_GUARD(kMainThreadContext) { SFTRACE_CALL(); - ALOGI( "SurfaceFlinger's main thread ready to run. " - "Initializing graphics H/W..."); + ALOGI("SurfaceFlinger's main thread ready to run. " + "Initializing graphics H/W..."); addTransactionReadyFilters(); Mutex::Autolock lock(mStateLock); @@ -1125,17 +1122,16 @@ void SurfaceFlinger::readPersistentProperties() { static_cast<ui::ColorMode>(base::GetIntProperty("persist.sys.sf.color_mode"s, 0)); } -status_t SurfaceFlinger::getSupportedFrameTimestamps( - std::vector<FrameEvent>* outSupported) const { +status_t SurfaceFlinger::getSupportedFrameTimestamps(std::vector<FrameEvent>* outSupported) const { *outSupported = { - FrameEvent::REQUESTED_PRESENT, - FrameEvent::ACQUIRE, - FrameEvent::LATCH, - FrameEvent::FIRST_REFRESH_START, - FrameEvent::LAST_REFRESH_START, - FrameEvent::GPU_COMPOSITION_DONE, - FrameEvent::DEQUEUE_READY, - FrameEvent::RELEASE, + FrameEvent::REQUESTED_PRESENT, + FrameEvent::ACQUIRE, + FrameEvent::LATCH, + FrameEvent::FIRST_REFRESH_START, + FrameEvent::LAST_REFRESH_START, + FrameEvent::GPU_COMPOSITION_DONE, + FrameEvent::DEQUEUE_READY, + FrameEvent::RELEASE, }; if (mHasReliablePresentFences) { @@ -2210,7 +2206,6 @@ status_t SurfaceFlinger::addHdrLayerInfoListener(const sp<IBinder>& displayToken } hdrInfoReporter->addListener(listener); - mAddingHDRLayerInfoListener = true; return OK; } @@ -2271,13 +2266,13 @@ sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( const auto cycle = [&] { if (FlagManager::getInstance().deprecate_vsync_sf()) { ALOGW_IF(vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger, - "requested unsupported config eVsyncSourceSurfaceFlinger"); + "requested unsupported config eVsyncSourceSurfaceFlinger"); return scheduler::Cycle::Render; } return vsyncSource == gui::ISurfaceComposer::VsyncSource::eVsyncSourceSurfaceFlinger - ? scheduler::Cycle::LastComposite - : scheduler::Cycle::Render; + ? scheduler::Cycle::LastComposite + : scheduler::Cycle::Render; }(); return mScheduler->createDisplayEventConnection(cycle, eventRegistration, layerHandle); } @@ -2807,9 +2802,10 @@ bool SurfaceFlinger::commit(PhysicalDisplayId pacesetterId, // setTransactionFlags which will schedule another SF frame. This was if the tracker // needs to adjust the vsync timeline, it will be done before the next frame. if (FlagManager::getInstance().vrr_config() && mustComposite) { - mScheduler->getVsyncSchedule()->getTracker().onFrameBegin( - pacesetterFrameTarget.expectedPresentTime(), - pacesetterFrameTarget.lastSignaledFrameTime()); + mScheduler->getVsyncSchedule() + ->getTracker() + .onFrameBegin(pacesetterFrameTarget.expectedPresentTime(), + pacesetterFrameTarget.lastSignaledFrameTime()); } if (transactionFlushNeeded()) { setTransactionFlags(eTransactionFlushNeeded); @@ -3058,7 +3054,7 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( // This should be interpreted to mean that there are 2 cached sets. // So there are only 2 non skipped layers -- b and s. // The layers rrc and cc are flattened into layers b and s respectively. - const LayerFE::HwcLayerDebugState &hwcState = layerFE->getLastHwcState(); + const LayerFE::HwcLayerDebugState& hwcState = layerFE->getLastHwcState(); if (hwcState.overrideBufferId != prevOverrideBufferId) { // End the existing run. if (prevOverrideBufferId) { @@ -3070,11 +3066,10 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( } } - compositionSummary.push_back( - layerFE->mSnapshot->classifyCompositionForDebug(hwcState)); + compositionSummary.push_back(layerFE->mSnapshot->classifyCompositionForDebug(hwcState)); if (hwcState.overrideBufferId && !hwcState.wasSkipped) { - compositionSummary.push_back(':'); + compositionSummary.push_back(':'); } prevOverrideBufferId = hwcState.overrideBufferId; @@ -4324,37 +4319,35 @@ void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) { mVisibleWindowIds = std::move(visibleWindowIds); } - BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo, - windowInfos = std::move(windowInfos), - displayInfos = std::move(displayInfos), - inputWindowCommands = - std::move(mInputWindowCommands), - inputFlinger = mInputFlinger, this, - visibleWindowsChanged, vsyncId, - frameTime]() mutable { - SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); - if (updateWindowInfo) { - mWindowInfosListenerInvoker - ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos), - std::move(displayInfos), - ftl::to_underlying(vsyncId), - frameTime.ns()}, - std::move(inputWindowCommands.releaseListeners()), - /* forceImmediateCall= */ visibleWindowsChanged || - !inputWindowCommands.getFocusRequests().empty()); - } else { - // If there are listeners but no changes to input windows, call the listeners - // immediately. - for (const auto& listener : inputWindowCommands.getListeners()) { - if (IInterface::asBinder(listener)->isBinderAlive()) { - listener->onWindowInfosReported(); + BackgroundExecutor::getInstance().sendCallbacks( + {[updateWindowInfo, windowInfos = std::move(windowInfos), + displayInfos = std::move(displayInfos), + inputWindowCommands = std::move(mInputWindowCommands), inputFlinger = mInputFlinger, + this, visibleWindowsChanged, vsyncId, frameTime]() mutable { + SFTRACE_NAME("BackgroundExecutor::updateInputFlinger"); + if (updateWindowInfo) { + mWindowInfosListenerInvoker + ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos), + std::move(displayInfos), + ftl::to_underlying(vsyncId), + frameTime.ns()}, + std::move(inputWindowCommands.releaseListeners()), + /* forceImmediateCall= */ visibleWindowsChanged || + !inputWindowCommands.getFocusRequests() + .empty()); + } else { + // If there are listeners but no changes to input windows, call the listeners + // immediately. + for (const auto& listener : inputWindowCommands.getListeners()) { + if (IInterface::asBinder(listener)->isBinderAlive()) { + listener->onWindowInfosReported(); + } + } } - } - } - for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) { - inputFlinger->setFocusedWindow(focusRequest); - } - }}); + for (const auto& focusRequest : inputWindowCommands.getFocusRequests()) { + inputFlinger->setFocusedWindow(focusRequest); + } + }}); mInputWindowCommands.clear(); } @@ -5067,8 +5060,8 @@ status_t SurfaceFlinger::setTransactionState( if (resolvedState.state.hasBufferChanges() && resolvedState.state.hasValidBuffer() && resolvedState.state.surface) { sp<Layer> layer = LayerHandle::getLayer(resolvedState.state.surface); - std::string layerName = (layer) ? - layer->getDebugName() : std::to_string(resolvedState.state.layerId); + std::string layerName = + (layer) ? layer->getDebugName() : std::to_string(resolvedState.state.layerId); resolvedState.externalTexture = getExternalTextureFromBufferData(*resolvedState.state.bufferData, layerName.c_str(), transactionId); @@ -5922,8 +5915,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); - if ((uid != AID_SHELL) && - !PermissionCache::checkPermission(sDump, pid, uid)) { + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { StringAppendF(&result, "Permission Denial: can't dump SurfaceFlinger from pid=%d, uid=%d\n", pid, uid); write(fd, result.c_str(), result.size()); @@ -6381,7 +6373,7 @@ void SurfaceFlinger::dumpAll(const DumpArgs& args, const std::string& compositio // figure out if we're stuck somewhere const nsecs_t now = systemTime(); const nsecs_t inTransaction(mDebugInTransaction); - nsecs_t inTransactionDuration = (inTransaction) ? now-inTransaction : 0; + nsecs_t inTransactionDuration = (inTransaction) ? now - inTransaction : 0; /* * Dump library configuration. @@ -6561,7 +6553,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { if (!callingThreadHasUnscopedSurfaceFlingerAccess(usePermissionCache)) { IPCThreadState* ipc = IPCThreadState::self(); ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d", - ipc->getCallingPid(), ipc->getCallingUid()); + ipc->getCallingPid(), ipc->getCallingUid()); return PERMISSION_DENIED; } return OK; @@ -6677,11 +6669,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r CHECK_INTERFACE(ISurfaceComposer, data, reply); IPCThreadState* ipc = IPCThreadState::self(); const int uid = ipc->getCallingUid(); - if (CC_UNLIKELY(uid != AID_SYSTEM - && !PermissionCache::checkCallingPermission(sHardwareTest))) { + if (CC_UNLIKELY(uid != AID_SYSTEM && + !PermissionCache::checkCallingPermission(sHardwareTest))) { const int pid = ipc->getCallingPid(); ALOGE("Permission Denial: " - "can't access SurfaceFlinger pid=%d, uid=%d", pid, uid); + "can't access SurfaceFlinger pid=%d, uid=%d", + pid, uid); return PERMISSION_DENIED; } int n; @@ -6752,7 +6745,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r n = data.readInt32(); if (n) { // color matrix is sent as a column-major mat4 matrix - for (size_t i = 0 ; i < 4; i++) { + for (size_t i = 0; i < 4; i++) { for (size_t j = 0; j < 4; j++) { mClientColorMatrix[i][j] = data.readFloat(); } @@ -7272,9 +7265,7 @@ auto SurfaceFlinger::getKernelIdleTimerProperties(PhysicalDisplayId displayId) class WindowDisconnector { public: WindowDisconnector(ANativeWindow* window, int api) : mWindow(window), mApi(api) {} - ~WindowDisconnector() { - native_window_api_disconnect(mWindow, mApi); - } + ~WindowDisconnector() { native_window_api_disconnect(mWindow, mApi); } private: ANativeWindow* mWindow; diff --git a/services/surfaceflinger/tests/end2end/.clang-format b/services/surfaceflinger/tests/end2end/.clang-format new file mode 120000 index 0000000000..5e8e20be26 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clang-format @@ -0,0 +1 @@ +../../../../../../build/soong/scripts/system-clang-format
\ No newline at end of file diff --git a/services/surfaceflinger/tests/end2end/.clang-tidy b/services/surfaceflinger/tests/end2end/.clang-tidy new file mode 100644 index 0000000000..29f3b4721c --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clang-tidy @@ -0,0 +1,380 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FormatStyle: file +InheritParentConfig: true + +# Please add checks explicitly rather than using wildcards like "modernize-*". +# These check names are current as of LLVM 20.0.0, as reported by "clang-tidy --list-checks --checks=*" +# For more information on each check, see https://clang.llvm.org/extra/clang-tidy/checks/list.html. +Checks: + # from android-* + - android-cloexec-accept + - android-cloexec-accept4 + - android-cloexec-creat + - android-cloexec-dup + - android-cloexec-epoll-create + - android-cloexec-epoll-create1 + - android-cloexec-fopen + - android-cloexec-inotify-init + - android-cloexec-inotify-init1 + - android-cloexec-memfd-create + - android-cloexec-open + - android-cloexec-pipe + - android-cloexec-pipe2 + - android-cloexec-socket + - android-comparison-in-temp-failure-retry + + # from bugprone-* + - bugprone-argument-comment + - bugprone-assert-side-effect + - bugprone-assignment-in-if-condition + - bugprone-bad-signal-to-kill-thread + - bugprone-bool-pointer-implicit-conversion + - bugprone-branch-clone + - bugprone-casting-through-void + - bugprone-chained-comparison + - bugprone-compare-pointer-to-member-virtual-function + - bugprone-copy-constructor-init + - bugprone-crtp-constructor-accessibility + - bugprone-dangling-handle + - bugprone-dynamic-static-initializers + - bugprone-easily-swappable-parameters + - bugprone-empty-catch + - bugprone-exception-escape + - bugprone-fold-init-type + - bugprone-forward-declaration-namespace + - bugprone-forwarding-reference-overload + - bugprone-implicit-widening-of-multiplication-result + - bugprone-inaccurate-erase + - bugprone-inc-dec-in-conditions + - bugprone-incorrect-enable-if + - bugprone-incorrect-roundings + - bugprone-infinite-loop + - bugprone-integer-division + - bugprone-lambda-function-name + - bugprone-macro-parentheses + - bugprone-macro-repeated-side-effects + - bugprone-misplaced-operator-in-strlen-in-alloc + - bugprone-misplaced-pointer-arithmetic-in-alloc + - bugprone-misplaced-widening-cast + - bugprone-move-forwarding-reference + - bugprone-multi-level-implicit-pointer-conversion + - bugprone-multiple-new-in-one-expression + - bugprone-multiple-statement-macro + - bugprone-narrowing-conversions + - bugprone-no-escape + - bugprone-non-zero-enum-to-bool-conversion + - bugprone-not-null-terminated-result + - bugprone-optional-value-conversion + - bugprone-parent-virtual-call + - bugprone-pointer-arithmetic-on-polymorphic-object + - bugprone-posix-return + - bugprone-redundant-branch-condition + - bugprone-reserved-identifier + - bugprone-return-const-ref-from-parameter + - bugprone-shared-ptr-array-mismatch + - bugprone-signal-handler + - bugprone-signed-char-misuse + - bugprone-sizeof-container + - bugprone-sizeof-expression + - bugprone-spuriously-wake-up-functions + - bugprone-standalone-empty + - bugprone-string-constructor + - bugprone-string-integer-assignment + - bugprone-string-literal-with-embedded-nul + - bugprone-stringview-nullptr + - bugprone-suspicious-enum-usage + - bugprone-suspicious-include + - bugprone-suspicious-memory-comparison + - bugprone-suspicious-memset-usage + - bugprone-suspicious-missing-comma + - bugprone-suspicious-realloc-usage + - bugprone-suspicious-semicolon + - bugprone-suspicious-string-compare + - bugprone-suspicious-stringview-data-usage + - bugprone-swapped-arguments + - bugprone-switch-missing-default-case + - bugprone-terminating-continue + - bugprone-throw-keyword-missing + - bugprone-too-small-loop-variable + - bugprone-unchecked-optional-access + - bugprone-undefined-memory-manipulation + - bugprone-undelegated-constructor + - bugprone-unhandled-exception-at-new + - bugprone-unhandled-self-assignment + - bugprone-unique-ptr-array-mismatch + - bugprone-unsafe-functions + - bugprone-unused-local-non-trivial-variable + - bugprone-unused-raii + - bugprone-unused-return-value + - bugprone-use-after-move + - bugprone-virtual-near-miss + + # from cert-* + - cert-con36-c + - cert-con54-cpp + - cert-ctr56-cpp + - cert-dcl03-c + - cert-dcl16-c + - cert-dcl37-c + - cert-dcl50-cpp + - cert-dcl51-cpp + - cert-dcl54-cpp + - cert-dcl58-cpp + - cert-dcl59-cpp + - cert-env33-c + - cert-err09-cpp + - cert-err33-c + - cert-err34-c + - cert-err52-cpp + - cert-err58-cpp + - cert-err60-cpp + - cert-err61-cpp + - cert-exp42-c + - cert-fio38-c + - cert-flp30-c + - cert-flp37-c + - cert-int09-c + - cert-mem57-cpp + - cert-msc24-c + - cert-msc30-c + - cert-msc32-c + - cert-msc33-c + - cert-msc50-cpp + - cert-msc51-cpp + - cert-msc54-cpp + - cert-oop11-cpp + - cert-oop54-cpp + - cert-oop57-cpp + - cert-oop58-cpp + - cert-pos44-c + - cert-pos47-c + - cert-sig30-c + - cert-str34-c + + # from concurrency-* + - concurrency-mt-unsafe + - concurrency-thread-canceltype-asynchronous + + # from cppcoreguidelines-* + - cppcoreguidelines-avoid-c-arrays + - cppcoreguidelines-avoid-capturing-lambda-coroutines + - cppcoreguidelines-avoid-const-or-ref-data-members + - cppcoreguidelines-avoid-do-while + - cppcoreguidelines-avoid-goto + - cppcoreguidelines-avoid-magic-numbers + - cppcoreguidelines-avoid-non-const-global-variables + - cppcoreguidelines-avoid-reference-coroutine-parameters + - cppcoreguidelines-c-copy-assignment-signature + - cppcoreguidelines-explicit-virtual-functions + - cppcoreguidelines-init-variables + - cppcoreguidelines-interfaces-global-init + - cppcoreguidelines-macro-to-enum + - cppcoreguidelines-macro-usage + - cppcoreguidelines-misleading-capture-default-by-value + - cppcoreguidelines-missing-std-forward + - cppcoreguidelines-narrowing-conversions + - cppcoreguidelines-no-malloc + - cppcoreguidelines-no-suspend-with-lock + - cppcoreguidelines-noexcept-destructor + - cppcoreguidelines-noexcept-move-operations + - cppcoreguidelines-noexcept-swap + - cppcoreguidelines-non-private-member-variables-in-classes + - cppcoreguidelines-owning-memory + - cppcoreguidelines-prefer-member-initializer + - cppcoreguidelines-pro-bounds-array-to-pointer-decay + - cppcoreguidelines-pro-bounds-constant-array-index + - cppcoreguidelines-pro-bounds-pointer-arithmetic + - cppcoreguidelines-pro-type-const-cast + - cppcoreguidelines-pro-type-cstyle-cast + - cppcoreguidelines-pro-type-member-init + - cppcoreguidelines-pro-type-reinterpret-cast + - cppcoreguidelines-pro-type-static-cast-downcast + - cppcoreguidelines-pro-type-union-access + - cppcoreguidelines-pro-type-vararg + - cppcoreguidelines-rvalue-reference-param-not-moved + - cppcoreguidelines-slicing + - cppcoreguidelines-special-member-functions + - cppcoreguidelines-use-default-member-init + - cppcoreguidelines-virtual-class-destructor + + # from google-* + - google-build-explicit-make-pair + - google-build-namespaces + - google-build-using-namespace + - google-default-arguments + - google-explicit-constructor + - google-global-names-in-headers + - google-objc-avoid-nsobject-new + - google-objc-avoid-throwing-exception + - google-objc-function-naming + - google-objc-global-variable-declaration + - google-readability-avoid-underscore-in-googletest-name + - google-readability-braces-around-statements + - google-readability-casting + - google-readability-function-size + - google-readability-namespace-comments + - google-readability-todo + - google-runtime-int + - google-runtime-operator + - google-upgrade-googletest-case + + # from misc-* + - misc-confusable-identifiers + - misc-const-correctness + - misc-coroutine-hostile-raii + - misc-definitions-in-headers + - misc-header-include-cycle + - misc-include-cleaner + - misc-misleading-bidirectional + - misc-misleading-identifier + - misc-misplaced-const + - misc-new-delete-overloads + - misc-no-recursion + - misc-non-copyable-objects + - misc-non-private-member-variables-in-classes + - misc-redundant-expression + - misc-static-assert + - misc-throw-by-value-catch-by-reference + - misc-unconventional-assign-operator + - misc-uniqueptr-reset-release + - misc-unused-alias-decls + - misc-unused-parameters + - misc-unused-using-decls + - misc-use-anonymous-namespace + - misc-use-internal-linkage + + # from modernize-* + - modernize-avoid-bind + - modernize-avoid-c-arrays + - modernize-concat-nested-namespaces + - modernize-deprecated-headers + - modernize-deprecated-ios-base-aliases + - modernize-loop-convert + - modernize-macro-to-enum + - modernize-make-shared + - modernize-make-unique + - modernize-min-max-use-initializer-list + - modernize-pass-by-value + - modernize-raw-string-literal + - modernize-redundant-void-arg + - modernize-replace-auto-ptr + - modernize-replace-disallow-copy-and-assign-macro + - modernize-replace-random-shuffle + - modernize-return-braced-init-list + - modernize-shrink-to-fit + - modernize-type-traits + - modernize-unary-static-assert + - modernize-use-auto + - modernize-use-bool-literals + - modernize-use-constraints + - modernize-use-default-member-init + - modernize-use-designated-initializers + - modernize-use-emplace + - modernize-use-equals-default + - modernize-use-equals-delete + - modernize-use-nodiscard + - modernize-use-noexcept + - modernize-use-nullptr + - modernize-use-override + - modernize-use-ranges + - modernize-use-starts-ends-with + - modernize-use-std-format + - modernize-use-std-numbers + - modernize-use-std-print + - modernize-use-trailing-return-type + - modernize-use-transparent-functors + - modernize-use-uncaught-exceptions + - modernize-use-using + + # from performance-* + - performance-avoid-endl + - performance-enum-size + - performance-faster-string-find + - performance-for-range-copy + - performance-implicit-conversion-in-loop + - performance-inefficient-algorithm + - performance-inefficient-string-concatenation + - performance-inefficient-vector-operation + - performance-move-const-arg + - performance-move-constructor-init + - performance-no-automatic-move + - performance-no-int-to-ptr + - performance-noexcept-destructor + - performance-noexcept-move-constructor + - performance-noexcept-swap + - performance-trivially-destructible + - performance-type-promotion-in-math-fn + - performance-unnecessary-copy-initialization + - performance-unnecessary-value-param + + # from portability-* + - portability-restrict-system-includes + - portability-simd-intrinsics + - portability-std-allocator-const + + # from readability-* + - readability-avoid-const-params-in-decls + - readability-avoid-nested-conditional-operator + - readability-avoid-return-with-void-value + - readability-avoid-unconditional-preprocessor-if + - readability-braces-around-statements + - readability-const-return-type + - readability-container-contains + - readability-container-data-pointer + - readability-container-size-empty + - readability-convert-member-functions-to-static + - readability-delete-null-pointer + - readability-duplicate-include + - readability-else-after-return + - readability-enum-initial-value + - readability-function-cognitive-complexity + - readability-function-size + - readability-identifier-length + - readability-identifier-naming + - readability-implicit-bool-conversion + - readability-inconsistent-declaration-parameter-name + - readability-isolate-declaration + - readability-magic-numbers + - readability-make-member-function-const + - readability-math-missing-parentheses + - readability-misleading-indentation + - readability-misplaced-array-index + - readability-named-parameter + - readability-non-const-parameter + - readability-operators-representation + - readability-qualified-auto + - readability-redundant-access-specifiers + - readability-redundant-casting + - readability-redundant-control-flow + - readability-redundant-declaration + - readability-redundant-function-ptr-dereference + - readability-redundant-inline-specifier + - readability-redundant-member-init + - readability-redundant-preprocessor + - readability-redundant-smartptr-get + - readability-redundant-string-cstr + - readability-redundant-string-init + - readability-reference-to-constructed-temporary + - readability-simplify-boolean-expr + - readability-simplify-subscript-expr + - readability-static-accessed-through-instance + - readability-static-definition-in-anonymous-namespace + - readability-string-compare + - readability-suspicious-call-argument + - readability-uniqueptr-delete-release + - readability-uppercase-literal-suffix + - readability-use-anyofallof + - readability-use-std-min-max diff --git a/services/surfaceflinger/tests/end2end/.clangd b/services/surfaceflinger/tests/end2end/.clangd new file mode 100644 index 0000000000..d64d2f8a55 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/.clangd @@ -0,0 +1,20 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +Diagnostics: + UnusedIncludes: Strict + MissingIncludes: Strict + ClangTidy: + FastCheckFilter: None + # See the .clang-tidy files for additional configuration diff --git a/services/surfaceflinger/tests/end2end/Android.bp b/services/surfaceflinger/tests/end2end/Android.bp new file mode 100644 index 0000000000..8810330179 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/Android.bp @@ -0,0 +1,68 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_test { + name: "surfaceflinger_end2end_tests", + test_suites: ["device-tests"], + require_root: true, + + cpp_std: "experimental", + cflags: [ + "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION", + "-DNODISCARD_EXPECTED", + "-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS", + "-Wall", + "-Wconversion", + "-Werror", + "-Wextra", + "-Wformat", + "-Wno-non-virtual-dtor", + "-Wno-sign-compare", + "-Wno-sign-conversion", + "-Wshadow", + "-Wthread-safety", + "-Wunreachable-code", + "-Wunused", + ], + srcs: [ + "main.cpp", + "test_framework/core/TestService.cpp", + "test_framework/fake_hwc3/Hwc3Composer.cpp", + "test_framework/fake_hwc3/Hwc3Controller.cpp", + "test_framework/surfaceflinger/SFController.cpp", + "tests/Placeholder_test.cpp", + ], + tidy: true, + tidy_flags: [ + "--config=", // Use the .clang-tidy closest to each source file for the configuration + ], + tidy_checks_as_errors: [ + "*", + ], + include_dirs: [ + "frameworks/native/include", + ], + local_include_dirs: ["."], + shared_libs: [ + "libbase", + "libbinder", + "libbinder_ndk", + "libcutils", + "libgui", + "libsync", + "libui", + "libutils", + ], + static_libs: [ + "android.hardware.common-V2-ndk", + "android.hardware.graphics.common-V6-ndk", + "android.hardware.graphics.composer3-V3-ndk", + "libgtest", + ], +} diff --git a/services/surfaceflinger/tests/end2end/AndroidTest.xml b/services/surfaceflinger/tests/end2end/AndroidTest.xml new file mode 100644 index 0000000000..99cb7b3fee --- /dev/null +++ b/services/surfaceflinger/tests/end2end/AndroidTest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright 2025 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Configuration for surfaceflinger_end2end_tests"> + <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.DisableSELinuxTargetPreparer" /> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <!-- Stop everything to run SurfaceFlinger in isolation, with relaxed SELinux permissions --> + <option name="teardown-command" value="stop" /> + <option name="teardown-command" value="setprop debug.sf.nobootanimation 1" /> + + <!-- Restart everything with normal settings after the test finishes. --> + <option name="teardown-command" value="stop" /> + <option name="teardown-command" value="setprop debug.sf.nobootanimation 0" /> + <option name="teardown-command" value="setprop debug.sf.hwc_service_name default" /> + <option name="teardown-command" value="start" /> + </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> + <option name="cleanup" value="true" /> + <option name="push" value="surfaceflinger_end2end_tests->/data/local/tests/surfaceflinger_end2end_tests" /> + </target_preparer> + <test class="com.android.tradefed.testtype.GTest" > + <option name="native-test-device-path" value="/data/local/tests" /> + <option name="module-name" value="surfaceflinger_end2end_tests" /> + <option name="native-test-timeout" value="15m"/> + </test> +</configuration> diff --git a/services/surfaceflinger/tests/end2end/README.md b/services/surfaceflinger/tests/end2end/README.md new file mode 100644 index 0000000000..2f58cec5c7 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/README.md @@ -0,0 +1,75 @@ +# `surfaceflinger_end2end_tests` + +Tests to cover end to end testing of SurfaceFlinger. + +In particular the test framework allows you to simulate various display +configurations, so the test can confirm displays are handled correctly. + +## Quick Useful info + +### Running the tests + +At present the tests should run on any target, though the typical target would +be a [Cuttlefish](https://source.android.com/docs/devices/cuttlefish) VM +target such as `aosp_cf_x86_64_phone`. + +At some future time the test may be rewritten to require +[`vkms`](https://dri.freedesktop.org/docs/drm/gpu/vkms.html) and +[`drm_hwcomposer`](https://gitlab.freedesktop.org/drm-hwcomposer/drm-hwcomposer) + +``` +atest surfaceflinger_end2end_tests +``` + +You can also run the google test binary directly. However you will also need +to run a few other set-up and tear-down commands that are part of the +AndroidTest.xml configuration, so that SurfaceFlinger can be used run isolation +from the rest of the system. + +``` +# Set-up +adb root +adb shell stop +adb shell setenforce 0 +adb shell setprop debug.sf.nobootanimation 1 + +# Sync and run the test +adb sync data +adb shell data/nativetest64/surfaceflinger_end2end_tests/surfaceflinger_end2end_tests + +# Tear-down +adb shell stop +adb shell setenforce 1 +adb shell setprop debug.sf.nobootanimation 0 +adb shell setprop debug.sf.hwc_service_name default +``` + +### Manual clang-tidy checks via Soong + +At present Android does not run the clang-tidy checks as part of its +presubmit checks. + +You can run them through the build system by using phony target that are +automatically created for each source subdirectory. + +For the code under `frameworks/native/services/surfaceflinger/tests/end2end`, +you would build: + +``` +m tidy-frameworks-native-services-surfaceflinger-tests-end2end +``` + +For more information see the build documentation: + +* <https://android.googlesource.com/platform/build/soong/+/main/docs/tidy.md#the-tidy_directory-targets> + +### Seeing clang-tidy checks in your editor + +If your editor supports using [`clangd`](https://clangd.llvm.org/) as a +C++ language server, you can build and export a compilation database using +Soong. With the local `.clangd` configuration file, you should see the same +checks in editor, along with all the other checks `clangd` runs. + +See the build documentation for the compilation database instructions: + +https://android.googlesource.com/platform/build/soong/+/main/docs/compdb.md diff --git a/services/surfaceflinger/tests/end2end/main.cpp b/services/surfaceflinger/tests/end2end/main.cpp new file mode 100644 index 0000000000..ddf690021d --- /dev/null +++ b/services/surfaceflinger/tests/end2end/main.cpp @@ -0,0 +1,55 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdlib> +#include <string_view> + +#include <android-base/logging.h> +#include <android/binder_process.h> +#include <gtest/gtest.h> + +namespace { + +void init(int argc, char** argv) { + using namespace std::string_view_literals; + + ::testing::InitGoogleTest(&argc, argv); + ::android::base::InitLogging(argv, android::base::StderrLogger); + + auto minimumSeverity = android::base::INFO; + for (int i = 1; i < argc; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + const std::string_view arg = argv[i]; + + if (arg == "-v"sv) { + minimumSeverity = android::base::DEBUG; + } else if (arg == "-vv"sv) { + minimumSeverity = android::base::VERBOSE; + } + } + ::android::base::SetMinimumLogSeverity(minimumSeverity); +} + +} // namespace + +auto main(int argc, char** argv) -> int { + init(argc, argv); + + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h new file mode 100644 index 0000000000..c3a535e10f --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/DisplayConfiguration.h @@ -0,0 +1,29 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <cstdint> + +namespace android::surfaceflinger::tests::end2end::test_framework::core { + +struct DisplayConfiguration final { + using Id = int64_t; + + Id id{}; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::core diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp new file mode 100644 index 0000000000..0531f18527 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.cpp @@ -0,0 +1,82 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <memory> +#include <span> +#include <string> +#include <utility> +#include <vector> + +#include <android-base/expected.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/core/TestService.h" +#include "test_framework/fake_hwc3/Hwc3Controller.h" +#include "test_framework/surfaceflinger/SFController.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::core { + +struct TestService::Passkey final {}; + +auto TestService::startWithDisplays(const std::vector<DisplayConfiguration>& displays) + -> base::expected<std::unique_ptr<TestService>, std::string> { + using namespace std::string_literals; + + auto service = std::make_unique<TestService>(TestService::Passkey{}); + if (service == nullptr) { + return base::unexpected("Failed to construct the TestService instance."s); + } + + if (auto result = service->init(displays); !result) { + return base::unexpected("Failed to init the TestService instance: "s + result.error()); + } + + return service; +} + +TestService::TestService(Passkey passkey) { + ftl::ignore(passkey); +} + +auto TestService::init(std::span<const DisplayConfiguration> displays) + -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto hwcResult = fake_hwc3::Hwc3Controller::make(displays); + if (!hwcResult) { + return base::unexpected(std::move(hwcResult).error()); + } + auto hwc = *std::move(hwcResult); + + auto flingerResult = surfaceflinger::SFController::make(); + if (!flingerResult) { + return base::unexpected(std::move(flingerResult).error()); + } + auto flinger = *std::move(flingerResult); + + surfaceflinger::SFController::useHwcService(fake_hwc3::Hwc3Controller::getServiceName()); + + if (auto result = flinger->startAndConnect(); !result) { + return base::unexpected(std::move(result).error()); + } + + mHwc = std::move(hwc); + mFlinger = std::move(flinger); + return {}; +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::core diff --git a/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h new file mode 100644 index 0000000000..21e6426406 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/core/TestService.h @@ -0,0 +1,76 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <span> +#include <string> +#include <vector> + +#include <android-base/expected.h> +#include <android-base/logging.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework { + +namespace surfaceflinger { + +class SFController; + +} // namespace surfaceflinger + +namespace fake_hwc3 { + +class Hwc3Controller; + +} // namespace fake_hwc3 + +namespace core { + +class TestService final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Constructs the test service, and starts it with the given displays as connected at boot. + [[nodiscard]] static auto startWithDisplays(const std::vector<DisplayConfiguration>& displays) + -> base::expected<std::unique_ptr<TestService>, std::string>; + + explicit TestService(Passkey passkey); + + // Obtains the HWC3 back-end controller + [[nodiscard]] auto hwc() -> fake_hwc3::Hwc3Controller& { + CHECK(mHwc); + return *mHwc; + } + + // Obtains the SurfaceFlinger front-end controller + [[nodiscard]] auto flinger() -> surfaceflinger::SFController& { + CHECK(mFlinger); + return *mFlinger; + } + + private: + [[nodiscard]] auto init(std::span<const DisplayConfiguration> displays) + -> base::expected<void, std::string>; + + std::shared_ptr<fake_hwc3::Hwc3Controller> mHwc; + std::shared_ptr<surfaceflinger::SFController> mFlinger; +}; + +} // namespace core +} // namespace android::surfaceflinger::tests::end2end::test_framework diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp new file mode 100644 index 0000000000..5349ef0b58 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.cpp @@ -0,0 +1,123 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdint> +#include <memory> +#include <string> +#include <string_view> +#include <utility> +#include <vector> + +#include <aidl/android/hardware/graphics/composer3/BnComposer.h> +#include <aidl/android/hardware/graphics/composer3/Capability.h> +#include <aidl/android/hardware/graphics/composer3/IComposer.h> +#include <aidl/android/hardware/graphics/composer3/PowerMode.h> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android/binder_auto_utils.h> +#include <android/binder_interface_utils.h> +#include <android/binder_status.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/fake_hwc3/Hwc3Composer.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer::Hwc3ComposerImpl final + : public aidl::android::hardware::graphics::composer3::BnComposer { + using Capability = aidl::android::hardware::graphics::composer3::Capability; + using IComposerClient = aidl::android::hardware::graphics::composer3::IComposerClient; + using Hwc3PowerMode = aidl::android::hardware::graphics::composer3::PowerMode; + + // begin IComposer overrides + + auto dump(int dumpFd, const char** args, uint32_t num_args) -> binder_status_t override { + UNIMPLEMENTED(WARNING); + ftl::ignore(dumpFd, args, num_args); + return static_cast<binder_status_t>(STATUS_NO_MEMORY); + } + + auto createClient(std::shared_ptr<IComposerClient>* out_client) -> ndk::ScopedAStatus override { + UNIMPLEMENTED(WARNING); + ftl::ignore(out_client); + return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage( + IComposer::EX_NO_RESOURCES, "Client failed to initialize"); + } + + auto getCapabilities(std::vector<Capability>* out_capabilities) -> ndk::ScopedAStatus override { + UNIMPLEMENTED(WARNING); + ftl::ignore(out_capabilities); + return ndk::ScopedAStatus::ok(); + } + + // end IComposer overrides +}; + +struct Hwc3Composer::Passkey final {}; + +auto Hwc3Composer::getServiceName(std::string_view baseServiceName) -> std::string { + return Hwc3ComposerImpl::makeServiceName(baseServiceName); +} + +auto Hwc3Composer::make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string> { + using namespace std::string_literals; + + auto composer = std::make_shared<Hwc3Composer>(Passkey{}); + if (composer == nullptr) { + return base::unexpected("Failed to construct the Hwc3Composer instance."s); + } + + if (auto result = composer->init(); !result) { + return base::unexpected("Failed to init the Hwc3Composer instance: "s + result.error()); + } + + return composer; +} + +Hwc3Composer::Hwc3Composer(Hwc3Composer::Passkey passkey) { + ftl::ignore(passkey); +} + +auto Hwc3Composer::init() -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto impl = ndk::SharedRefBase::make<Hwc3ComposerImpl>(); + if (!impl) { + return base::unexpected("Failed to construct the Hwc3ComposerImpl instance."s); + } + + mImpl = std::move(impl); + + return {}; +} + +auto Hwc3Composer::getComposer() -> std::shared_ptr<Hwc3IComposer> { + return mImpl; +} + +void Hwc3Composer::addDisplay(const core::DisplayConfiguration& display) { + UNIMPLEMENTED(WARNING); + ftl::ignore(display, mImpl); +} + +void Hwc3Composer::removeDisplay(core::DisplayConfiguration::Id displayId) { + UNIMPLEMENTED(WARNING); + ftl::ignore(displayId, mImpl); +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h new file mode 100644 index 0000000000..6d6b7374c6 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Composer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> +#include <string_view> + +#include <aidl/android/hardware/graphics/composer3/IComposer.h> +#include <android-base/expected.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + class Hwc3ComposerImpl; // An internal class implements the AIDL interface. + + public: + using Hwc3IComposer = aidl::android::hardware::graphics::composer3::IComposer; + + // Gets the full qualified service name given a base name for the service. + [[nodiscard]] static auto getServiceName(std::string_view baseServiceName) -> std::string; + + // Constructs a Hwc3Composer instance. + [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<Hwc3Composer>, std::string>; + + explicit Hwc3Composer(Passkey passkey); + + // Obtains the AIDL composer3::IComposer interface for the internal instance. + [[nodiscard]] auto getComposer() -> std::shared_ptr<Hwc3IComposer>; + + // Adds a display to the composer. This will sent a hotplug connect event. + void addDisplay(const core::DisplayConfiguration& display); + + // Removes a display from the composer. This will sent a hotplug disconnect event. + void removeDisplay(core::DisplayConfiguration::Id displayId); + + private: + [[nodiscard]] auto init() -> base::expected<void, std::string>; + + std::shared_ptr<Hwc3ComposerImpl> mImpl; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp new file mode 100644 index 0000000000..ea985c09b4 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <memory> +#include <span> +#include <string> +#include <utility> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android/binder_manager.h> +#include <android/binder_stability.h> +#include <android/binder_status.h> +#include <fmt/format.h> +#include <ftl/ignore.h> + +#include "test_framework/core/DisplayConfiguration.h" +#include "test_framework/fake_hwc3/Hwc3Composer.h" +#include "test_framework/fake_hwc3/Hwc3Controller.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +struct Hwc3Controller::Passkey final {}; + +auto Hwc3Controller::make(std::span<const core::DisplayConfiguration> displays) + -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string> { + using namespace std::string_literals; + + auto controller = std::make_unique<Hwc3Controller>(Passkey{}); + if (controller == nullptr) { + return base::unexpected("Failed to construct the Hwc3Controller instance"s); + } + + if (auto result = controller->init(displays); !result) { + return base::unexpected("Failed to construct the Hwc3Controller instance: "s + + result.error()); + } + + return controller; +} + +Hwc3Controller::Hwc3Controller(Passkey passkey) { + ftl::ignore(passkey); +} + +auto Hwc3Controller::init(const std::span<const core::DisplayConfiguration> displays) + -> base::expected<void, std::string> { + using namespace std::string_literals; + + auto qualifiedServiceName = Hwc3Composer::getServiceName(baseServiceName); + + auto composerResult = Hwc3Composer::make(); + if (!composerResult) { + return base::unexpected(std::move(composerResult).error()); + } + auto composer = *std::move(composerResult); + + for (const auto& display : displays) { + composer->addDisplay(display); + } + + auto binder = composer->getComposer()->asBinder(); + + // This downgrade allows us to use the fake service name without it being defined in the + // VINTF manifest. + AIBinder_forceDowngradeToLocalStability(binder.get()); + + auto status = AServiceManager_addService(binder.get(), qualifiedServiceName.c_str()); + if (status != STATUS_OK) { + return base::unexpected(fmt::format("Failed to register service {}. Error {}.", + qualifiedServiceName, status)); + } + LOG(INFO) << "Registered service " << qualifiedServiceName << ". Error: " << status; + + mComposer = std::move(composer); + return {}; +} + +auto Hwc3Controller::getServiceName() -> std::string { + return Hwc3Composer::getServiceName(baseServiceName); +} + +void Hwc3Controller::addDisplay(const core::DisplayConfiguration& config) { + CHECK(mComposer); + mComposer->addDisplay(config); +} + +void Hwc3Controller::removeDisplay(core::DisplayConfiguration::Id displayId) { + CHECK(mComposer); + mComposer->removeDisplay(displayId); +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h new file mode 100644 index 0000000000..e53d2cfc48 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/fake_hwc3/Hwc3Controller.h @@ -0,0 +1,59 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <span> +#include <string> + +#include <android-base/expected.h> + +#include "test_framework/core/DisplayConfiguration.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 { + +class Hwc3Composer; + +class Hwc3Controller final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Gets the service name for the HWC3 instance that will be created and registered + [[nodiscard]] static auto getServiceName() -> std::string; + + // Makes the HWC3 controller instance. + [[nodiscard]] static auto make(std::span<const core::DisplayConfiguration> displays) + -> base::expected<std::shared_ptr<fake_hwc3::Hwc3Controller>, std::string>; + + explicit Hwc3Controller(Passkey passkey); + + // Adds a new display to the HWC3, which will become a hotplug connect event. + void addDisplay(const core::DisplayConfiguration& config); + + // Removes a new display from the HWC3, which will become a hotplug disconnect event. + void removeDisplay(core::DisplayConfiguration::Id displayId); + + private: + static constexpr std::string baseServiceName = "fake"; + + [[nodiscard]] auto init(std::span<const core::DisplayConfiguration> displays) + -> base::expected<void, std::string>; + + std::shared_ptr<Hwc3Composer> mComposer; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::fake_hwc3 diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp new file mode 100644 index 0000000000..1cf49c5750 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <chrono> +#include <cstdlib> +#include <memory> +#include <string> +#include <string_view> +#include <thread> +#include <utility> + +#include <android-base/expected.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/gui/ISurfaceComposer.h> +#include <binder/IBinder.h> +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> +#include <binder/Status.h> +#include <ftl/finalizer.h> +#include <ftl/ignore.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <utils/String16.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> + +#include "test_framework/surfaceflinger/SFController.h" + +namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger { + +namespace { + +auto waitForSurfaceFlingerAIDL() -> sp<gui::ISurfaceComposer> { + constexpr auto kTimeout = std::chrono::seconds(30); + constexpr auto kSurfaceFlingerServiceName = "SurfaceFlingerAIDL"; + const sp<android::IServiceManager> serviceManager(android::defaultServiceManager()); + const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout; + + LOG(INFO) << "Waiting " << kTimeout << " for service manager registration...."; + sp<android::IBinder> flingerService; + while (flingerService == nullptr) { + if (std::chrono::steady_clock::now() > kTimeoutAfter) { + LOG(INFO) << "... Timeout!"; + return nullptr; + } + + constexpr auto sleepTime = std::chrono::milliseconds(10); + std::this_thread::sleep_for(sleepTime); + flingerService = serviceManager->checkService(String16(kSurfaceFlingerServiceName)); + } + LOG(INFO) << "Obtained surfaceflinger interface from service manager."; + + return interface_cast<gui::ISurfaceComposer>(flingerService); +} + +} // namespace + +struct SFController::Passkey final {}; + +void SFController::useHwcService(std::string_view fqn) { + base::SetProperty("debug.sf.hwc_service_name", std::string(fqn)); +} + +auto SFController::make() -> base::expected<std::shared_ptr<SFController>, std::string> { + using namespace std::string_literals; + + auto controller = std::make_unique<SFController>(Passkey{}); + if (controller == nullptr) { + return base::unexpected("Failed to construct the SFController instance."s); + } + + if (auto result = controller->init(); !result) { + return base::unexpected("Failed to init the SFController instance: "s + result.error()); + } + + return controller; +} + +SFController::SFController(Passkey passkey) { + ftl::ignore(passkey); +} + +auto SFController::init() -> base::expected<void, std::string> { + LOG(INFO) << "Stopping everything to prepare for tests"; + // NOLINTBEGIN(cert-env33-c) + system("stop"); + // NOLINTEND(cert-env33-c) + + mCleanup = ftl::Finalizer([this]() { stop(); }); + + return {}; +} + +auto SFController::startAndConnect() -> base::expected<void, std::string> { + using namespace std::string_literals; + + start(); + + LOG(VERBOSE) << "Getting ISurfaceComposer...."; + auto surfaceComposerAidl = waitForSurfaceFlingerAIDL(); + if (surfaceComposerAidl == nullptr) { + return base::unexpected("Failed to obtain the surfaceComposerAidl interface."s); + } + LOG(VERBOSE) << "Getting ISurfaceComposerClient...."; + sp<gui::ISurfaceComposerClient> surfaceComposerClientAidl; + if (!surfaceComposerAidl->createConnection(&surfaceComposerClientAidl).isOk()) { + return base::unexpected("Failed to obtain the surfaceComposerClientAidl interface."s); + } + if (surfaceComposerClientAidl == nullptr) { + return base::unexpected("Failed to obtain a valid surfaceComposerClientAidl interface."s); + } + auto surfaceComposerClient = sp<SurfaceComposerClient>::make(surfaceComposerClientAidl); + if (surfaceComposerClient == nullptr) { + return base::unexpected( + "Failed to construct a surfaceComposerClient around the aidl interface."s); + } + + mSurfaceComposerAidl = std::move(surfaceComposerAidl); + mSurfaceComposerClientAidl = std::move(surfaceComposerClientAidl); + mSurfaceComposerClient = std::move(surfaceComposerClient); + + LOG(INFO) << "Connected to surfaceflinger"; + return {}; +} + +void SFController::start() { + LOG(INFO) << "Starting surfaceflinger"; + // NOLINTBEGIN(cert-env33-c) + system("start surfaceflinger"); + // NOLINTEND(cert-env33-c) +} + +void SFController::stop() { + LOG(INFO) << "Stopping surfaceflinger"; + // NOLINTBEGIN(cert-env33-c) + system("stop surfaceflinger"); + // NOLINTEND(cert-env33-c) + + if (mSurfaceComposerAidl != nullptr) { + LOG(INFO) << "Waiting for SF AIDL interface to die"; + + constexpr auto kTimeout = std::chrono::seconds(30); + const auto binder = android::gui::ISurfaceComposer::asBinder(mSurfaceComposerAidl); + const auto kTimeoutAfter = std::chrono::steady_clock::now() + kTimeout; + + while (binder->isBinderAlive()) { + if (std::chrono::steady_clock::now() > kTimeoutAfter) { + LOG(INFO) << "... Timeout!"; + break; + } + + ftl::ignore = binder->pingBinder(); + + constexpr auto kPollInterval = std::chrono::milliseconds(10); + std::this_thread::sleep_for(kPollInterval); + } + + constexpr auto kShutdownWait = std::chrono::milliseconds(500); + std::this_thread::sleep_for(kShutdownWait); + } + + mSurfaceComposerClient = nullptr; + mSurfaceComposerClientAidl = nullptr; + mSurfaceComposerAidl = nullptr; +} + +} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger diff --git a/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h new file mode 100644 index 0000000000..58bac9197f --- /dev/null +++ b/services/surfaceflinger/tests/end2end/test_framework/surfaceflinger/SFController.h @@ -0,0 +1,70 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <memory> +#include <string> +#include <string_view> + +#include <android-base/expected.h> +#include <ftl/finalizer.h> +#include <utils/StrongPointer.h> + +namespace android::gui { + +class ISurfaceComposer; +class ISurfaceComposerClient; + +} // namespace android::gui + +namespace android { + +class SurfaceComposerClient; + +} // namespace android + +namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger { + +class SFController final { + struct Passkey; // Uses the passkey idiom to restrict construction. + + public: + // Sets a property so that SurfaceFlinger uses the named HWC service. + static void useHwcService(std::string_view fqn); + + // Makes an instance of the SFController. + [[nodiscard]] static auto make() -> base::expected<std::shared_ptr<SFController>, std::string>; + + explicit SFController(Passkey pass); + + // Starts SurfaceFlinger and establishes the AIDL interface connections. + [[nodiscard]] auto startAndConnect() -> base::expected<void, std::string>; + + private: + [[nodiscard]] auto init() -> base::expected<void, std::string>; + static void start(); + void stop(); + + sp<gui::ISurfaceComposer> mSurfaceComposerAidl; + sp<gui::ISurfaceComposerClient> mSurfaceComposerClientAidl; + sp<SurfaceComposerClient> mSurfaceComposerClient; + + // Finalizers should be last so their destructors are invoked first. + ftl::FinalizerFtl mCleanup; +}; + +} // namespace android::surfaceflinger::tests::end2end::test_framework::surfaceflinger diff --git a/services/surfaceflinger/tests/end2end/tests/.clang-tidy b/services/surfaceflinger/tests/end2end/tests/.clang-tidy new file mode 100644 index 0000000000..4924c466c0 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/tests/.clang-tidy @@ -0,0 +1,32 @@ +# Copyright 2025 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FormatStyle: file +InheritParentConfig: true + +# Note: For tests, we are actually turning off certain checks enabled for the +# non-test code in the parent .clang-tidy file. +Checks: + - -cppcoreguidelines-avoid-magic-numbers # Allow tests to use magic numbers. + - -cppcoreguidelines-avoid-goto # Google Test macros use goto. + - -cppcoreguidelines-avoid-non-const-global-variables # Google Test macros define global variables. + - -cppcoreguidelines-macro-usage # Google Benchmark defines function-like macros. + - -cppcoreguidelines-owning-memory # Google Test macros use operator new directly. + - -google-runtime-int # Tests might intentionally use the base "short"/"long" types and not want to use "int16"/"int64". + - -misc-use-anonymous-namespace # Google Test macros declare some static global variables to not export them. + - -modernize-use-trailing-return-type # Google Test macros use non-trailing return types. + - -performance-move-const-arg # Tests might std::move() a trivially copyable value as part of testing that moving works. + - -readability-function-cognitive-complexity # Assertions turn into extra branches, increasing apparent complexity. + - -readability-magic-numbers # Allow tests to use magic numbers + diff --git a/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp new file mode 100644 index 0000000000..3c4277f8a0 --- /dev/null +++ b/services/surfaceflinger/tests/end2end/tests/Placeholder_test.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <android-base/logging.h> + +#include "test_framework/core/TestService.h" + +namespace android::surfaceflinger::tests::end2end { +namespace { + +struct Placeholder : public ::testing::Test {}; + +TEST_F(Placeholder, Bringup) { + auto serviceResult = test_framework::core::TestService::startWithDisplays({ + {.id = 123}, + }); + if (!serviceResult) { + LOG(WARNING) << "End2End service not available. " << serviceResult.error(); + GTEST_SKIP() << "End2End service not available. " << serviceResult.error(); + } +} + +} // namespace +} // namespace android::surfaceflinger::tests::end2end |