diff options
41 files changed, 2004 insertions, 150 deletions
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index fdac5db1d1..316f04cb27 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -316,97 +316,6 @@ on late-init && property:ro.boot.fastboot.boottrace= write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 -# Only create the tracing instance if persist.mm_events.enabled -# Attempting to remove the tracing instance after it has been created -# will likely fail with EBUSY as it would be in use by traced_probes. -on mm_events_property_available && property:persist.mm_events.enabled=true -# Create MM Events Tracing Instance for Kmem Activity Trigger - mkdir /sys/kernel/debug/tracing/instances/mm_events 0755 system system - mkdir /sys/kernel/tracing/instances/mm_events 0755 system system - -# Read and set per CPU buffer size - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb - chmod 0666 /sys/kernel/tracing/instances/mm_events/buffer_size_kb - -# Set the default buffer size to the minimum - write /sys/kernel/debug/tracing/instances/mm_events/buffer_size_kb 1 - write /sys/kernel/tracing/instances/mm_events/buffer_size_kb 1 - -# Read and enable tracing - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/tracing_on - chmod 0666 /sys/kernel/tracing/instances/mm_events/tracing_on - -# Tracing disabled by default - write /sys/kernel/debug/tracing/instances/mm_events/tracing_on 0 - write /sys/kernel/tracing/instances/mm_events/tracing_on 0 - -# Read and truncate kernel trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/trace - -# Enable trace events - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_direct_reclaim_begin/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/vmscan/mm_vmscan_kswapd_wake/enable - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - chmod 0666 /sys/kernel/tracing/instances/mm_events/events/compaction/mm_compaction_begin/enable - -# Read and clear per-CPU raw kernel trace -# Cannot use wildcards in .rc files. Update this if there is a phone with -# more CPUs. - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu0/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu1/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu2/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu3/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu4/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu5/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu6/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu7/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu8/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu9/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu10/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu11/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu12/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu13/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu14/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu15/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu16/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu17/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu18/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu19/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu20/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu21/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu22/trace - chmod 0666 /sys/kernel/debug/tracing/instances/mm_events/per_cpu/cpu23/trace - chmod 0666 /sys/kernel/tracing/instances/mm_events/per_cpu/cpu23/trace - -on property:ro.persistent_properties.ready=true - trigger mm_events_property_available - # Handle hyp tracing instance on late-init && property:ro.boot.hypervisor.vm.supported=1 diff --git a/include/android/configuration.h b/include/android/configuration.h index 46c7dfeceb..a291db0ecd 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -26,6 +26,7 @@ #ifndef ANDROID_CONFIGURATION_H #define ANDROID_CONFIGURATION_H +#include <stdint.h> #include <sys/cdefs.h> #include <android/asset_manager.h> diff --git a/include/android/thermal.h b/include/android/thermal.h index e5d46b5b8a..93ae961423 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -304,6 +304,26 @@ int AThermal_getThermalHeadroomThresholds(AThermalManager* _Nonnull manager, * Prototype of the function that is called when thermal headroom or thresholds changes. * It's passed the updated thermal headroom and thresholds as parameters, as well as the * pointer provided by the client that registered a callback. + * <p> + * This may not be used to fully replace the {@link AThermal_getThermalHeadroom} API as it will + * only notify on one of the conditions below that will significantly change one or both + * values of current headroom and headroom thresholds since previous callback: + * 1. thermal throttling events: when the skin temperature has cross any of the thresholds + * and there isn't a previous callback in a short time ago with similar values. + * 2. skin temperature threshold change events: note that if the absolute °C threshold + * values change in a way that does not significantly change the current headroom nor + * headroom thresholds, it will not trigger any callback. The client should not + * need to take action in such case since the difference from temperature vs threshold + * hasn't changed. + * <p> + * By API version 36, it provides a forecast in the same call for developer's convenience + * based on a {@code forecastSeconds} defined by the device, which can be static or dynamic + * varied by OEM. Be aware that it will not notify on forecast temperature change but the + * events mentioned above. So periodically polling against {@link AThermal_getThermalHeadroom} + * API should still be used to actively monitor temperature forecast in advance. + * <p> + * This serves as a more advanced option compared to thermal status listener, where the + * latter will only notify on thermal throttling events with status update. * * @param data The data pointer to be passed when callback is called. * @param headroom The current non-negative normalized headroom value, also see diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index bc7ae37ff0..9883eb2672 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -445,6 +445,9 @@ status_t BBinder::linkToDeath( const sp<DeathRecipient>& /*recipient*/, void* /*cookie*/, uint32_t /*flags*/) { + // BBinder::linkToDeath is invalid because this process owns this binder. + // The DeathRecipient is called on BpBinders when the process owning the + // binder node dies. return INVALID_OPERATION; } 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/Entry.h b/services/inputflinger/dispatcher/Entry.h index becfb05f67..9cd76c7d11 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -125,10 +125,17 @@ struct KeyEntry : EventEntry { bool syntheticRepeat; // set to true for synthetic key repeats enum class InterceptKeyResult { + // The interception result is unknown. UNKNOWN, + // The event should be skipped and not sent to the application. SKIP, + // The event should be sent to the application. CONTINUE, + // The event should eventually be sent to the application, after a delay. TRY_AGAIN_LATER, + // The event should not be initially sent to the application, but instead go through + // post-processing to generate a fallback key event and then sent to the application. + FALLBACK, }; // These are special fields that may need to be modified while the event is being dispatched. mutable InterceptKeyResult interceptKeyResult; // set based on the interception result diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 95e1c06615..2908c61182 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -952,7 +952,7 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, new LatencyAggregatorWithHistograms())) : std::move(std::unique_ptr<InputEventTimelineProcessor>( new LatencyAggregator()))), - mLatencyTracker(*mInputEventTimelineProcessor) { + mLatencyTracker(*mInputEventTimelineProcessor, mInputDevices) { mReporter = createInputReporter(); mWindowInfoListener = sp<DispatcherWindowListener>::make(*this); @@ -1961,11 +1961,74 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con } } + if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::FALLBACK) { + findAndDispatchFallbackEvent(currentTime, entry, inputTargets); + // Drop the key. + return true; + } + // Dispatch the key. dispatchEventLocked(currentTime, entry, inputTargets); return true; } +void InputDispatcher::findAndDispatchFallbackEvent(nsecs_t currentTime, + std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) { + // Find the fallback associated with the incoming key event and dispatch it. + KeyEvent event = createKeyEvent(*entry); + const int32_t originalKeyCode = entry->keyCode; + + // Fetch the fallback event. + KeyCharacterMap::FallbackAction fallback; + for (const InputDeviceInfo& deviceInfo : mInputDevices) { + if (deviceInfo.getId() == entry->deviceId) { + const KeyCharacterMap* map = deviceInfo.getKeyCharacterMap(); + + LOG_ALWAYS_FATAL_IF(map == nullptr, "No KeyCharacterMap for device %d", + entry->deviceId); + map->getFallbackAction(entry->keyCode, entry->metaState, &fallback); + break; + } + } + + if (fallback.keyCode == AKEYCODE_UNKNOWN) { + // No fallback detected. + return; + } + + std::unique_ptr<KeyEntry> fallbackKeyEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), entry->injectionState, + event.getEventTime(), event.getDeviceId(), event.getSource(), + event.getDisplayId(), entry->policyFlags, entry->action, + event.getFlags() | AKEY_EVENT_FLAG_FALLBACK, + fallback.keyCode, event.getScanCode(), /*metaState=*/0, + event.getRepeatCount(), event.getDownTime()); + + if (mTracer) { + fallbackKeyEntry->traceTracker = + mTracer->traceDerivedEvent(*fallbackKeyEntry, *entry->traceTracker); + } + + for (const InputTarget& inputTarget : inputTargets) { + std::shared_ptr<Connection> connection = inputTarget.connection; + if (!connection->responsive || (connection->status != Connection::Status::NORMAL)) { + return; + } + + connection->inputState.setFallbackKey(originalKeyCode, fallback.keyCode); + if (entry->action == AKEY_EVENT_ACTION_UP) { + connection->inputState.removeFallbackKey(originalKeyCode); + } + + if (mTracer) { + mTracer->dispatchToTargetHint(*fallbackKeyEntry->traceTracker, inputTarget); + } + enqueueDispatchEntryAndStartDispatchCycleLocked(currentTime, connection, + std::move(fallbackKeyEntry), inputTarget); + } +} + void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& entry) { LOG_IF(INFO, DEBUG_OUTBOUND_EVENT_DETAILS) << prefix << "eventTime=" << entry.eventTime << ", deviceId=" << entry.deviceId @@ -2447,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) { @@ -2487,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(); @@ -2514,7 +2596,8 @@ InputDispatcher::DispatcherTouchState::findTouchedWindowTargets( tempTouchState.addOrUpdateWindow(wallpaper, InputTarget::DispatchMode::AS_IS, wallpaperFlags, entry.deviceId, {pointer}, - entry.eventTime); + entry.eventTime, + /*forwardingWindowToken=*/nullptr); } } } @@ -2613,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, @@ -4346,7 +4430,7 @@ void InputDispatcher::notifyInputDevicesChanged(const NotifyInputDevicesChangedA std::scoped_lock _l(mLock); // Reset key repeating in case a keyboard device was added or removed or something. resetKeyRepeatLocked(); - mLatencyTracker.setInputDevices(args.inputDeviceInfos); + mInputDevices = args.inputDeviceInfos; } void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { @@ -5770,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; @@ -5784,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; } @@ -5828,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()) { @@ -5871,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); @@ -5954,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) { @@ -7132,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); } } @@ -7173,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 38f782573a..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); @@ -918,10 +919,14 @@ private: std::unique_ptr<const KeyEntry> afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry* dispatchEntry, bool handled) REQUIRES(mLock); + void findAndDispatchFallbackEvent(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, + std::vector<InputTarget>& inputTargets) REQUIRES(mLock); // Statistics gathering. nsecs_t mLastStatisticPushTime = 0; std::unique_ptr<InputEventTimelineProcessor> mInputEventTimelineProcessor GUARDED_BY(mLock); + // Must outlive `mLatencyTracker`. + std::vector<InputDeviceInfo> mInputDevices; LatencyTracker mLatencyTracker GUARDED_BY(mLock); void traceInboundQueueLengthLocked() REQUIRES(mLock); void traceOutboundQueueLength(const Connection& connection); diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp index 0921e37d03..7c23694fbd 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -67,8 +67,9 @@ static void eraseByValue(std::multimap<K, V>& map, const V& value) { } // namespace -LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor) - : mTimelineProcessor(&processor) {} +LatencyTracker::LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices) + : mTimelineProcessor(&processor), mInputDevices(inputDevices) {} void LatencyTracker::trackListener(const NotifyArgs& args) { if (const NotifyKeyArgs* keyArgs = std::get_if<NotifyKeyArgs>(&args)) { @@ -248,8 +249,4 @@ std::string LatencyTracker::dump(const char* prefix) const { StringPrintf("%s mEventTimes.size() = %zu\n", prefix, mEventTimes.size()); } -void LatencyTracker::setInputDevices(const std::vector<InputDeviceInfo>& inputDevices) { - mInputDevices = inputDevices; -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h index 79ea14c4fb..7e2cc79249 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -20,9 +20,11 @@ #include <map> #include <unordered_map> +#include <vector> #include <binder/IBinder.h> #include <input/Input.h> +#include <input/InputDevice.h> #include "InputEventTimeline.h" #include "NotifyArgs.h" @@ -41,8 +43,10 @@ public: /** * Create a LatencyTracker. * param reportingFunction: the function that will be called in order to report full latency. + * param inputDevices: input devices relevant for tracking. */ - LatencyTracker(InputEventTimelineProcessor& processor); + LatencyTracker(InputEventTimelineProcessor& processor, + std::vector<InputDeviceInfo>& inputDevices); /** * Start keeping track of an event identified by the args. This must be called first. * If duplicate events are encountered (events that have the same eventId), none of them will be @@ -60,7 +64,6 @@ public: std::array<nsecs_t, GraphicsTimeline::SIZE> timeline); std::string dump(const char* prefix) const; - void setInputDevices(const std::vector<InputDeviceInfo>& inputDevices); private: /** @@ -81,7 +84,7 @@ private: std::multimap<nsecs_t /*eventTime*/, int32_t /*inputEventId*/> mEventTimes; InputEventTimelineProcessor* mTimelineProcessor; - std::vector<InputDeviceInfo> mInputDevices; + std::vector<InputDeviceInfo>& mInputDevices; void trackListener(int32_t inputEventId, nsecs_t eventTime, nsecs_t readTime, DeviceId deviceId, const std::set<InputDeviceUsageSource>& sources, int32_t inputEventAction, 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/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index c982dab019..63eb357bdb 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -144,28 +144,39 @@ public: // records it if so. void processGesture(const TouchpadInputMapper::MetricsIdentifier& id, const Gesture& gesture) { std::scoped_lock lock(mLock); + Counters& counters = mCounters[id]; switch (gesture.type) { case kGestureTypeFling: if (gesture.details.fling.fling_state == GESTURES_FLING_START) { // Indicates the end of a two-finger scroll gesture. - mCounters[id].twoFingerSwipeGestures++; + counters.twoFingerSwipeGestures++; } break; case kGestureTypeSwipeLift: - mCounters[id].threeFingerSwipeGestures++; + // The Gestures library occasionally outputs two lift gestures in a row, which can + // cause inaccurate metrics reporting. To work around this, deduplicate successive + // lift gestures. + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeSwipeLift) { + counters.threeFingerSwipeGestures++; + } break; case kGestureTypeFourFingerSwipeLift: - mCounters[id].fourFingerSwipeGestures++; + // TODO(b/404529050): fix the Gestures library, and remove this check. + if (counters.lastGestureType != kGestureTypeFourFingerSwipeLift) { + counters.fourFingerSwipeGestures++; + } break; case kGestureTypePinch: if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) { - mCounters[id].pinchGestures++; + counters.pinchGestures++; } break; default: // We're not interested in any other gestures. break; } + counters.lastGestureType = gesture.type; } private: @@ -214,6 +225,10 @@ private: int32_t threeFingerSwipeGestures = 0; int32_t fourFingerSwipeGestures = 0; int32_t pinchGestures = 0; + + // Records the last type of gesture received for this device, for deduplication purposes. + // TODO(b/404529050): fix the Gestures library and remove this field. + GestureType lastGestureType = kGestureTypeContactInitiated; }; // Metrics are aggregated by device model and version, so if two devices of the same model and diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 835b677e1e..298ba4209a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -37,6 +37,7 @@ #include <input/BlockingQueue.h> #include <input/Input.h> #include <input/InputConsumer.h> +#include <input/KeyCharacterMap.h> #include <input/PrintTools.h> #include <linux/input.h> #include <sys/epoll.h> @@ -139,6 +140,30 @@ static KeyEvent getTestKeyEvent() { return event; } +InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t productId, DeviceId deviceId) { + InputDeviceIdentifier identifier; + identifier.vendor = vendorId; + identifier.product = productId; + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "Test Device", + /*isExternal=*/false, /*hasMic=*/false, ui::LogicalDisplayId::INVALID); + return info; +} + +std::unique_ptr<KeyCharacterMap> loadKeyCharacterMap(const char* name) { + InputDeviceIdentifier identifier; + identifier.name = name; + std::string path = getInputDeviceConfigurationFilePathByName(identifier.getCanonicalName(), + InputDeviceConfigurationFileType:: + KEY_CHARACTER_MAP); + + if (path.empty()) { + return nullptr; + } + + return *KeyCharacterMap::load(path, KeyCharacterMap::Format::BASE); +} + } // namespace // --- InputDispatcherTest --- @@ -1151,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))); @@ -1183,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. @@ -6583,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))); } @@ -7119,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) { @@ -7159,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, @@ -7293,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, @@ -7480,6 +7654,50 @@ TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserAct mFakePolicy->assertUserActivityPoked(); } +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyFallbackKey) { +#if !defined(__ANDROID__) + GTEST_SKIP() << "b/253299089 Generic files are currently read directly from device."; +#endif + InputDeviceInfo testDevice = generateTestDeviceInfo(/*vendorId=*/0, + /*productId=*/0, /*deviceId=*/1); + std::unique_ptr<KeyCharacterMap> kcm = loadKeyCharacterMap("Generic"); + ASSERT_NE(nullptr, kcm); + + testDevice.setKeyCharacterMap(std::move(kcm)); + mDispatcher->notifyInputDevicesChanged(NotifyInputDevicesChangedArgs(/*id=*/1, {testDevice})); + + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", + ui::LogicalDisplayId::DEFAULT); + + window->setFocusable(true); + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + mFakePolicy->setInterceptKeyBeforeDispatchingResult( + inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK); + + // In the Generic KCM fallbacks, Meta + Space => SEARCH. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_SPACE) + .metaState(AMETA_META_ON) + .build()); + mDispatcher->waitForIdle(); + + // Should have poked user activity + mFakePolicy->assertUserActivityPoked(); + + // Fallback is generated and sent instead. + std::unique_ptr<KeyEvent> consumedEvent = window->consumeKey(/*handled=*/false); + ASSERT_NE(nullptr, consumedEvent); + ASSERT_THAT(*consumedEvent, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_SEARCH), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); +} + class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, public ::testing::WithParamInterface<bool> {}; @@ -12541,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); @@ -15113,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)); @@ -15195,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/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp index d8c5eac3c4..b7ca24bb61 100644 --- a/services/inputflinger/tests/LatencyTracker_test.cpp +++ b/services/inputflinger/tests/LatencyTracker_test.cpp @@ -19,10 +19,13 @@ #include "NotifyArgsBuilders.h" #include "android/input.h" +#include <vector> + #include <android-base/logging.h> #include <android-base/properties.h> #include <binder/Binder.h> #include <gtest/gtest.h> +#include <input/InputDevice.h> #include <input/PrintTools.h> #include <inttypes.h> #include <linux/input.h> @@ -51,11 +54,6 @@ static InputDeviceInfo generateTestDeviceInfo(uint16_t vendorId, uint16_t produc return info; } -void setDefaultInputDeviceInfo(LatencyTracker& tracker) { - InputDeviceInfo deviceInfo = generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID); - tracker.setInputDevices({deviceInfo}); -} - const auto FIRST_TOUCH_POINTER = PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200); const auto FIRST_MOUSE_POINTER = PointerBuilder(/*id=*/1, ToolType::MOUSE); @@ -120,13 +118,14 @@ protected: std::unique_ptr<LatencyTracker> mTracker; sp<IBinder> connection1; sp<IBinder> connection2; + std::vector<InputDeviceInfo> inputDevices; void SetUp() override { connection1 = sp<BBinder>::make(); connection2 = sp<BBinder>::make(); - mTracker = std::make_unique<LatencyTracker>(*this); - setDefaultInputDeviceInfo(*mTracker); + inputDevices.push_back(generateTestDeviceInfo(/*vendorId=*/0, /*productId=*/0, DEVICE_ID)); + mTracker = std::make_unique<LatencyTracker>(*this, inputDevices); } void TearDown() override {} @@ -140,6 +139,10 @@ protected: */ void assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines); + void updateInputDevices(const std::vector<InputDeviceInfo>& inputDevicesUpdated) { + inputDevices = inputDevicesUpdated; + } + private: void processTimeline(const InputEventTimeline& timeline) override { mReceivedTimelines.push_back(timeline); @@ -448,7 +451,7 @@ TEST_F(LatencyTrackerTest, TrackListenerCheck_DeviceInfoFieldsInputEventTimeline deviceInfo2.addSource(AINPUT_SOURCE_TOUCHSCREEN); deviceInfo2.addSource(AINPUT_SOURCE_STYLUS); - mTracker->setInputDevices({deviceInfo1, deviceInfo2}); + updateInputDevices({deviceInfo1, deviceInfo2}); mTracker->trackListener( MotionArgsBuilder(AMOTION_EVENT_ACTION_CANCEL, AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS, inputEventId) diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp index 157a3338da..9c027fa00c 100644 --- a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp +++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp @@ -17,6 +17,8 @@ #include <fuzzer/FuzzedDataProvider.h> #include <linux/input.h> +#include <vector> + #include "../../InputDeviceMetricsSource.h" #include "../InputEventTimeline.h" #include "NotifyArgsBuilders.h" @@ -58,7 +60,8 @@ extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); EmptyProcessor emptyProcessor; - LatencyTracker tracker(emptyProcessor); + std::vector<InputDeviceInfo> emptyDevices; + LatencyTracker tracker(emptyProcessor, emptyDevices); // Make some pre-defined tokens to ensure that some timelines are complete. std::array<sp<IBinder> /*token*/, 10> predefinedTokens; 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 diff --git a/vulkan/scripts/vkjson_generator.py b/vulkan/scripts/vkjson_generator.py index 7dc55d9703..6f621a1542 100644 --- a/vulkan/scripts/vkjson_generator.py +++ b/vulkan/scripts/vkjson_generator.py @@ -184,12 +184,22 @@ def generate_vk_core_struct_definition(f): vkJson_core_entries.append(f"{struct_name} {version.lower()}") f.write(f"struct {struct_name} {{\n") + f.write(f" {struct_name}() {{\n") # Start of constructor + for item in items: + for struct_type, _ in item.items(): + field_name = "properties" if "Properties" in struct_type else "features" + f.write(f" memset(&{field_name}, 0, sizeof({struct_type}));\n") + f.write(" }\n") # End of constructor for item in items: for struct_type, _ in item.items(): field_name = "properties" if "Properties" in struct_type else "features" f.write(f" {struct_type} {field_name};\n") + if version == "Core14": + f.write(f"std::vector<VkImageLayout> copy_src_layouts;\n") + f.write(f"std::vector<VkImageLayout> copy_dst_layouts;\n") + f.write("};\n\n") return vkJson_core_entries @@ -212,11 +222,6 @@ def generate_memset_statements(f): f.write(f"memset(&{variable_name}, 0, sizeof({class_name}));\n") entries.append(f"{class_name} {variable_name}") - # Process vulkan core structs - for version in VK.VULKAN_CORES_AND_STRUCTS_MAPPING["versions"]: - struct_name = f"VkJson{version}" - f.write(f"memset(&{version.lower()}, 0, sizeof({struct_name}));\n") - return entries @@ -1757,6 +1762,21 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { if (device.properties.apiVersion >= VK_API_VERSION_1_4) {\n""") f.write(cc_code_properties_14) f.write(f"vkGetPhysicalDeviceProperties2(physical_device, &properties);\n\n") + + f.write("""\ +if (device.core14.properties.copySrcLayoutCount > 0 || device.core14.properties.copyDstLayoutCount > 0 ) { + if (device.core14.properties.copySrcLayoutCount > 0) { + device.core14.copy_src_layouts.resize(device.core14.properties.copySrcLayoutCount); + device.core14.properties.pCopySrcLayouts = device.core14.copy_src_layouts.data(); + } + if (device.core14.properties.copyDstLayoutCount > 0) { + device.core14.copy_dst_layouts.resize(device.core14.properties.copyDstLayoutCount); + device.core14.properties.pCopyDstLayouts = device.core14.copy_dst_layouts.data(); + } + vkGetPhysicalDeviceProperties2(physical_device, &properties); +} + \n""") + f.write(cc_code_features_14) f.write(f"vkGetPhysicalDeviceFeatures2(physical_device, &features);\n\n") f.write("""\ diff --git a/vulkan/vkjson/vkjson.cc b/vulkan/vkjson/vkjson.cc index 531a623e90..517e62d1db 100644 --- a/vulkan/vkjson/vkjson.cc +++ b/vulkan/vkjson/vkjson.cc @@ -36,6 +36,9 @@ #include <type_traits> #include <utility> +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ namespace { /* diff --git a/vulkan/vkjson/vkjson.h b/vulkan/vkjson/vkjson.h index d74644ca8e..cfba8c58d9 100644 --- a/vulkan/vkjson/vkjson.h +++ b/vulkan/vkjson/vkjson.h @@ -33,6 +33,9 @@ #undef max #endif +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ struct VkJsonLayer { VkLayerProperties properties; std::vector<VkExtensionProperties> extensions; @@ -263,23 +266,41 @@ struct VkJsonKHRDriverProperties { }; struct VkJsonCore11 { + VkJsonCore11() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan11Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan11Features)); + } VkPhysicalDeviceVulkan11Properties properties; VkPhysicalDeviceVulkan11Features features; }; struct VkJsonCore12 { + VkJsonCore12() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan12Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan12Features)); + } VkPhysicalDeviceVulkan12Properties properties; VkPhysicalDeviceVulkan12Features features; }; struct VkJsonCore13 { + VkJsonCore13() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan13Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan13Features)); + } VkPhysicalDeviceVulkan13Properties properties; VkPhysicalDeviceVulkan13Features features; }; struct VkJsonCore14 { + VkJsonCore14() { + memset(&properties, 0, sizeof(VkPhysicalDeviceVulkan14Properties)); + memset(&features, 0, sizeof(VkPhysicalDeviceVulkan14Features)); + } VkPhysicalDeviceVulkan14Properties properties; VkPhysicalDeviceVulkan14Features features; + std::vector<VkImageLayout> copy_src_layouts; + std::vector<VkImageLayout> copy_dst_layouts; }; struct VkJsonDevice { @@ -306,10 +327,6 @@ struct VkJsonDevice { sizeof(VkPhysicalDeviceSamplerYcbcrConversionFeatures)); memset(&shader_draw_parameter_features, 0, sizeof(VkPhysicalDeviceShaderDrawParameterFeatures)); - memset(&core11, 0, sizeof(VkJsonCore11)); - memset(&core12, 0, sizeof(VkJsonCore12)); - memset(&core13, 0, sizeof(VkJsonCore13)); - memset(&core14, 0, sizeof(VkJsonCore14)); } VkJsonKHRVariablePointers khr_variable_pointers; VkJsonKHRShaderFloat16Int8 khr_shader_float16_int8; diff --git a/vulkan/vkjson/vkjson_instance.cc b/vulkan/vkjson/vkjson_instance.cc index 22b3204e61..636c11933a 100644 --- a/vulkan/vkjson/vkjson_instance.cc +++ b/vulkan/vkjson/vkjson_instance.cc @@ -27,6 +27,9 @@ #include <algorithm> #include <utility> +/* + * This file is autogenerated by vkjson_generator.py. Do not edit directly. + */ namespace { bool EnumerateExtensions(const char* layer_name, @@ -512,6 +515,23 @@ VkJsonDevice VkJsonGetDevice(VkPhysicalDevice physical_device) { vkGetPhysicalDeviceProperties2(physical_device, &properties); + if (device.core14.properties.copySrcLayoutCount > 0 || + device.core14.properties.copyDstLayoutCount > 0) { + if (device.core14.properties.copySrcLayoutCount > 0) { + device.core14.copy_src_layouts.resize( + device.core14.properties.copySrcLayoutCount); + device.core14.properties.pCopySrcLayouts = + device.core14.copy_src_layouts.data(); + } + if (device.core14.properties.copyDstLayoutCount > 0) { + device.core14.copy_dst_layouts.resize( + device.core14.properties.copyDstLayoutCount); + device.core14.properties.pCopyDstLayouts = + device.core14.copy_dst_layouts.data(); + } + vkGetPhysicalDeviceProperties2(physical_device, &properties); + } + device.core14.features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES; device.core14.features.pNext = features.pNext; |