diff options
21 files changed, 557 insertions, 276 deletions
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 97c467053e..000f458fd1 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -485,20 +485,26 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, mSyncedFrameNumbers.erase(callbackId.framenumber); } -void BLASTBufferQueue::acquireNextBufferLocked( +status_t BLASTBufferQueue::acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) { - // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't - // include the extra buffer when checking if we can acquire the next buffer. - const bool includeExtraAcquire = !transaction; - const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire); - if (mNumFrameAvailable == 0 || maxAcquired) { - BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired)); - return; + // Check if we have frames available and we have not acquired the maximum number of buffers. + // Even with this check, the consumer can fail to acquire an additional buffer if the consumer + // has already acquired (mMaxAcquiredBuffers + 1) and the new buffer is not droppable. In this + // case mBufferItemConsumer->acquireBuffer will return with NO_BUFFER_AVAILABLE. + if (mNumFrameAvailable == 0) { + BQA_LOGV("Can't acquire next buffer. No available frames"); + return BufferQueue::NO_BUFFER_AVAILABLE; + } + + if (mNumAcquired >= (mMaxAcquiredBuffers + 2)) { + BQA_LOGV("Can't acquire next buffer. Already acquired max frames %d max:%d + 2", + mNumAcquired, mMaxAcquiredBuffers); + return BufferQueue::NO_BUFFER_AVAILABLE; } if (mSurfaceControl == nullptr) { BQA_LOGE("ERROR : surface control is null"); - return; + return NAME_NOT_FOUND; } SurfaceComposerClient::Transaction localTransaction; @@ -515,10 +521,10 @@ void BLASTBufferQueue::acquireNextBufferLocked( mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false); if (status == BufferQueue::NO_BUFFER_AVAILABLE) { BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE"); - return; + return status; } else if (status != OK) { BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str()); - return; + return status; } auto buffer = bufferItem.mGraphicBuffer; @@ -528,7 +534,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( if (buffer == nullptr) { mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); BQA_LOGE("Buffer was empty"); - return; + return BAD_VALUE; } if (rejectBuffer(bufferItem)) { @@ -537,8 +543,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height, buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform); mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE); - acquireNextBufferLocked(transaction); - return; + return acquireNextBufferLocked(transaction); } mNumAcquired++; @@ -592,9 +597,23 @@ void BLASTBufferQueue::acquireNextBufferLocked( t->setDesiredPresentTime(bufferItem.mTimestamp); } - if (!mNextFrameTimelineInfoQueue.empty()) { - t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front()); - mNextFrameTimelineInfoQueue.pop(); + // Drop stale frame timeline infos + while (!mPendingFrameTimelines.empty() && + mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) { + ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64, + mPendingFrameTimelines.front().first, + mPendingFrameTimelines.front().second.vsyncId); + mPendingFrameTimelines.pop(); + } + + if (!mPendingFrameTimelines.empty() && + mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) { + ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64 + " vsyncId: %" PRId64, + bufferItem.mFrameNumber, + mPendingFrameTimelines.front().second.vsyncId); + t->setFrameTimelineInfo(mPendingFrameTimelines.front().second); + mPendingFrameTimelines.pop(); } { @@ -626,6 +645,7 @@ void BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "", static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(), bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform); + return OK; } Rect BLASTBufferQueue::computeCrop(const BufferItem& item) { @@ -648,44 +668,19 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence); } -void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { - if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) { - // We are waiting on a previous sync's transaction callback so allow another sync - // transaction to proceed. - // - // We need to first flush out the transactions that were in between the two syncs. - // We do this by merging them into mSyncTransaction so any buffer merging will get - // a release callback invoked. The release callback will be async so we need to wait - // on max acquired to make sure we have the capacity to acquire another buffer. - if (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting to flush shadow queue..."); - mCallbackCV.wait(lock); - } - while (mNumFrameAvailable > 0) { - // flush out the shadow queue - acquireAndReleaseBuffer(); - } - } - - while (maxBuffersAcquired(false /* includeExtraAcquire */)) { - BQA_LOGD("waiting for free buffer."); - mCallbackCV.wait(lock); - } -} - void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr; SurfaceComposerClient::Transaction* prevTransaction = nullptr; - bool waitForTransactionCallback = !mSyncedFrameNumbers.empty(); { - BBQ_TRACE(); std::unique_lock _lock{mMutex}; + BBQ_TRACE(); + + bool waitForTransactionCallback = !mSyncedFrameNumbers.empty(); const bool syncTransactionSet = mTransactionReadyCallback != nullptr; BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet)); if (syncTransactionSet) { - bool mayNeedToWaitForBuffer = true; // If we are going to re-use the same mSyncTransaction, release the buffer that may // already be set in the Transaction. This is to allow us a free slot early to continue // processing a new buffer. @@ -696,14 +691,29 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { bufferData->frameNumber); releaseBuffer(bufferData->generateReleaseCallbackId(), bufferData->acquireFence); - // Because we just released a buffer, we know there's no need to wait for a free - // buffer. - mayNeedToWaitForBuffer = false; } } - if (mayNeedToWaitForBuffer) { - flushAndWaitForFreeBuffer(_lock); + if (waitForTransactionCallback) { + // We are waiting on a previous sync's transaction callback so allow another sync + // transaction to proceed. + // + // We need to first flush out the transactions that were in between the two syncs. + // We do this by merging them into mSyncTransaction so any buffer merging will get + // a release callback invoked. + while (mNumFrameAvailable > 0) { + // flush out the shadow queue + acquireAndReleaseBuffer(); + } + } else { + // Make sure the frame available count is 0 before proceeding with a sync to ensure + // the correct frame is used for the sync. The only way mNumFrameAvailable would be + // greater than 0 is if we already ran out of buffers previously. This means we + // need to flush the buffers before proceeding with the sync. + while (mNumFrameAvailable > 0) { + BQA_LOGD("waiting until no queued buffers"); + mCallbackCV.wait(_lock); + } } } @@ -719,14 +729,23 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { item.mFrameNumber, boolToString(syncTransactionSet)); if (syncTransactionSet) { - acquireNextBufferLocked(mSyncTransaction); + // Add to mSyncedFrameNumbers before waiting in case any buffers are released + // while waiting for a free buffer. The release and commit callback will try to + // acquire buffers if there are any available, but we don't want it to acquire + // in the case where a sync transaction wants the buffer. + mSyncedFrameNumbers.emplace(item.mFrameNumber); + // If there's no available buffer and we're in a sync transaction, we need to wait + // instead of returning since we guarantee a buffer will be acquired for the sync. + while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) { + BQA_LOGD("waiting for available buffer"); + mCallbackCV.wait(_lock); + } // Only need a commit callback when syncing to ensure the buffer that's synced has been // sent to SF incStrong((void*)transactionCommittedCallbackThunk); mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this)); - mSyncedFrameNumbers.emplace(item.mFrameNumber); if (mAcquireSingleBuffer) { prevCallback = mTransactionReadyCallback; prevTransaction = mSyncTransaction; @@ -829,15 +848,6 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) { return mSize != bufferSize; } -// Check if we have acquired the maximum number of buffers. -// Consumer can acquire an additional buffer if that buffer is not droppable. Set -// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state -// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE. -bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const { - int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1); - return mNumAcquired >= maxAcquiredBuffers; -} - class BBQSurface : public Surface { private: std::mutex mMutex; @@ -874,12 +884,13 @@ public: return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy); } - status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override { + status_t setFrameTimelineInfo(uint64_t frameNumber, + const FrameTimelineInfo& frameTimelineInfo) override { std::unique_lock _lock{mMutex}; if (mDestroyed) { return DEAD_OBJECT; } - return mBbq->setFrameTimelineInfo(frameTimelineInfo); + return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo); } void destroy() override { @@ -901,9 +912,12 @@ status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility, return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply(); } -status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { +status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber, + const FrameTimelineInfo& frameTimelineInfo) { + ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(), + frameNumber, frameTimelineInfo.vsyncId); std::unique_lock _lock{mMutex}; - mNextFrameTimelineInfoQueue.push(frameTimelineInfo); + mPendingFrameTimelines.push({frameNumber, frameTimelineInfo}); return OK; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 100e36e7b3..16edfd4267 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1869,12 +1869,13 @@ int Surface::dispatchGetLastQueuedBuffer2(va_list args) { int Surface::dispatchSetFrameTimelineInfo(va_list args) { ATRACE_CALL(); + auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t)); auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t)); auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t)); auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t)); ALOGV("Surface::%s", __func__); - return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos}); + return setFrameTimelineInfo(frameNumber, {frameTimelineVsyncId, inputEventId, startTimeNanos}); } bool Surface::transformToDisplayInverse() const { @@ -2648,7 +2649,8 @@ status_t Surface::setFrameRate(float frameRate, int8_t compatibility, changeFrameRateStrategy); } -status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) { +status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/, + const FrameTimelineInfo& frameTimelineInfo) { return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo); } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 0f5192d41c..05beb07891 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1274,8 +1274,11 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha mStatus = BAD_INDEX; return *this; } + if (alpha < 0.0f || alpha > 1.0f) { + ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha); + } s->what |= layer_state_t::eAlphaChanged; - s->alpha = alpha; + s->alpha = std::clamp(alpha, 0.f, 1.f); registerSurfaceControlForCallback(sc); return *this; diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 1278931202..40ffea6809 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -111,7 +111,7 @@ public: void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format); status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); - status_t setFrameTimelineInfo(const FrameTimelineInfo& info); + status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info); void setSidebandStream(const sp<NativeHandle>& stream); @@ -141,12 +141,11 @@ private: void resizeFrameEventHistory(size_t newSize); - void acquireNextBufferLocked( + status_t acquireNextBufferLocked( const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); // Return true if we need to reject the buffer based on the scaling mode and the buffer size. bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); - bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) REQUIRES(mMutex); @@ -155,7 +154,6 @@ private: void acquireAndReleaseBuffer() REQUIRES(mMutex); void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) REQUIRES(mMutex); - void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock); std::string mName; // Represents the queued buffer count from buffer queue, @@ -244,7 +242,7 @@ private: std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>> mPendingTransactions GUARDED_BY(mMutex); - std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex); + std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex); // Tracks the last acquired frame number uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 77615fe4c1..4a552b6643 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -211,7 +211,7 @@ public: virtual status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy); - virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info); + virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info); protected: virtual ~Surface(); diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h index e5d268445c..00096158e7 100644 --- a/libs/gui/include/gui/TraceUtils.h +++ b/libs/gui/include/gui/TraceUtils.h @@ -27,6 +27,8 @@ #define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__) +#define ATRACE_FORMAT_INSTANT(fmt, ...) TraceUtils::intantFormat(fmt, ##__VA_ARGS__) + namespace android { class TraceUtils { @@ -50,6 +52,20 @@ public: ATRACE_BEGIN(buf); } + static void intantFormat(const char* fmt, ...) { + if (CC_LIKELY(!ATRACE_ENABLED())) return; + + const int BUFFER_SIZE = 256; + va_list ap; + char buf[BUFFER_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUFFER_SIZE, fmt, ap); + va_end(ap); + + ATRACE_INSTANT(buf); + } + }; // class TraceUtils } /* namespace android */ diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index a54af1fa62..86e76c4f2c 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1043,11 +1043,12 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo } static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window, + uint64_t frameNumber, int64_t frameTimelineVsyncId, int32_t inputEventId, int64_t startTimeNanos) { - return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId, - inputEventId, startTimeNanos); + return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber, + frameTimelineVsyncId, inputEventId, startTimeNanos); } // ------------------------------------------------------------------------------------------------ diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 9b62894433..83ada8e778 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -27,6 +27,7 @@ #include <ftl/enum.h> #include <gui/SurfaceComposerClient.h> #include <input/InputDevice.h> +#include <openssl/mem.h> #include <powermanager/PowerManager.h> #include <unistd.h> #include <utils/Trace.h> @@ -2208,8 +2209,31 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the temporary touch state. BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[pointerIndex].id); - tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); + + // If this is the pointer going down and the touched window has a wallpaper + // then also add the touched wallpaper windows so they are locked in for the duration + // of the touch gesture. + // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper + // engine only supports touch events. We would need to add a mechanism similar + // to View.onGenericMotionEvent to enable wallpapers to handle these events. + if (maskedAction == AMOTION_EVENT_ACTION_DOWN || + maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + if ((targetFlags & InputTarget::FLAG_FOREGROUND) && + windowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { + sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle); + if (wallpaper != nullptr) { + int32_t wallpaperFlags = InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::FLAG_DISPATCH_AS_IS; + if (isSplit) { + wallpaperFlags |= InputTarget::FLAG_SPLIT; + } + tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds); + } + } + } } } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -2286,6 +2310,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[0].id); tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + + // Check if the wallpaper window should deliver the corresponding event. + slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, + tempTouchState, pointerIds); } } @@ -2391,39 +2419,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } - // If this is the first pointer going down and the touched window has a wallpaper - // then also add the touched wallpaper windows so they are locked in for the duration - // of the touch gesture. - // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper - // engine only supports touch events. We would need to add a mechanism similar - // to View.onGenericMotionEvent to enable wallpapers to handle these events. - if (maskedAction == AMOTION_EVENT_ACTION_DOWN) { - sp<WindowInfoHandle> foregroundWindowHandle = - tempTouchState.getFirstForegroundWindowHandle(); - if (foregroundWindowHandle && - foregroundWindowHandle->getInfo()->inputConfig.test( - WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { - const std::vector<sp<WindowInfoHandle>>& windowHandles = - getWindowHandlesLocked(displayId); - for (const sp<WindowInfoHandle>& windowHandle : windowHandles) { - const WindowInfo* info = windowHandle->getInfo(); - if (info->displayId == displayId && - windowHandle->getInfo()->inputConfig.test( - WindowInfo::InputConfig::IS_WALLPAPER)) { - BitSet32 pointerIds; - pointerIds.markBit(entry.pointerProperties[0].id); - tempTouchState - .addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED | - InputTarget:: - FLAG_WINDOW_IS_PARTIALLY_OBSCURED | - InputTarget::FLAG_DISPATCH_AS_IS, - pointerIds); - } - } - } - } - // Success! Output targets. injectionResult = InputEventInjectionResult::SUCCEEDED; @@ -3702,7 +3697,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( - const sp<Connection>& connection) { + const sp<Connection>& connection, int32_t targetFlags) { if (connection->status == Connection::Status::BROKEN) { return; } @@ -3730,7 +3725,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = targetFlags; const bool wasEmpty = connection->outboundQueue.empty(); @@ -3765,6 +3760,16 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( } } +void InputDispatcher::synthesizeCancelationEventsForWindowLocked( + const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { + if (windowHandle != nullptr) { + sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken()); + if (wallpaperConnection != nullptr) { + synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); + } + } +} + std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); @@ -4450,7 +4455,7 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu if (calculatedHmac == INVALID_HMAC) { return nullptr; } - if (calculatedHmac != event.getHmac()) { + if (0 != CRYPTO_memcmp(calculatedHmac.data(), event.getHmac().data(), calculatedHmac.size())) { return nullptr; } return result; @@ -4792,14 +4797,7 @@ void InputDispatcher::setInputWindowsLocked( touchedWindow.windowHandle->getInfo()->inputConfig.test( gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow(); - if (wallpaper != nullptr) { - sp<Connection> wallpaperConnection = - getConnectionLocked(wallpaper->getToken()); - if (wallpaperConnection != nullptr) { - synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, - options); - } - } + synthesizeCancelationEventsForWindowLocked(wallpaper, options); } } state.windows.erase(state.windows.begin() + i); @@ -5112,6 +5110,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. int32_t oldTargetFlags = touchedWindow->targetFlags; BitSet32 pointerIds = touchedWindow->pointerIds; + sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); // Add new window. @@ -5143,7 +5142,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< options(CancelationOptions::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); - synthesizePointerDownEventsForConnectionLocked(toConnection); + synthesizePointerDownEventsForConnectionLocked(toConnection, newTargetFlags); + // Check if the wallpaper window should deliver the corresponding event. + transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle, + *state, pointerIds); } if (DEBUG_FOCUS) { @@ -6428,4 +6430,97 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon mMonitorDispatchingTimeout = timeout; } +void InputDispatcher::slipWallpaperTouch(int32_t targetFlags, + const sp<WindowInfoHandle>& oldWindowHandle, + const sp<WindowInfoHandle>& newWindowHandle, + TouchState& state, const BitSet32& pointerIds) { + const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const bool newHasWallpaper = (targetFlags & InputTarget::FLAG_FOREGROUND) && + newWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const sp<WindowInfoHandle> oldWallpaper = + oldHasWallpaper ? state.getWallpaperWindow() : nullptr; + const sp<WindowInfoHandle> newWallpaper = + newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr; + if (oldWallpaper == newWallpaper) { + return; + } + + if (oldWallpaper != nullptr) { + state.addOrUpdateWindow(oldWallpaper, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + BitSet32(0)); + } + + if (newWallpaper != nullptr) { + state.addOrUpdateWindow(newWallpaper, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER | + InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED, + pointerIds); + } +} + +void InputDispatcher::transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags, + const sp<WindowInfoHandle> fromWindowHandle, + const sp<WindowInfoHandle> toWindowHandle, + TouchState& state, const BitSet32& pointerIds) { + const bool oldHasWallpaper = (oldTargetFlags & InputTarget::FLAG_FOREGROUND) && + fromWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const bool newHasWallpaper = (newTargetFlags & InputTarget::FLAG_FOREGROUND) && + toWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + + const sp<WindowInfoHandle> oldWallpaper = + oldHasWallpaper ? state.getWallpaperWindow() : nullptr; + const sp<WindowInfoHandle> newWallpaper = + newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr; + if (oldWallpaper == newWallpaper) { + return; + } + + if (oldWallpaper != nullptr) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, + "transferring touch focus to another window"); + state.removeWindowByToken(oldWallpaper->getToken()); + synthesizeCancelationEventsForWindowLocked(oldWallpaper, options); + } + + if (newWallpaper != nullptr) { + int32_t wallpaperFlags = + oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); + wallpaperFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds); + sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken()); + if (wallpaperConnection != nullptr) { + sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken()); + toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState); + synthesizePointerDownEventsForConnectionLocked(wallpaperConnection, wallpaperFlags); + } + } +} + +sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( + const sp<WindowInfoHandle>& windowHandle) const { + const std::vector<sp<WindowInfoHandle>>& windowHandles = + getWindowHandlesLocked(windowHandle->getInfo()->displayId); + bool foundWindow = false; + for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { + if (!foundWindow && otherHandle != windowHandle) { + continue; + } + if (windowHandle == otherHandle) { + foundWindow = true; + continue; + } + + if (otherHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::IS_WALLPAPER)) { + return otherHandle; + } + } + return nullptr; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 24e7432869..7769b9eabf 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -622,8 +622,12 @@ private: const CancelationOptions& options) REQUIRES(mLock); - void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection) - REQUIRES(mLock); + void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection, + int32_t targetFlags) REQUIRES(mLock); + + void synthesizeCancelationEventsForWindowLocked( + const sp<android::gui::WindowInfoHandle>& windowHandle, + const CancelationOptions& options) REQUIRES(mLock); // Splitting motion events across windows. std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, @@ -685,6 +689,18 @@ private: bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock); sp<InputReporterInterface> mReporter; + + void slipWallpaperTouch(int32_t targetFlags, + const sp<android::gui::WindowInfoHandle>& oldWindowHandle, + const sp<android::gui::WindowInfoHandle>& newWindowHandle, + TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock); + void transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags, + const sp<android::gui::WindowInfoHandle> fromWindowHandle, + const sp<android::gui::WindowInfoHandle> toWindowHandle, + TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock); + + sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( + const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index e860e3c532..b23b88adb4 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -58,6 +58,8 @@ static constexpr int32_t POINTER_1_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_2_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +static constexpr int32_t POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); @@ -73,6 +75,9 @@ static constexpr int32_t MONITOR_PID = 2001; static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms; +static constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + struct PointF { float x; float y; @@ -1670,8 +1675,6 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1714,8 +1717,6 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1745,24 +1746,27 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { foregroundWindow->consumeMotionCancel(); } +class ShouldSplitTouchFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> {}; +INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture, + ::testing::Values(true, false)); /** * 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. */ -TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { +TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); - window->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> foregroundWindow = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + foregroundWindow->setDupTouchToWallpaper(true); + foregroundWindow->setPreventSplitting(GetParam()); sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); // Touch down on top window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1771,7 +1775,7 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Both top window and its wallpaper should receive the touch down - window->consumeMotionDown(); + foregroundWindow->consumeMotionDown(); wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Second finger down on the top window @@ -1790,11 +1794,34 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerDown(1 /* pointerIndex */); + foregroundWindow->consumeMotionPointerDown(1 /* pointerIndex */); wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - window->assertNoEvents(); - wallpaperWindow->assertNoEvents(); + + const MotionEvent secondFingerUpEvent = + MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + foregroundWindow->consumeMotionPointerUp(0); + wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); } /** @@ -1821,8 +1848,6 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setFrame(Rect(0, 0, 400, 200)); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows( {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); @@ -1887,62 +1912,49 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { wallpaperWindow->assertNoEvents(); } -TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) { +/** + * Two windows: a window on the left with dup touch to wallpaper and window on the right without it. + * The touch slips to the right window. so left window and wallpaper should receive ACTION_CANCEL + * The right window should receive ACTION_DOWN. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); - window->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> leftWindow = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 200, 200)); + leftWindow->setDupTouchToWallpaper(true); + leftWindow->setSlippery(true); + + sp<FakeWindowHandle> rightWindow = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + rightWindow->setFrame(Rect(200, 0, 400, 200)); sp<FakeWindowHandle> wallpaperWindow = - sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - wallpaperWindow->setPreventSplitting(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); + // Touch down on left window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {50, 50})) + {100, 100})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); + + // Both foreground window and its wallpaper should receive the touch down + leftWindow->consumeMotionDown(); wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) - .build(); + // Move to right window, the left window should receive cancel. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - - window->consumeMotionPointerDown(1); - wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - - const MotionEvent secondFingerUpEvent = - MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {201, 100})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerUp(1); - wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionUp(ADISPLAY_ID_DEFAULT); - wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + leftWindow->consumeMotionCancel(); + rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); } /** @@ -2696,20 +2708,26 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // Create a couple of windows sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); - + sp<FakeWindowHandle> wallpaper = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaper->setIsWallpaper(true); // Add the windows to the dispatcher - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}}); // Send down to the first window NotifyMotionArgs downMotionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT); mDispatcher->notifyMotion(&downMotionArgs); + // Only the first window should get the down event firstWindow->consumeMotionDown(); secondWindow->assertNoEvents(); + wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Transfer touch to the second window TransferFunction f = GetParam(); @@ -2718,6 +2736,7 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); + wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Send up event to the second window NotifyMotionArgs upMotionArgs = @@ -2727,6 +2746,7 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // The first window gets no events and the second gets up firstWindow->assertNoEvents(); secondWindow->consumeMotionUp(); + wallpaper->assertNoEvents(); } /** @@ -2848,6 +2868,65 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { secondWindow->consumeMotionUp(); } +TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + // Create a couple of windows + sp<FakeWindowHandle> firstWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); + firstWindow->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> secondWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); + secondWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> wallpaper1 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1", ADISPLAY_ID_DEFAULT); + wallpaper1->setIsWallpaper(true); + + sp<FakeWindowHandle> wallpaper2 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT); + wallpaper2->setIsWallpaper(true); + // Add the windows to the dispatcher + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}}); + + // Send down to the first window + NotifyMotionArgs downMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&downMotionArgs); + + // Only the first window should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->assertNoEvents(); + + // Transfer touch focus to the second window + TransferFunction f = GetParam(); + bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken()); + ASSERT_TRUE(success); + + // The first window gets cancel and the second gets down + firstWindow->consumeMotionCancel(); + secondWindow->consumeMotionDown(); + wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Send up event to the second window + NotifyMotionArgs upMotionArgs = + generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT); + mDispatcher->notifyMotion(&upMotionArgs); + // The first window gets no events and the second gets up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionUp(); + wallpaper1->assertNoEvents(); + wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); +} + // For the cases of single pointer touch and two pointers non-split touch, the api's // 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ // for the case where there are multiple pointers split across several windows. diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 86ad4ef169..b49c95d677 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -174,7 +174,7 @@ auto DisplayDevice::getInputInfo() const -> InputInfo { void DisplayDevice::setPowerMode(hal::PowerMode mode) { if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); mBrightness = *mStagedBrightness; } @@ -336,7 +336,7 @@ void DisplayDevice::stageBrightness(float brightness) { } void DisplayDevice::persistBrightness(bool needsComposite) { - if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (mStagedBrightness && mBrightness != mStagedBrightness) { if (needsComposite) { getCompositionDisplay()->setNextBrightness(*mStagedBrightness); } diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index f14bef3d11..b91dece909 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -284,8 +284,8 @@ private: // allow initial power mode as null. std::optional<hardware::graphics::composer::hal::PowerMode> mPowerMode; DisplayModePtr mActiveMode; - std::optional<float> mStagedBrightness = std::nullopt; - float mBrightness = -1.f; + std::optional<float> mStagedBrightness; + std::optional<float> mBrightness; const DisplayModes mSupportedModes; std::atomic<nsecs_t> mLastHwVsync = 0; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 905fe40600..a31cdf0f23 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2398,16 +2398,7 @@ WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { info.inputConfig |= WindowInfo::InputConfig::NOT_TOUCHABLE; } - // For compatibility reasons we let layers which can receive input - // receive input before they have actually submitted a buffer. Because - // of this we use canReceiveInput instead of isVisible to check the - // policy-visibility, ignoring the buffer state. However for layers with - // hasInputInfo()==false we can use the real visibility state. - // We are just using these layers for occlusion detection in - // InputDispatcher, and obviously if they aren't visible they can't occlude - // anything. - const bool visible = hasInputInfo() ? canReceiveInput() : isVisible(); - info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !visible); + info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !isVisibleForInput()); info.alpha = getAlpha(); fillTouchOcclusionMode(info); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index f0c8ad76f4..5ffcabf86b 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -472,6 +472,21 @@ public: virtual bool canReceiveInput() const; /* + * Whether or not the layer should be considered visible for input calculations. + */ + virtual bool isVisibleForInput() const { + // For compatibility reasons we let layers which can receive input + // receive input before they have actually submitted a buffer. Because + // of this we use canReceiveInput instead of isVisible to check the + // policy-visibility, ignoring the buffer state. However for layers with + // hasInputInfo()==false we can use the real visibility state. + // We are just using these layers for occlusion detection in + // InputDispatcher, and obviously if they aren't visible they can't occlude + // anything. + return hasInputInfo() ? canReceiveInput() : isVisible(); + } + + /* * isProtected - true if the layer may contain protected contents in the * GRALLOC_USAGE_PROTECTED sense. */ diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 2487dbd793..e126931e6c 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -203,25 +203,14 @@ float sampleArea(const uint32_t* data, int32_t width, int32_t height, int32_t st return 0.0f; } - // (b/133849373) ROT_90 screencap images produced upside down - auto area = sample_area; - if (orientation & ui::Transform::ROT_90) { - area.top = height - area.top; - area.bottom = height - area.bottom; - std::swap(area.top, area.bottom); - - area.left = width - area.left; - area.right = width - area.right; - std::swap(area.left, area.right); - } - - const uint32_t pixelCount = (area.bottom - area.top) * (area.right - area.left); + const uint32_t pixelCount = + (sample_area.bottom - sample_area.top) * (sample_area.right - sample_area.left); uint32_t accumulatedLuma = 0; // Calculates luma with approximation of Rec. 709 primaries - for (int32_t row = area.top; row < area.bottom; ++row) { + for (int32_t row = sample_area.top; row < sample_area.bottom; ++row) { const uint32_t* rowBase = data + row * stride; - for (int32_t column = area.left; column < area.right; ++column) { + for (int32_t column = sample_area.left; column < sample_area.right; ++column) { uint32_t pixel = rowBase[column]; const uint32_t r = pixel & 0xFF; const uint32_t g = (pixel >> 8) & 0xFF; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 26f80109ef..8c46515bf1 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -3269,16 +3269,34 @@ void SurfaceFlinger::updateInputFlinger() { if (!updateWindowInfo && mInputWindowCommands.empty()) { return; } + + std::unordered_set<Layer*> visibleLayers; + mDrawingState.traverse([&visibleLayers](Layer* layer) { + if (layer->isVisibleForInput()) { + visibleLayers.insert(layer); + } + }); + bool visibleLayersChanged = false; + if (visibleLayers != mVisibleLayers) { + visibleLayersChanged = true; + mVisibleLayers = std::move(visibleLayers); + } + BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo, windowInfos = std::move(windowInfos), displayInfos = std::move(displayInfos), inputWindowCommands = std::move(mInputWindowCommands), - inputFlinger = mInputFlinger, this]() { + inputFlinger = mInputFlinger, this, + visibleLayersChanged]() { ATRACE_NAME("BackgroundExecutor::updateInputFlinger"); if (updateWindowInfo) { - mWindowInfosListenerInvoker->windowInfosChanged(windowInfos, displayInfos, - inputWindowCommands.syncInputWindows); + mWindowInfosListenerInvoker + ->windowInfosChanged(std::move(windowInfos), std::move(displayInfos), + /* shouldSync= */ inputWindowCommands.syncInputWindows, + /* forceImmediateCall= */ + visibleLayersChanged || + !inputWindowCommands.focusRequests.empty()); } else if (inputWindowCommands.syncInputWindows) { // If the caller requested to sync input windows, but there are no // changes to input windows, notify immediately. diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 62ee1b9af8..d9add5c88c 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1454,6 +1454,11 @@ private: nsecs_t mAnimationTransactionTimeout = s2ns(5); friend class SurfaceComposerAIDL; + + // Layers visible during the last commit. This set should only be used for testing set equality + // and membership. The pointers should not be dereferenced as it's possible the set contains + // pointers to freed layers. + std::unordered_set<Layer*> mVisibleLayers; }; class SurfaceComposerAIDL : public gui::BnSurfaceComposer { diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.cpp b/services/surfaceflinger/WindowInfosListenerInvoker.cpp index 30b9d8f1cb..023402f747 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.cpp +++ b/services/surfaceflinger/WindowInfosListenerInvoker.cpp @@ -28,19 +28,26 @@ using gui::WindowInfo; struct WindowInfosListenerInvoker::WindowInfosReportedListener : gui::BnWindowInfosReportedListener { - explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker) : mInvoker(invoker) {} + explicit WindowInfosReportedListener(WindowInfosListenerInvoker& invoker, size_t callbackCount, + bool shouldSync) + : mInvoker(invoker), mCallbacksPending(callbackCount), mShouldSync(shouldSync) {} binder::Status onWindowInfosReported() override { - mInvoker.windowInfosReported(); + mCallbacksPending--; + if (mCallbacksPending == 0) { + mInvoker.windowInfosReported(mShouldSync); + } return binder::Status::ok(); } +private: WindowInfosListenerInvoker& mInvoker; + std::atomic<size_t> mCallbacksPending; + bool mShouldSync; }; WindowInfosListenerInvoker::WindowInfosListenerInvoker(SurfaceFlinger& flinger) - : mFlinger(flinger), - mWindowInfosReportedListener(sp<WindowInfosReportedListener>::make(*this)) {} + : mFlinger(flinger) {} void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) { sp<IBinder> asBinder = IInterface::asBinder(listener); @@ -64,30 +71,76 @@ void WindowInfosListenerInvoker::binderDied(const wp<IBinder>& who) { mWindowInfosListeners.erase(who); } -void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos, - const std::vector<DisplayInfo>& displayInfos, - bool shouldSync) { - ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners; - { - std::scoped_lock lock(mListenersMutex); - for (const auto& [_, listener] : mWindowInfosListeners) { - windowInfosListeners.push_back(listener); +void WindowInfosListenerInvoker::windowInfosChanged(std::vector<WindowInfo> windowInfos, + std::vector<DisplayInfo> displayInfos, + bool shouldSync, bool forceImmediateCall) { + auto callListeners = [this, windowInfos = std::move(windowInfos), + displayInfos = std::move(displayInfos)](bool shouldSync) mutable { + ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners; + { + std::scoped_lock lock(mListenersMutex); + for (const auto& [_, listener] : mWindowInfosListeners) { + windowInfosListeners.push_back(listener); + } } - } - mCallbacksPending = windowInfosListeners.size(); + auto reportedListener = + sp<WindowInfosReportedListener>::make(*this, windowInfosListeners.size(), + shouldSync); + + for (const auto& listener : windowInfosListeners) { + auto status = + listener->onWindowInfosChanged(windowInfos, displayInfos, reportedListener); + if (!status.isOk()) { + reportedListener->onWindowInfosReported(); + } + } + }; + + { + std::scoped_lock lock(mMessagesMutex); + // If there are unacked messages and this isn't a forced call, then return immediately. + // If a forced window infos change doesn't happen first, the update will be sent after + // the WindowInfosReportedListeners are called. If a forced window infos change happens or + // if there are subsequent delayed messages before this update is sent, then this message + // will be dropped and the listeners will only be called with the latest info. This is done + // to reduce the amount of binder memory used. + if (mActiveMessageCount > 0 && !forceImmediateCall) { + mWindowInfosChangedDelayed = std::move(callListeners); + mShouldSyncDelayed |= shouldSync; + return; + } - for (const auto& listener : windowInfosListeners) { - listener->onWindowInfosChanged(windowInfos, displayInfos, - shouldSync ? mWindowInfosReportedListener : nullptr); + mWindowInfosChangedDelayed = nullptr; + shouldSync |= mShouldSyncDelayed; + mShouldSyncDelayed = false; + mActiveMessageCount++; } + callListeners(shouldSync); } -void WindowInfosListenerInvoker::windowInfosReported() { - mCallbacksPending--; - if (mCallbacksPending == 0) { +void WindowInfosListenerInvoker::windowInfosReported(bool shouldSync) { + if (shouldSync) { mFlinger.windowInfosReported(); } + + std::function<void(bool)> callListeners; + bool shouldSyncDelayed; + { + std::scoped_lock lock{mMessagesMutex}; + mActiveMessageCount--; + if (!mWindowInfosChangedDelayed || mActiveMessageCount > 0) { + return; + } + + mActiveMessageCount++; + callListeners = std::move(mWindowInfosChangedDelayed); + mWindowInfosChangedDelayed = nullptr; + shouldSyncDelayed = mShouldSyncDelayed; + mShouldSyncDelayed = false; + } + + callListeners(shouldSyncDelayed); } } // namespace android diff --git a/services/surfaceflinger/WindowInfosListenerInvoker.h b/services/surfaceflinger/WindowInfosListenerInvoker.h index d8d8d0f570..701f11efcd 100644 --- a/services/surfaceflinger/WindowInfosListenerInvoker.h +++ b/services/surfaceflinger/WindowInfosListenerInvoker.h @@ -34,15 +34,15 @@ public: void addWindowInfosListener(sp<gui::IWindowInfosListener>); void removeWindowInfosListener(const sp<gui::IWindowInfosListener>& windowInfosListener); - void windowInfosChanged(const std::vector<gui::WindowInfo>&, - const std::vector<gui::DisplayInfo>&, bool shouldSync); + void windowInfosChanged(std::vector<gui::WindowInfo>, std::vector<gui::DisplayInfo>, + bool shouldSync, bool forceImmediateCall); protected: void binderDied(const wp<IBinder>& who) override; private: struct WindowInfosReportedListener; - void windowInfosReported(); + void windowInfosReported(bool shouldSync); SurfaceFlinger& mFlinger; std::mutex mListenersMutex; @@ -51,8 +51,10 @@ private: ftl::SmallMap<wp<IBinder>, const sp<gui::IWindowInfosListener>, kStaticCapacity> mWindowInfosListeners GUARDED_BY(mListenersMutex); - sp<gui::IWindowInfosReportedListener> mWindowInfosReportedListener; - std::atomic<size_t> mCallbacksPending{0}; + std::mutex mMessagesMutex; + uint32_t mActiveMessageCount GUARDED_BY(mMessagesMutex) = 0; + std::function<void(bool)> mWindowInfosChangedDelayed GUARDED_BY(mMessagesMutex); + bool mShouldSyncDelayed; }; } // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp index 225ad163d9..ac5e9274bb 100644 --- a/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayDevice_SetDisplayBrightnessTest.cpp @@ -96,5 +96,23 @@ TEST_F(SetDisplayBrightnessTest, persistDisplayBrightnessWithCompositeShortCircu EXPECT_EQ(std::nullopt, displayDevice->getCompositionDisplay()->getState().displayBrightness); } +TEST_F(SetDisplayBrightnessTest, firstDisplayBrightnessWithComposite) { + ftl::FakeGuard guard(kMainThreadContext); + sp<DisplayDevice> displayDevice = getDisplayDevice(); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + + constexpr float kDisplayBrightness = -1.0f; + displayDevice->stageBrightness(kDisplayBrightness); + + EXPECT_EQ(-1.0f, displayDevice->getStagedBrightness()); + + displayDevice->persistBrightness(true); + + EXPECT_EQ(std::nullopt, displayDevice->getStagedBrightness()); + EXPECT_EQ(kDisplayBrightness, + displayDevice->getCompositionDisplay()->getState().displayBrightness); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp index f19e55409c..409e1ef5d7 100644 --- a/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp +++ b/services/surfaceflinger/tests/unittests/RegionSamplingTest.cpp @@ -106,40 +106,6 @@ TEST_F(RegionSamplingTest, bounds_checking) { testing::Eq(0.0)); } -// workaround for b/133849373 -TEST_F(RegionSamplingTest, orientation_90) { - std::generate(buffer.begin(), buffer.end(), - [n = 0]() mutable { return (n++ > (kStride * kHeight >> 1)) ? kBlack : kWhite; }); - - Rect tl_region{0, 0, 4, 4}; - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, - tl_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, - tl_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, - tl_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, - tl_region), - testing::Eq(0.0)); - - Rect br_region{kWidth - 4, kHeight - 4, kWidth, kHeight}; - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_0, - br_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_180, - br_region), - testing::Eq(0.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_90, - br_region), - testing::Eq(1.0)); - EXPECT_THAT(sampleArea(buffer.data(), kWidth, kHeight, kStride, ui::Transform::ROT_270, - br_region), - testing::Eq(1.0)); -} - } // namespace android // TODO(b/129481165): remove the #pragma below and fix conversion issues |