diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-11-01 21:37:22 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-11-01 21:37:22 +0000 |
commit | 2a29a5603f6ce1881a72e80ffebff3a92aa2ea48 (patch) | |
tree | ffebaaf45ab249d55507d07426c68fe221f76794 | |
parent | 2fe7eddf185ecb63223eeadc58804f9bf6cbd426 (diff) | |
parent | e3ad741d3997a408c91382620a831de088c5cd0c (diff) | |
download | native-android-platform-13.0.0_r4.tar.gz |
Snap for 9239618 from e3ad741d3997a408c91382620a831de088c5cd0c to tm-platform-releaseandroid-platform-13.0.0_r4android-platform-13.0.0_r3
Change-Id: I55bbf43d148bbc9cdafb1f0bfd54b96adbde9389
104 files changed, 3108 insertions, 992 deletions
diff --git a/cmds/dumpstate/DumpstateInternal.cpp b/cmds/dumpstate/DumpstateInternal.cpp index 3091f6b5d0..6f7fea3432 100644 --- a/cmds/dumpstate/DumpstateInternal.cpp +++ b/cmds/dumpstate/DumpstateInternal.cpp @@ -162,17 +162,16 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, return 0; } bool newline = false; + int poll_timeout_ms = 30 * 1000; while (true) { - uint64_t start_time = Nanotime(); pollfd fds[] = { { .fd = fd, .events = POLLIN } }; - int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), 30 * 1000)); + int ret = TEMP_FAILURE_RETRY(poll(fds, arraysize(fds), poll_timeout_ms)); if (ret == -1) { dprintf(out_fd, "*** %s: poll failed: %s\n", path, strerror(errno)); newline = true; break; - } else if (ret == 0) { - uint64_t elapsed = Nanotime() - start_time; - dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC); + } else if (ret == 0 && poll_timeout_ms != 0) { + dprintf(out_fd, "*** %s: Timed out after %ds\n", path, poll_timeout_ms / 1000 ); newline = true; break; } else { @@ -189,6 +188,7 @@ int DumpFileFromFdToFd(const std::string& title, const std::string& path_string, break; } } + poll_timeout_ms = 0; } if (!newline) dprintf(out_fd, "\n"); diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 45aeab6fa8..4d9b71016e 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -1184,8 +1184,8 @@ static int wait_child(pid_t pid) { int wait_child_with_timeout(pid_t pid, int timeout_ms) { int pidfd = pidfd_open(pid, /*flags=*/0); if (pidfd < 0) { - PLOG(ERROR) << "pidfd_open failed for pid " << pid; - kill(pid, SIGKILL); + PLOG(ERROR) << "pidfd_open failed for pid " << pid + << ", waiting for child process without timeout"; return wait_child(pid); } diff --git a/include/android/choreographer.h b/include/android/choreographer.h index 63aa7ff6c0..cd8e63dec2 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -16,6 +16,28 @@ /** * @addtogroup Choreographer + * + * Choreographer coordinates the timing of frame rendering. This is the C version of the + * android.view.Choreographer object in Java. + * + * As of API level 33, apps can follow proper frame pacing and even choose a future frame to render. + * The API is used as follows: + * 1. The app posts an {@link AChoreographer_vsyncCallback} to Choreographer to run on the next + * frame. + * 2. The callback is called when it is the time to start the frame with an {@link + * AChoreographerFrameCallbackData} payload: information about multiple possible frame + * timelines. + * 3. Apps can choose a frame timeline from the {@link + * AChoreographerFrameCallbackData} payload, depending on the frame deadline they can meet when + * rendering the frame and their desired presentation time, and subsequently + * {@link ASurfaceTransaction_setFrameTimeline notify SurfaceFlinger} + * of the choice. Alternatively, for apps that do not choose a frame timeline, their frame would be + * presented at the earliest possible timeline. + * - The preferred frame timeline is the default frame + * timeline that the platform scheduled for the app, based on device configuration. + * 4. SurfaceFlinger attempts to follow the chosen frame timeline, by not applying transactions or + * latching buffers before the desired presentation time. + * * @{ */ @@ -47,7 +69,8 @@ typedef int64_t AVsyncId; struct AChoreographerFrameCallbackData; /** - * Opaque type that provides access to an AChoreographerFrameCallbackData object. + * Opaque type that provides access to an AChoreographerFrameCallbackData object, which contains + * various methods to extract frame information. */ typedef struct AChoreographerFrameCallbackData AChoreographerFrameCallbackData; @@ -73,8 +96,9 @@ typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* dat /** * Prototype of the function that is called when a new frame is being rendered. - * It's passed the frame data that should not outlive the callback, as well as the data pointer - * provided by the application that registered a callback. + * It is called with \c callbackData describing multiple frame timelines, as well as the \c data + * pointer provided by the application that registered a callback. The \c callbackData does not + * outlive the callback. */ typedef void (*AChoreographer_vsyncCallback)( const AChoreographerFrameCallbackData* callbackData, void* data); @@ -110,7 +134,7 @@ void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer, __DEPRECATED_IN(29); /** - * Power a callback to be run on the next frame. The data pointer provided will + * Post a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. * * Available since API level 29. @@ -131,8 +155,10 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, uint32_t delayMillis) __INTRODUCED_IN(29); /** - * Posts a callback to run on the next frame. The data pointer provided will + * Posts a callback to be run on the next frame. The data pointer provided will * be passed to the callback function when it's called. + * + * Available since API level 33. */ void AChoreographer_postVsyncCallback(AChoreographer* choreographer, AChoreographer_vsyncCallback callback, void* data) @@ -189,7 +215,10 @@ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, __INTRODUCED_IN(30); /** - * The time in nanoseconds when the frame started being rendered. + * The time in nanoseconds at which the frame started being rendered. + * + * Note that this time should \b not be used to advance animation clocks. + * Instead, see AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(). */ int64_t AChoreographerFrameCallbackData_getFrameTimeNanos( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); @@ -201,25 +230,38 @@ size_t AChoreographerFrameCallbackData_getFrameTimelinesLength( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); /** - * Get index of the platform-preferred FrameTimeline. + * Gets the index of the platform-preferred frame timeline. + * The preferred frame timeline is the default + * by which the platform scheduled the app, based on the device configuration. */ size_t AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex( const AChoreographerFrameCallbackData* data) __INTRODUCED_IN(33); /** - * The vsync ID token used to map Choreographer data. + * Gets the token used by the platform to identify the frame timeline at the given \c index. + * + * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See + * AChoreographerFrameCallbackData_getFrameTimelinesLength() */ AVsyncId AChoreographerFrameCallbackData_getFrameTimelineVsyncId( const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); /** - * The time in nanoseconds which the frame at given index is expected to be presented. + * Gets the time in nanoseconds at which the frame described at the given \c index is expected to + * be presented. This time should be used to advance any animation clocks. + * + * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See + * AChoreographerFrameCallbackData_getFrameTimelinesLength() */ int64_t AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos( const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); /** - * The time in nanoseconds which the frame at given index needs to be ready by. + * Gets the time in nanoseconds at which the frame described at the given \c index needs to be + * ready by in order to be presented on time. + * + * \param index index of a frame timeline, in \f( [0, FrameTimelinesLength) \f). See + * AChoreographerFrameCallbackData_getFrameTimelinesLength() */ int64_t AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos( const AChoreographerFrameCallbackData* data, size_t index) __INTRODUCED_IN(33); diff --git a/include/android/surface_control.h b/include/android/surface_control.h index 9a36ecb537..6223ef7f82 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -597,20 +597,20 @@ void ASurfaceTransaction_setEnableBackPressure(ASurfaceTransaction* transaction, __INTRODUCED_IN(31); /** - * Sets the frame timeline to use in Surface Flinger. + * Sets the frame timeline to use in SurfaceFlinger. * - * A frame timeline should be chosen based on what frame deadline the application - * can meet when rendering the frame and the application's desired present time. - * By setting a frame timeline, Surface Flinger tries to present the frame at the corresponding - * expected present time. + * A frame timeline should be chosen based on the frame deadline the application + * can meet when rendering the frame and the application's desired presentation time. + * By setting a frame timeline, SurfaceFlinger tries to present the frame at the corresponding + * expected presentation time. * * To receive frame timelines, a callback must be posted to Choreographer using - * AChoreographer_postExtendedFrameCallback(). The \a vsnycId can then be extracted from the + * AChoreographer_postVsyncCallback(). The \c vsyncId can then be extracted from the * callback payload using AChoreographerFrameCallbackData_getFrameTimelineVsyncId(). * - * \param vsyncId The vsync ID received from AChoreographer, setting the frame's present target to - * the corresponding expected present time and deadline from the frame to be rendered. A stale or - * invalid value will be ignored. + * \param vsyncId The vsync ID received from AChoreographer, setting the frame's presentation target + * to the corresponding expected presentation time and deadline from the frame to be rendered. A + * stale or invalid value will be ignored. */ void ASurfaceTransaction_setFrameTimeline(ASurfaceTransaction* transaction, AVsyncId vsyncId) __INTRODUCED_IN(33); diff --git a/include/input/Input.h b/include/input/Input.h index e7d68fc349..7ea297049b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -297,6 +297,8 @@ enum class MotionClassification : uint8_t { */ const char* motionClassificationToString(MotionClassification classification); +const char* motionToolTypeToString(int32_t toolType); + /** * Portion of FrameMetrics timeline of interest to input code. */ diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index c4f03c9119..3585392c2b 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -300,6 +300,8 @@ enum class InputDeviceConfigurationFileType : int32_t { /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. + * The optional suffix is appended to the end of the file name (before the + * extension). * * The device identifier is used to construct several default configuration file * names to try based on the device name, vendor, product, and version. @@ -307,8 +309,8 @@ enum class InputDeviceConfigurationFileType : int32_t { * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type); + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix = ""); /* * Gets the path of an input device configuration file, if one is available. diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index b2bd535cbf..50849506a4 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -21,8 +21,8 @@ #include <stdint.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> -#include <utils/RefBase.h> #include <utils/Tokenizer.h> +#include <set> #include <input/InputDevice.h> @@ -65,8 +65,8 @@ struct AxisInfo { */ class KeyLayoutMap { public: - static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename); - static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + static base::Result<std::shared_ptr<KeyLayoutMap>> load(const std::string& filename, + const char* contents = nullptr); static base::Result<std::shared_ptr<KeyLayoutMap>> loadContents(const std::string& filename, const char* contents); @@ -84,6 +84,8 @@ public: virtual ~KeyLayoutMap(); private: + static base::Result<std::shared_ptr<KeyLayoutMap>> load(Tokenizer* tokenizer); + struct Key { int32_t keyCode; uint32_t flags; @@ -104,6 +106,7 @@ private: KeyedVector<int32_t, Led> mLedsByScanCode; KeyedVector<int32_t, Led> mLedsByUsageCode; std::unordered_map<int32_t, Sensor> mSensorsByAbsCode; + std::set<std::string> mRequiredKernelConfigs; std::string mLoadFileName; KeyLayoutMap(); @@ -124,6 +127,7 @@ private: status_t parseAxis(); status_t parseLed(); status_t parseSensor(); + status_t parseRequiredKernelConfig(); }; }; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 08ad8c6e5a..9a3e15f1cd 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -61,9 +61,7 @@ private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name); - std::string getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type); + const std::string& name); }; /** diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 0a75278494..55f730b287 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -17,6 +17,7 @@ #pragma once #include <map> +#include <optional> #include <set> #include <string> @@ -28,6 +29,15 @@ std::string constToString(const T& v) { } /** + * Convert an optional type to string. + */ +template <typename T> +std::string toString(const std::optional<T>& optional, + std::string (*toString)(const T&) = constToString) { + return optional ? toString(*optional) : "<not set>"; +} + +/** * Convert a set of integral types to string. */ template <typename T> diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index 0f1a02a553..3551a8fd0a 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -86,6 +86,7 @@ static const char* hidl_hal_interfaces_to_dump[] { /* list of hal interface to dump containing process during native dumps */ static const std::vector<std::string> aidl_interfaces_to_dump { "android.hardware.camera.provider.ICameraProvider", + "android.hardware.input.processor.IInputProcessor", }; /* list of extra hal interfaces to dump containing process during native dumps */ diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dbccf30fae..a51bbb1553 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -287,18 +287,17 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, // We need to check if we were waiting for a transaction callback in order to // process any pending buffers and unblock. It's possible to get transaction - // callbacks for previous requests so we need to ensure the frame from this - // transaction callback matches the last acquired buffer. Since acquireNextBuffer - // will stop processing buffers when mWaitForTransactionCallback is set, we know - // that mLastAcquiredFrameNumber is the frame we're waiting on. - // We also want to check if mNextTransaction is null because it's possible another + // callbacks for previous requests so we need to ensure that there are no pending + // frame numbers that were in a sync. We remove the frame from mSyncedFrameNumbers + // set and then check if it's empty. If there are no more pending syncs, we can + // proceed with flushing the shadow queue. + // We also want to check if mSyncTransaction is null because it's possible another // sync request came in while waiting, but it hasn't started processing yet. In that // case, we don't actually want to flush the frames in between since they will get // processed and merged with the sync transaction and released earlier than if they // were sent to SF - if (mWaitForTransactionCallback && mSyncTransaction == nullptr && - currFrameNumber >= mLastAcquiredFrameNumber) { - mWaitForTransactionCallback = false; + mSyncedFrameNumbers.erase(currFrameNumber); + if (mSyncedFrameNumbers.empty() && mSyncTransaction == nullptr) { flushShadowQueue(); } } else { @@ -308,7 +307,6 @@ void BLASTBufferQueue::transactionCommittedCallback(nsecs_t /*latchTime*/, BQA_LOGE("No matching SurfaceControls found: mSurfaceControlsWithPendingCallback was " "empty."); } - decStrong((void*)transactionCommittedCallbackThunk); } } @@ -351,6 +349,20 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } + auto currFrameNumber = stat.frameEventStats.frameNumber; + std::vector<ReleaseCallbackId> staleReleases; + for (const auto& [key, value]: mSubmitted) { + if (currFrameNumber > key.framenumber) { + staleReleases.push_back(key); + } + } + for (const auto& staleRelease : staleReleases) { + BQA_LOGE("Faking releaseBufferCallback from transactionCompleteCallback"); + BBQ_TRACE("FakeReleaseCallback"); + releaseBufferCallbackLocked(staleRelease, + stat.previousReleaseFence ? stat.previousReleaseFence : Fence::NO_FENCE, + stat.currentMaxAcquiredBufferCount); + } } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } @@ -391,7 +403,14 @@ void BLASTBufferQueue::releaseBufferCallback( const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) { BBQ_TRACE(); + std::unique_lock _lock{mMutex}; + releaseBufferCallbackLocked(id, releaseFence, currentMaxAcquiredBufferCount); +} + +void BLASTBufferQueue::releaseBufferCallbackLocked(const ReleaseCallbackId& id, + const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount) { + ATRACE_CALL(); BQA_LOGV("releaseBufferCallback %s", id.to_string().c_str()); // Calculate how many buffers we need to hold before we release them back @@ -409,16 +428,22 @@ void BLASTBufferQueue::releaseBufferCallback( const auto numPendingBuffersToHold = isEGL ? std::max(0u, mMaxAcquiredBuffers - mCurrentMaxAcquiredBufferCount) : 0; - mPendingRelease.emplace_back(ReleasedBuffer{id, releaseFence}); + + auto rb = ReleasedBuffer{id, releaseFence}; + if (std::find(mPendingRelease.begin(), mPendingRelease.end(), rb) == mPendingRelease.end()) { + mPendingRelease.emplace_back(rb); + } // Release all buffers that are beyond the ones that we need to hold while (mPendingRelease.size() > numPendingBuffersToHold) { const auto releasedBuffer = mPendingRelease.front(); mPendingRelease.pop_front(); releaseBuffer(releasedBuffer.callbackId, releasedBuffer.releaseFence); - // Don't process the transactions here if mWaitForTransactionCallback is set. Instead, let - // onFrameAvailable handle processing them since it will merge with the syncTransaction. - if (!mWaitForTransactionCallback) { + // Don't process the transactions here if mSyncedFrameNumbers is not empty. That means + // are still transactions that have sync buffers in them that have not been applied or + // dropped. Instead, let onFrameAvailable handle processing them since it will merge with + // the syncTransaction. + if (mSyncedFrameNumbers.empty()) { acquireNextBufferLocked(std::nullopt); } } @@ -442,6 +467,9 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, BQA_LOGV("released %s", callbackId.to_string().c_str()); mBufferItemConsumer->releaseBuffer(it->second, releaseFence); mSubmitted.erase(it); + // Remove the frame number from mSyncedFrameNumbers since we can get a release callback + // without getting a transaction committed if the buffer was dropped. + mSyncedFrameNumbers.erase(callbackId.framenumber); } void BLASTBufferQueue::acquireNextBufferLocked( @@ -608,7 +636,7 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() { } void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) { - if (mWaitForTransactionCallback && mNumFrameAvailable > 0) { + if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) { // We are waiting on a previous sync's transaction callback so allow another sync // transaction to proceed. // @@ -635,6 +663,8 @@ void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& l 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}; @@ -666,7 +696,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { // add to shadow queue mNumFrameAvailable++; - if (mWaitForTransactionCallback && mNumFrameAvailable >= 2) { + if (waitForTransactionCallback && mNumFrameAvailable >= 2) { acquireAndReleaseBuffer(); } ATRACE_INT(mQueuedBufferTrace.c_str(), @@ -683,14 +713,14 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { incStrong((void*)transactionCommittedCallbackThunk); mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk, static_cast<void*>(this)); - mWaitForTransactionCallback = true; + mSyncedFrameNumbers.emplace(item.mFrameNumber); if (mAcquireSingleBuffer) { prevCallback = mTransactionReadyCallback; prevTransaction = mSyncTransaction; mTransactionReadyCallback = nullptr; mSyncTransaction = nullptr; } - } else if (!mWaitForTransactionCallback) { + } else if (!waitForTransactionCallback) { acquireNextBufferLocked(std::nullopt); } } @@ -1097,9 +1127,9 @@ void BLASTBufferQueue::abandon() { } // Clear sync states - if (mWaitForTransactionCallback) { - BQA_LOGD("mWaitForTransactionCallback cleared"); - mWaitForTransactionCallback = false; + if (!mSyncedFrameNumbers.empty()) { + BQA_LOGD("mSyncedFrameNumbers cleared"); + mSyncedFrameNumbers.clear(); } if (mSyncTransaction != nullptr) { diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 47d801a78d..0f5192d41c 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -352,7 +352,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.latchTime, surfaceStats.acquireTimeOrFence, transactionStats.presentFence, surfaceStats.previousReleaseFence, surfaceStats.transformHint, - surfaceStats.eventStats); + surfaceStats.eventStats, + surfaceStats.currentMaxAcquiredBufferCount); } callbackFunction(transactionStats.latchTime, transactionStats.presentFence, @@ -377,7 +378,8 @@ void TransactionCompletedListener::onTransactionCompleted(ListenerStats listener transactionStats.latchTime, surfaceStats.acquireTimeOrFence, transactionStats.presentFence, surfaceStats.previousReleaseFence, surfaceStats.transformHint, - surfaceStats.eventStats); + surfaceStats.eventStats, + surfaceStats.currentMaxAcquiredBufferCount); if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) { callbacksMap[callbackId] .surfaceControls[surfaceStats.surfaceControl] @@ -897,6 +899,10 @@ void SurfaceComposerClient::Transaction::clear() { mApplyToken = nullptr; } +uint64_t SurfaceComposerClient::Transaction::getId() { + return mId; +} + void SurfaceComposerClient::doUncacheBufferTransaction(uint64_t cacheId) { sp<ISurfaceComposer> sf(ComposerService::getComposerService()); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 9328a54184..f5898d20f1 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -95,9 +95,12 @@ public: const std::vector<SurfaceControlStats>& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, std::optional<uint32_t> currentMaxAcquiredBufferCount); + void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp<Fence>& releaseFence, + std::optional<uint32_t> currentMaxAcquiredBufferCount); void syncNextTransaction(std::function<void(SurfaceComposerClient::Transaction*)> callback, bool acquireSingleBuffer = true); void stopContinuousSyncTransaction(); + void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber); @@ -177,6 +180,12 @@ private: struct ReleasedBuffer { ReleaseCallbackId callbackId; sp<Fence> releaseFence; + bool operator==(const ReleasedBuffer& rhs) const { + // Only compare Id so if we somehow got two callbacks + // with different fences we don't decrement mNumAcquired + // too far. + return rhs.callbackId == callbackId; + } }; std::deque<ReleasedBuffer> mPendingRelease GUARDED_BY(mMutex); @@ -251,7 +260,6 @@ private: std::queue<sp<SurfaceControl>> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount; - bool mWaitForTransactionCallback GUARDED_BY(mMutex) = false; // Flag to determine if syncTransaction should only acquire a single buffer and then clear or // continue to acquire buffers until explicitly cleared @@ -279,6 +287,8 @@ private: uint64_t mLastAppliedFrameNumber = 0; std::function<void(bool)> mTransactionHangCallback; + + std::unordered_set<uint64_t> mSyncedFrameNumbers GUARDED_BY(mMutex); }; } // namespace android diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index efbdb36fef..9033e17c53 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -65,14 +65,16 @@ struct SurfaceControlStats { SurfaceControlStats(const sp<SurfaceControl>& sc, nsecs_t latchTime, std::variant<nsecs_t, sp<Fence>> acquireTimeOrFence, const sp<Fence>& presentFence, const sp<Fence>& prevReleaseFence, - uint32_t hint, FrameEventHistoryStats eventStats) + uint32_t hint, FrameEventHistoryStats eventStats, + uint32_t currentMaxAcquiredBufferCount) : surfaceControl(sc), latchTime(latchTime), acquireTimeOrFence(std::move(acquireTimeOrFence)), presentFence(presentFence), previousReleaseFence(prevReleaseFence), transformHint(hint), - frameEventStats(eventStats) {} + frameEventStats(eventStats), + currentMaxAcquiredBufferCount(currentMaxAcquiredBufferCount) {} sp<SurfaceControl> surfaceControl; nsecs_t latchTime = -1; @@ -81,6 +83,7 @@ struct SurfaceControlStats { sp<Fence> previousReleaseFence; uint32_t transformHint = 0; FrameEventHistoryStats frameEventStats; + uint32_t currentMaxAcquiredBufferCount = 0; }; using TransactionCompletedCallbackTakesContext = @@ -461,6 +464,10 @@ public: // Clears the contents of the transaction without applying it. void clear(); + // Returns the current id of the transaction. + // The id is updated every time the transaction is applied. + uint64_t getId(); + status_t apply(bool synchronous = false, bool oneWay = false); // Merge another transaction in to this one, clearing other // as if it had been applied. diff --git a/libs/gui/include/gui/test/CallbackUtils.h b/libs/gui/include/gui/test/CallbackUtils.h index 62d1496ccd..08785b49c1 100644 --- a/libs/gui/include/gui/test/CallbackUtils.h +++ b/libs/gui/include/gui/test/CallbackUtils.h @@ -135,7 +135,8 @@ private: void verifySurfaceControlStats(const SurfaceControlStats& surfaceControlStats, nsecs_t latchTime) const { const auto& [surfaceControl, latch, acquireTimeOrFence, presentFence, - previousReleaseFence, transformHint, frameEvents] = surfaceControlStats; + previousReleaseFence, transformHint, frameEvents, ignore] = + surfaceControlStats; ASSERT_TRUE(std::holds_alternative<nsecs_t>(acquireTimeOrFence)); ASSERT_EQ(std::get<nsecs_t>(acquireTimeOrFence) > 0, diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index cb7e94c932..b993289e6a 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -161,6 +161,10 @@ public: ASSERT_EQ(numFramesSubmitted, mBlastBufferQueueAdapter->mSubmitted.size()); } + void mergeWithNextTransaction(Transaction* merge, uint64_t frameNumber) { + mBlastBufferQueueAdapter->mergeWithNextTransaction(merge, frameNumber); + } + private: sp<TestBLASTBufferQueue> mBlastBufferQueueAdapter; }; @@ -1111,6 +1115,39 @@ TEST_F(BLASTBufferQueueTest, SyncNextTransactionOverwrite) { ASSERT_TRUE(receivedCallback); } +TEST_F(BLASTBufferQueueTest, SyncNextTransactionDropBuffer) { + uint8_t r = 255; + uint8_t g = 0; + uint8_t b = 0; + + BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); + + sp<IGraphicBufferProducer> igbProducer; + setUpProducer(adapter, igbProducer); + + Transaction sync; + adapter.setSyncTransaction(sync); + queueBuffer(igbProducer, 0, 255, 0, 0); + + // Merge a transaction that has a complete callback into the next frame so we can get notified + // when to take a screenshot + CallbackHelper transactionCallback; + Transaction t; + t.addTransactionCompletedCallback(transactionCallback.function, + transactionCallback.getContext()); + adapter.mergeWithNextTransaction(&t, 2); + queueBuffer(igbProducer, r, g, b, 0); + + // Drop the buffer, but ensure the next one continues to get processed. + sync.setBuffer(mSurfaceControl, nullptr); + + CallbackData callbackData; + transactionCallback.getCallbackData(&callbackData); + ASSERT_EQ(NO_ERROR, captureDisplay(mCaptureArgs, mCaptureResults)); + ASSERT_NO_FATAL_FAILURE( + checkScreenCapture(r, g, b, {0, 0, (int32_t)mDisplayWidth, (int32_t)mDisplayHeight})); +} + // This test will currently fail because the old surfacecontrol will steal the last presented buffer // until the old surface control is destroyed. This is not necessarily a bug but to document a // limitation with the update API and to test any changes to make the api more robust. The current diff --git a/libs/gui/tests/EndToEndNativeInputTest.cpp b/libs/gui/tests/EndToEndNativeInputTest.cpp index c6cdeb7706..2637f59b5e 100644 --- a/libs/gui/tests/EndToEndNativeInputTest.cpp +++ b/libs/gui/tests/EndToEndNativeInputTest.cpp @@ -1184,18 +1184,23 @@ public: std::vector<sp<IGraphicBufferProducer>> mProducers; }; -TEST_F(MultiDisplayTests, drop_input_if_layer_on_invalid_display) { +TEST_F(MultiDisplayTests, drop_touch_if_layer_on_invalid_display) { ui::LayerStack layerStack = ui::LayerStack::fromValue(42); // Do not create a display associated with the LayerStack. std::unique_ptr<InputSurface> surface = makeSurface(100, 100); surface->doTransaction([&](auto &t, auto &sc) { t.setLayerStack(sc, layerStack); }); surface->showAt(100, 100); + // Touches should be dropped if the layer is on an invalid display. injectTapOnDisplay(101, 101, layerStack.id); + EXPECT_EQ(surface->consumeEvent(100), nullptr); + + // However, we still let the window be focused and receive keys. surface->requestFocus(layerStack.id); - injectKeyOnDisplay(AKEYCODE_V, layerStack.id); + surface->assertFocusChange(true); - EXPECT_EQ(surface->consumeEvent(100), nullptr); + injectKeyOnDisplay(AKEYCODE_V, layerStack.id); + surface->expectKey(AKEYCODE_V); } TEST_F(MultiDisplayTests, virtual_display_receives_input) { diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 5d7874af77..1335e4dfd0 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -67,6 +67,7 @@ cc_library { "libbase", "liblog", "libcutils", + "libvintf", ], static_libs: [ diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 13ca9ecd35..155cb040fb 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -64,9 +64,10 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { } bool shouldDisregardTransformation(uint32_t source) { - // Do not apply any transformations to axes from joysticks or touchpads. + // Do not apply any transformations to axes from joysticks, touchpads, or relative mice. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || - isFromSource(source, AINPUT_SOURCE_CLASS_POSITION); + isFromSource(source, AINPUT_SOURCE_CLASS_POSITION) || + isFromSource(source, AINPUT_SOURCE_MOUSE_RELATIVE); } bool shouldDisregardOffset(uint32_t source) { @@ -89,6 +90,25 @@ const char* motionClassificationToString(MotionClassification classification) { } } +const char* motionToolTypeToString(int32_t toolType) { + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_UNKNOWN: + return "UNKNOWN"; + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return "FINGER"; + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return "STYLUS"; + case AMOTION_EVENT_TOOL_TYPE_MOUSE: + return "MOUSE"; + case AMOTION_EVENT_TOOL_TYPE_ERASER: + return "ERASER"; + case AMOTION_EVENT_TOOL_TYPE_PALM: + return "PALM"; + default: + return "INVALID"; + } +} + // --- IdGenerator --- IdGenerator::IdGenerator(Source source) : mSource(source) {} diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 0bee1b6f2a..a9089690b0 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -53,33 +53,39 @@ static void appendInputDeviceConfigurationFileRelativePath(std::string& path, } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - std::string versionPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type); + std::string versionPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%" + "04x_Version_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + deviceIdentifier.version, + suffix), + type); if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - std::string productPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type); + std::string productPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + suffix), + type); if (!productPath.empty()) { return productPath; } } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix, + type); } std::string getInputDeviceConfigurationFilePathByName( diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 7c25cda9ac..170e748ca6 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -21,24 +21,33 @@ #include <input/InputEventLabels.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> #include <utils/Timers.h> #include <utils/Tokenizer.h> +#include <vintf/RuntimeInfo.h> +#include <vintf/VintfObject.h> #include <cstdlib> #include <string_view> #include <unordered_map> -// Enables debug output for the parser. -#define DEBUG_PARSER 0 +/** + * Log debug output for the parser. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart) + */ +const bool DEBUG_PARSER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO); // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - +/** + * Log debug output for mapping. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart) + */ +const bool DEBUG_MAPPING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO); namespace android { namespace { @@ -69,6 +78,29 @@ static const std::unordered_map<std::string_view, InputDeviceSensorType> SENSOR_ sensorPair<InputDeviceSensorType::GYROSCOPE_UNCALIBRATED>(), sensorPair<InputDeviceSensorType::SIGNIFICANT_MOTION>()}; +bool kernelConfigsArePresent(const std::set<std::string>& configs) { + std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo = + android::vintf::VintfObject::GetInstance()->getRuntimeInfo( + vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); + LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched"); + + const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs(); + for (const std::string& requiredConfig : configs) { + const auto configIt = kernelConfigs.find(requiredConfig); + if (configIt == kernelConfigs.end()) { + ALOGI("Required kernel config %s is not found", requiredConfig.c_str()); + return false; + } + const std::string& option = configIt->second; + if (option != "y" && option != "m") { + ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(), + option.c_str()); + return false; + } + } + return true; +} + } // namespace KeyLayoutMap::KeyLayoutMap() = default; @@ -76,32 +108,34 @@ KeyLayoutMap::~KeyLayoutMap() = default; base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key layout map.", status); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr<Tokenizer> t(tokenizer); - auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; - } - return ret; + return load(filename, contents); } -base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename) { +base::Result<std::shared_ptr<KeyLayoutMap>> KeyLayoutMap::load(const std::string& filename, + const char* contents) { Tokenizer* tokenizer; - status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + status_t status; + if (contents == nullptr) { + status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + } else { + status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + } if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); } std::unique_ptr<Tokenizer> t(tokenizer); auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + if (!ret.ok()) { + return ret; + } + const std::shared_ptr<KeyLayoutMap>& map = *ret; + LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); + if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) { + ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str()); + return Errorf("Missing kernel config"); } + map->mLoadFileName = filename; return ret; } @@ -134,9 +168,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const { const Key* key = getKey(scanCode, usageCode); if (!key) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, + usageCode); *outKeyCode = AKEYCODE_UNKNOWN; *outFlags = 0; return NAME_NOT_FOUND; @@ -145,10 +178,9 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, *outKeyCode = key->keyCode; *outFlags = key->flags; -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); return NO_ERROR; } @@ -156,17 +188,12 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, base::Result<std::pair<InputDeviceSensorType, int32_t>> KeyLayoutMap::mapSensor(int32_t absCode) { auto it = mSensorsByAbsCode.find(absCode); if (it == mSensorsByAbsCode.end()) { -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode); return Errorf("Can't find abs code {}.", absCode); } const Sensor& sensor = it->second; - -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, - ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } @@ -200,21 +227,18 @@ status_t KeyLayoutMap::findScanCodesForKey( status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { ssize_t index = mAxes.indexOfKey(scanCode); if (index < 0) { -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); return NAME_NOT_FOUND; } *outAxisInfo = mAxes.valueAt(index); -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, + outAxisInfo->splitValue, outAxisInfo->flatOverride); + return NO_ERROR; } @@ -223,15 +247,12 @@ status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) for (size_t i = 0; i < N; i++) { if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { *outScanCode = mLedsByScanCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, + *outScanCode); return NO_ERROR; } } -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); return NAME_NOT_FOUND; } @@ -240,15 +261,12 @@ status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCod for (size_t i = 0; i < N; i++) { if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { *outUsageCode = mLedsByUsageCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode); return NO_ERROR; } } -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return NAME_NOT_FOUND; } @@ -264,10 +282,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); mTokenizer->skipDelimiters(WHITESPACE); @@ -289,6 +305,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseSensor(); if (status) return status; + } else if (keywordToken == "requires_kernel_config") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseRequiredKernelConfig(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -361,10 +381,9 @@ status_t KeyLayoutMap::Parser::parseKey() { flags |= flag; } -#if DEBUG_PARSER - ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", code, keyCode, flags); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); + Key key; key.keyCode = keyCode; key.flags = flags; @@ -462,13 +481,12 @@ status_t KeyLayoutMap::Parser::parseAxis() { } } -#if DEBUG_PARSER - ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - axisInfo.mode, axisInfo.axis, axisInfo.highAxis, - axisInfo.splitValue, axisInfo.flatOverride); -#endif + ALOGD_IF(DEBUG_PARSER, + "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + mMap->mAxes.add(scanCode, axisInfo); return NO_ERROR; } @@ -505,10 +523,8 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } -#if DEBUG_PARSER - ALOGD("Parsed led %s: code=%d, ledCode=%d.", - mapUsage ? "usage" : "scan code", code, ledCode); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code", + code, ledCode); Led led; led.ledCode = ledCode; @@ -584,10 +600,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { } int32_t sensorDataIndex = indexOpt.value(); -#if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, - ftl::enum_string(sensorType).c_str(), sensorDataIndex); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + ftl::enum_string(sensorType).c_str(), sensorDataIndex); Sensor sensor; sensor.sensorType = sensorType; @@ -596,4 +610,23 @@ status_t KeyLayoutMap::Parser::parseSensor() { return NO_ERROR; } +// Parse the name of a required kernel config. +// The layout won't be used if the specified kernel config is not present +// Examples: +// requires_kernel_config CONFIG_HID_PLAYSTATION +status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + std::string configName = codeToken.string(); + + const auto result = mMap->mRequiredKernelConfigs.emplace(configName); + if (!result.second) { + ALOGE("%s: Duplicate entry for required kernel config %s.", + mTokenizer->getLocation().string(), configName.c_str()); + return BAD_VALUE; + } + + ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); + return NO_ERROR; +} + } // namespace android diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index f0895b32ef..c3f5151fd1 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -20,16 +20,23 @@ #include <unistd.h> #include <limits.h> -#include <input/Keyboard.h> +#include <input/InputDevice.h> #include <input/InputEventLabels.h> -#include <input/KeyLayoutMap.h> #include <input/KeyCharacterMap.h> -#include <input/InputDevice.h> +#include <input/KeyLayoutMap.h> +#include <input/Keyboard.h> +#include <log/log.h> #include <utils/Errors.h> -#include <utils/Log.h> namespace android { +static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, + InputDeviceConfigurationFileType type) { + return name.empty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + // --- KeyMap --- KeyMap::KeyMap() { @@ -111,11 +118,25 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, } base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(path); + if (ret.ok()) { + keyLayoutMap = *ret; + keyLayoutFile = path; + return OK; + } + + // Try to load fallback layout if the regular layout could not be loaded due to missing + // kernel modules + std::string fallbackPath( + getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, + InputDeviceConfigurationFileType:: + KEY_LAYOUT, + "_fallback")); + ret = KeyLayoutMap::load(fallbackPath); if (!ret.ok()) { return ret.error().code(); } keyLayoutMap = *ret; - keyLayoutFile = path; + keyLayoutFile = fallbackPath; return OK; } @@ -137,14 +158,6 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return OK; } -std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type) { - return name.empty() - ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) - : getInputDeviceConfigurationFilePathByName(name, type); -} - - // --- Global functions --- bool isKeyboardSpecialFunction(const PropertyMap* config) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 6ffe8518b6..d947cd99e8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -36,8 +36,9 @@ cc_test { "liblog", "libui", "libutils", + "libvintf", ], - data: ["data/*.kcm"], + data: ["data/*"], test_suites: ["device-tests"], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index 61e88df11d..e872fa442b 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -64,13 +64,11 @@ protected: mKeyMap.keyCharacterMapFile = path; } - virtual void SetUp() override { + void SetUp() override { loadKeyLayout("Generic"); loadKeyCharacterMap("Generic"); } - virtual void TearDown() override {} - KeyMap mKeyMap; }; @@ -132,4 +130,20 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); } +TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; + // We assert error message here because it's used by 'validatekeymaps' tool + ASSERT_EQ("Missing kernel config", ret.error().message()); +} + +TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl"; + base::Result<std::shared_ptr<KeyLayoutMap>> ret = KeyLayoutMap::load(klPath); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath; + const std::shared_ptr<KeyLayoutMap>& map = *ret; + ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present"; +} + } // namespace android diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index a92016ba3b..4b3124636b 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -715,10 +715,10 @@ TEST_F(MotionEventTest, ApplyTransform) { } TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { - constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, - AMOTION_EVENT_ACTION_DOWN), - std::pair(AINPUT_SOURCE_JOYSTICK, - AMOTION_EVENT_ACTION_MOVE)}; + constexpr static std::array kNonTransformedSources = + {std::pair(AINPUT_SOURCE_TOUCHPAD, AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_JOYSTICK, AMOTION_EVENT_ACTION_MOVE), + std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, AMOTION_EVENT_ACTION_MOVE)}; // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform transform(ui::Transform::ROT_90, 800, 400); transform.set(transform.tx() + 20, transform.ty() + 40); @@ -738,7 +738,7 @@ TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL, AMOTION_EVENT_ACTION_DOWN), - std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, + std::pair(AINPUT_SOURCE_TOUCH_NAVIGATION, AMOTION_EVENT_ACTION_MOVE)}; // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform transform(ui::Transform::ROT_90, 800, 400); diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl new file mode 100644 index 0000000000..2d0a507fbd --- /dev/null +++ b/libs/input/tests/data/kl_with_required_fake_config.kl @@ -0,0 +1,20 @@ +# Copyright (C) 2022 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. + +# This KL should not be loaded unless the below config is present in the kernel +# This config will never exist, and therefore this KL should never be loaded +requires_kernel_config CONFIG_HID_FAKEMODULE + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl new file mode 100644 index 0000000000..303b23e48a --- /dev/null +++ b/libs/input/tests/data/kl_with_required_real_config.kl @@ -0,0 +1,21 @@ +# Copyright (C) 2022 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. + +# This KL should not be loaded unless the below config is present in the kernel +# The CONFIG_UHID option has been required for a while, and therefore it's safe +# to assume that this will always be loaded +requires_kernel_config CONFIG_UHID + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X
\ No newline at end of file diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 2a3924b5f2..dce327ed18 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -196,9 +196,20 @@ std::string NotifyMotionArgs::dump() const { } coords += StringPrintf("{%" PRIu32 ": ", i); coords += - StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id, + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f pressure=%.1f", pointerProperties[i].id, pointerCoords[i].getX(), pointerCoords[i].getY(), pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + const int32_t toolType = pointerProperties[i].toolType; + if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) { + coords += StringPrintf(" toolType=%s", motionToolTypeToString(toolType)); + } + const float major = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + const float minor = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + const float orientation = pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + if (major != 0 || minor != 0) { + coords += StringPrintf(" major=%.1f minor=%.1f orientation=%.1f", major, minor, + orientation); + } coords += "}"; } return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index f57ff33d50..ec41025e9d 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -29,8 +29,40 @@ using android::base::StringPrintf; +/** + * This type is declared here to ensure consistency between the instantiated type (used in the + * constructor via std::make_unique) and the cast-to type (used in PalmRejector::dump() with + * static_cast). Due to the lack of rtti support, dynamic_cast is not available, so this can't be + * checked at runtime to avoid undefined behaviour. + */ +using PalmFilterImplementation = ::ui::NeuralStylusPalmDetectionFilter; + namespace android { +/** + * Log detailed debug messages about each inbound motion event notification to the blocker. + * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerInboundMotion DEBUG" + * (requires restart) + */ +const bool DEBUG_INBOUND_MOTION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "InboundMotion", ANDROID_LOG_INFO); + +/** + * Log detailed debug messages about each outbound motion event processed by the blocker. + * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerOutboundMotion DEBUG" + * (requires restart) + */ +const bool DEBUG_OUTBOUND_MOTION = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "OutboundMotion", ANDROID_LOG_INFO); + +/** + * Log the data sent to the model and received back from the model. + * Enable this via "adb shell setprop log.tag.UnwantedInteractionBlockerModel DEBUG" + * (requires restart) + */ +const bool DEBUG_MODEL = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Model", ANDROID_LOG_INFO); + // Category (=namespace) name for the input settings that are applied at boot time static const char* INPUT_NATIVE_BOOT = "input_native_boot"; /** @@ -45,13 +77,11 @@ static std::string toLower(std::string s) { } static bool isFromTouchscreen(int32_t source) { - return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN) && - !isFromSource(source, AINPUT_SOURCE_STYLUS); + return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN); } static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { - return ::base::TimeTicks::UnixEpoch() + - ::base::Milliseconds(static_cast<float>(ns2ms(eventTime))); + return ::base::TimeTicks::UnixEpoch() + ::base::TimeDelta::FromNanosecondsD(eventTime); } /** @@ -61,39 +91,22 @@ static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { static bool isPalmRejectionEnabled() { std::string value = toLower( server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, - PALM_REJECTION_ENABLED, "false")); - if (value == "true" || value == "1") { + PALM_REJECTION_ENABLED, "0")); + if (value == "1") { return true; } return false; } -static int getLinuxToolType(int32_t toolType) { - switch (toolType) { - case AMOTION_EVENT_TOOL_TYPE_FINGER: - return MT_TOOL_FINGER; - case AMOTION_EVENT_TOOL_TYPE_STYLUS: - return MT_TOOL_PEN; - case AMOTION_EVENT_TOOL_TYPE_PALM: - return MT_TOOL_PALM; +static int getLinuxToolCode(int toolType) { + if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { + return BTN_TOOL_PEN; } - ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType); - return MT_TOOL_FINGER; -} - -static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { - std::string out; - out += StringPrintf("max_x = %.2f\n", info.max_x); - out += StringPrintf("max_y = %.2f\n", info.max_y); - out += StringPrintf("x_res = %.2f\n", info.x_res); - out += StringPrintf("y_res = %.2f\n", info.y_res); - out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res); - out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res); - out += StringPrintf("minor_radius_supported = %s\n", - info.minor_radius_supported ? "true" : "false"); - out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res); - out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res); - return out; + if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { + return BTN_TOOL_FINGER; + } + ALOGW("Got tool type %" PRId32 ", converting to BTN_TOOL_FINGER", toolType); + return BTN_TOOL_FINGER; } static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { @@ -128,32 +141,6 @@ static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { return AMOTION_EVENT_ACTION_MOVE; } -std::string toString(const ::ui::InProgressTouchEvdev& touch) { - return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," - " pressure=%.1f, major=%i, minor=%i, " - "tool_type=%i, altered=%s, was_touching=%s, touching=%s", - touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure, - touch.major, touch.minor, touch.tool_type, toString(touch.altered), - toString(touch.was_touching), toString(touch.touching)); -} - -/** - * Remove the data for the provided pointers from the args. The pointers are identified by their - * pointerId, not by the index inside the array. - * Return the new NotifyMotionArgs struct that has the remaining pointers. - * The only fields that may be different in the returned args from the provided args are: - * - action - * - pointerCount - * - pointerProperties - * - pointerCoords - * Action might change because it contains a pointer index. If another pointer is removed, the - * active pointer index would be shifted. - * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer - * id is the acting pointer id. - * - * @param args the args from which the pointers should be removed - * @param pointerIds the pointer ids of the pointers that should be removed - */ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, const std::set<int32_t>& pointerIds) { const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); @@ -199,6 +186,26 @@ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, return newArgs; } +/** + * Remove stylus pointers from the provided NotifyMotionArgs. + * + * Return NotifyMotionArgs where the stylus pointers have been removed. + * If this results in removal of the active pointer, then return nullopt. + */ +static std::optional<NotifyMotionArgs> removeStylusPointerIds(const NotifyMotionArgs& args) { + std::set<int32_t> stylusPointerIds; + for (uint32_t i = 0; i < args.pointerCount; i++) { + if (args.pointerProperties[i].toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS) { + stylusPointerIds.insert(args.pointerProperties[i].id); + } + } + NotifyMotionArgs withoutStylusPointers = removePointerIds(args, stylusPointerIds); + if (withoutStylusPointers.pointerCount == 0 || withoutStylusPointers.action == ACTION_UNKNOWN) { + return std::nullopt; + } + return withoutStylusPointers; +} + std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( const InputDeviceInfo& deviceInfo) { if (!isFromTouchscreen(deviceInfo.getSources())) { @@ -326,6 +333,7 @@ void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { } void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + ALOGD_IF(DEBUG_INBOUND_MOTION, "%s: %s", __func__, args->dump().c_str()); { // acquire lock std::scoped_lock lock(mLock); const std::vector<NotifyMotionArgs> processedArgs = @@ -339,17 +347,22 @@ void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { mQueuedListener.flush(); } +void UnwantedInteractionBlocker::enqueueOutboundMotionLocked(const NotifyMotionArgs& args) { + ALOGD_IF(DEBUG_OUTBOUND_MOTION, "%s: %s", __func__, args.dump().c_str()); + mQueuedListener.notifyMotion(&args); +} + void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) { auto it = mPalmRejectors.find(args->deviceId); const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); if (!sendToPalmRejector) { - mQueuedListener.notifyMotion(args); + enqueueOutboundMotionLocked(*args); return; } std::vector<NotifyMotionArgs> processedArgs = it->second.processMotion(*args); for (const NotifyMotionArgs& loopArgs : processedArgs) { - mQueuedListener.notifyMotion(&loopArgs); + enqueueOutboundMotionLocked(loopArgs); } } @@ -428,9 +441,10 @@ void UnwantedInteractionBlocker::dump(std::string& dump) { dump += "UnwantedInteractionBlocker:\n"; dump += " mPreferStylusOverTouchBlocker:\n"; dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " "); - dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); + dump += StringPrintf(" mEnablePalmRejection: %s\n", + std::to_string(mEnablePalmRejection).c_str()); dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", - toString(isPalmRejectionEnabled())); + std::to_string(isPalmRejectionEnabled()).c_str()); dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; for (const auto& [deviceId, palmRejector] : mPalmRejectors) { dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); @@ -512,6 +526,15 @@ std::string SlotState::dump() const { return out; } +class AndroidPalmRejectionModel : public ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel { +public: + AndroidPalmRejectionModel() + : ::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel(/*default version*/ "", + std::vector<float>()) { + config_.resample_period = ::ui::kResamplePeriod; + } +}; + PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, std::unique_ptr<::ui::PalmDetectionFilter> filter) : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()), @@ -523,11 +546,9 @@ PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, return; } std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model = - std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( - std::vector<float>()); - mPalmDetectionFilter = - std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model), - mSharedPalmState.get()); + std::make_unique<AndroidPalmRejectionModel>(); + mPalmDetectionFilter = std::make_unique<PalmFilterImplementation>(mDeviceInfo, std::move(model), + mSharedPalmState.get()); } std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, @@ -541,7 +562,7 @@ std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, touches.emplace_back(::ui::InProgressTouchEvdev()); touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); - touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType); + // The field 'tool_type' is not used for palm rejection // Whether there is new information for the touch. touches.back().altered = true; @@ -588,32 +609,16 @@ std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, // The fields 'radius_x' and 'radius_x' are not used for palm rejection touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - touches.back().tool_code = BTN_TOOL_FINGER; + touches.back().tool_code = getLinuxToolCode(args.pointerProperties[i].toolType); // The field 'orientation' is not used for palm rejection // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection - touches.back().reported_tool_type = ::ui::EventPointerType::kTouch; + // The field 'reported_tool_type' is not used for palm rejection touches.back().stylus_button = false; } return touches; } -std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) { - if (mPalmDetectionFilter == nullptr) { - return {args}; - } - const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || - args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || - args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || - args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || - args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || - args.action == AMOTION_EVENT_ACTION_SCROLL; - if (skipThisEvent) { - // Lets not process hover events, button events, or scroll for now. - return {args}; - } - if (args.action == AMOTION_EVENT_ACTION_DOWN) { - mSuppressedPointerIds.clear(); - } +std::set<int32_t> PalmRejector::detectPalmPointers(const NotifyMotionArgs& args) { std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold; std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress; @@ -621,15 +626,26 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs // the slots that have been removed due to the incoming event. SlotState oldSlotState = mSlotState; mSlotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = getTouches(args, mDeviceInfo, oldSlotState, mSlotState); ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime); + if (DEBUG_MODEL) { + std::stringstream touchesStream; + for (const ::ui::InProgressTouchEvdev& touch : touches) { + touchesStream << touch.tracking_id << " : " << touch << "\n"; + } + ALOGD("Filter: touches = %s", touchesStream.str().c_str()); + } + mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress); + ALOGD_IF(DEBUG_MODEL, "Response: slotsToHold = %s, slotsToSuppress = %s", + slotsToHold.to_string().c_str(), slotsToSuppress.to_string().c_str()); + // Now that we know which slots should be suppressed, let's convert those to pointer id's. - std::set<int32_t> oldSuppressedIds; - std::swap(oldSuppressedIds, mSuppressedPointerIds); + std::set<int32_t> newSuppressedIds; for (size_t i = 0; i < args.pointerCount; i++) { const int32_t pointerId = args.pointerProperties[i].id; std::optional<size_t> slot = oldSlotState.getSlotForPointerId(pointerId); @@ -638,9 +654,41 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId); } if (slotsToSuppress.test(*slot)) { - mSuppressedPointerIds.insert(pointerId); + newSuppressedIds.insert(pointerId); } } + return newSuppressedIds; +} + +std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs& args) { + if (mPalmDetectionFilter == nullptr) { + return {args}; + } + const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || + args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || + args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || + args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || + args.action == AMOTION_EVENT_ACTION_SCROLL; + if (skipThisEvent) { + // Lets not process hover events, button events, or scroll for now. + return {args}; + } + if (args.action == AMOTION_EVENT_ACTION_DOWN) { + mSuppressedPointerIds.clear(); + } + + std::set<int32_t> oldSuppressedIds; + std::swap(oldSuppressedIds, mSuppressedPointerIds); + + std::optional<NotifyMotionArgs> touchOnlyArgs = removeStylusPointerIds(args); + if (touchOnlyArgs) { + mSuppressedPointerIds = detectPalmPointers(*touchOnlyArgs); + } else { + // This is a stylus-only event. + // We can skip this event and just keep the suppressed pointer ids the same as before. + mSuppressedPointerIds = oldSuppressedIds; + } std::vector<NotifyMotionArgs> argsWithoutUnwantedPointers = cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds); @@ -648,29 +696,40 @@ std::vector<NotifyMotionArgs> PalmRejector::processMotion(const NotifyMotionArgs LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str()); } - if (mSuppressedPointerIds != oldSuppressedIds) { - if (argsWithoutUnwantedPointers.size() != 1 || - argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) { - ALOGI("Palm detected, removing pointer ids %s from %s", - dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str()); - } + // Only log if new pointers are getting rejected. That means mSuppressedPointerIds is not a + // subset of oldSuppressedIds. + if (!std::includes(oldSuppressedIds.begin(), oldSuppressedIds.end(), + mSuppressedPointerIds.begin(), mSuppressedPointerIds.end())) { + ALOGI("Palm detected, removing pointer ids %s after %" PRId64 "ms from %s", + dumpSet(mSuppressedPointerIds).c_str(), ns2ms(args.eventTime - args.downTime), + args.dump().c_str()); } return argsWithoutUnwantedPointers; } -const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { +const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() const { return mDeviceInfo; } std::string PalmRejector::dump() const { std::string out; out += "mDeviceInfo:\n"; - out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " "); + std::stringstream deviceInfo; + deviceInfo << mDeviceInfo << ", touch_major_res=" << mDeviceInfo.touch_major_res + << ", touch_minor_res=" << mDeviceInfo.touch_minor_res << "\n"; + out += addLinePrefix(deviceInfo.str(), " "); out += "mSlotState:\n"; out += addLinePrefix(mSlotState.dump(), " "); out += "mSuppressedPointerIds: "; out += dumpSet(mSuppressedPointerIds) + "\n"; + std::stringstream state; + state << *mSharedPalmState; + out += "mSharedPalmState: " + state.str() + "\n"; + std::stringstream filter; + filter << static_cast<const PalmFilterImplementation&>(*mPalmDetectionFilter); + out += "mPalmDetectionFilter:\n"; + out += addLinePrefix(filter.str(), " ") + "\n"; return out; } diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h index a43376419f..5d0dde8e64 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.h +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -43,6 +43,25 @@ std::optional<AndroidPalmFilterDeviceInfo> createPalmFilterDeviceInfo( static constexpr int32_t ACTION_UNKNOWN = -1; +/** + * Remove the data for the provided pointers from the args. The pointers are identified by their + * pointerId, not by the index inside the array. + * Return the new NotifyMotionArgs struct that has the remaining pointers. + * The only fields that may be different in the returned args from the provided args are: + * - action + * - pointerCount + * - pointerProperties + * - pointerCoords + * Action might change because it contains a pointer index. If another pointer is removed, the + * active pointer index would be shifted. + * + * If the active pointer id is removed (for example, for events like + * POINTER_UP or POINTER_DOWN), then the action is set to ACTION_UNKNOWN. It is up to the caller + * to set the action appropriately after the call. + * + * @param args the args from which the pointers should be removed + * @param pointerIds the pointer ids of the pointers that should be removed + */ NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, const std::set<int32_t>& pointerIds); @@ -101,6 +120,9 @@ private: std::map<int32_t /*deviceId*/, PalmRejector> mPalmRejectors GUARDED_BY(mLock); // TODO(b/210159205): delete this when simultaneous stylus and touch is supported void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock); + + // Call this function for outbound events so that they can be logged when logging is enabled. + void enqueueOutboundMotionLocked(const NotifyMotionArgs& args) REQUIRES(mLock); }; class SlotState { @@ -144,13 +166,21 @@ public: std::vector<NotifyMotionArgs> processMotion(const NotifyMotionArgs& args); // Get the device info of this device, for comparison purposes - const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo(); + const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo() const; std::string dump() const; private: PalmRejector(const PalmRejector&) = delete; PalmRejector& operator=(const PalmRejector&) = delete; + /** + * Update the slot state and send this event to the palm rejection model for palm detection. + * Return the pointer ids that should be suppressed. + * + * This function is not const because it has side-effects. It will update the slot state using + * the incoming args! Also, it will call Filter(..), which has side-effects. + */ + std::set<int32_t> detectPalmPointers(const NotifyMotionArgs& args); std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState; AndroidPalmFilterDeviceInfo mDeviceInfo; std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter; diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index 4f8995f43d..9a2aea69ec 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -75,11 +75,8 @@ const bool DEBUG_TOUCH_MODE = /** * Log debug messages about touch occlusion - * Enable this via "adb shell setprop log.tag.InputDispatcherTouchOcclusion DEBUG" (requires - * restart) */ -const bool DEBUG_TOUCH_OCCLUSION = - __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "TouchOcclusion", ANDROID_LOG_INFO); +constexpr bool DEBUG_TOUCH_OCCLUSION = true; /** * Log debug messages about the app switch latency optimization. diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5e9427ad87..d8120fcb5e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2195,10 +2195,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the temporary touch state. BitSet32 pointerIds; - if (isSplit) { - uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - pointerIds.markBit(pointerId); - } + pointerIds.markBit(entry.pointerProperties[pointerIndex].id); tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); } @@ -2275,9 +2272,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } BitSet32 pointerIds; - if (isSplit) { - pointerIds.markBit(entry.pointerProperties[0].id); - } + pointerIds.markBit(entry.pointerProperties[0].id); tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); } } @@ -2453,21 +2448,28 @@ Failed: } } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) { // One pointer went up. - if (isSplit) { - int32_t pointerIndex = getMotionEventActionPointerIndex(action); - uint32_t pointerId = entry.pointerProperties[pointerIndex].id; - - for (size_t i = 0; i < tempTouchState.windows.size();) { - TouchedWindow& touchedWindow = tempTouchState.windows[i]; - if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) { - touchedWindow.pointerIds.clearBit(pointerId); - if (touchedWindow.pointerIds.isEmpty()) { - tempTouchState.windows.erase(tempTouchState.windows.begin() + i); - continue; - } - } - i += 1; + int32_t pointerIndex = getMotionEventActionPointerIndex(action); + uint32_t pointerId = entry.pointerProperties[pointerIndex].id; + + for (size_t i = 0; i < tempTouchState.windows.size();) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + touchedWindow.pointerIds.clearBit(pointerId); + if (touchedWindow.pointerIds.isEmpty()) { + tempTouchState.windows.erase(tempTouchState.windows.begin() + i); + continue; + } + i += 1; + } + } else if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // If no split, we suppose all touched windows should receive pointer down. + const int32_t pointerIndex = getMotionEventActionPointerIndex(action); + for (size_t i = 0; i < tempTouchState.windows.size(); i++) { + TouchedWindow& touchedWindow = tempTouchState.windows[i]; + // Ignore drag window for it should just track one pointer. + if (mDragState && mDragState->dragWindow == touchedWindow.windowHandle) { + continue; } + touchedWindow.pointerIds.markBit(entry.pointerProperties[pointerIndex].id); } } @@ -4470,6 +4472,14 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( if (it == mDisplayInfos.end()) return; const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform; + if (entry.xCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION && + entry.yCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) { + const vec2 cursor = + MotionEvent::calculateTransformedXY(entry.source, transformToDisplay, + {entry.xCursorPosition, entry.yCursorPosition}); + entry.xCursorPosition = cursor.x; + entry.yCursorPosition = cursor.y; + } for (uint32_t i = 0; i < entry.pointerCount; i++) { entry.pointerCoords[i] = MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay, @@ -5079,14 +5089,13 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Store the dragging window. if (isDragDrop) { - if (pointerIds.count() > 1) { - ALOGW("The drag and drop cannot be started when there is more than 1 pointer on the" - " window."); + if (pointerIds.count() != 1) { + ALOGW("The drag and drop cannot be started when there is no pointer or more than 1" + " pointer on the window."); return false; } - // If the window didn't not support split or the source is mouse, the pointerIds count - // would be 0, so we have to track the pointer 0. - const int32_t id = pointerIds.count() == 0 ? 0 : pointerIds.firstMarkedBit(); + // Track the pointer id for drag window and generate the drag state. + const int32_t id = pointerIds.firstMarkedBit(); mDragState = std::make_unique<DragState>(toWindowHandle, id); } @@ -6322,6 +6331,13 @@ void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& window { // acquire lock std::scoped_lock _l(mLock); + + // Ensure that we have an entry created for all existing displays so that if a displayId has + // no windows, we can tell that the windows were removed from the display. + for (const auto& [displayId, _] : mWindowHandlesByDisplay) { + handlesPerDisplay[displayId]; + } + mDisplayInfos.clear(); for (const auto& displayInfo : displayInfos) { mDisplayInfos.emplace(displayInfo.displayId, displayInfo); diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp index d39113b286..2df97d9a12 100644 --- a/services/inputflinger/dispatcher/InputTarget.cpp +++ b/services/inputflinger/dispatcher/InputTarget.cpp @@ -43,8 +43,8 @@ std::string dispatchModeToString(int32_t dispatchMode) { } void InputTarget::addPointers(BitSet32 newPointerIds, const ui::Transform& transform) { - // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors - // and non splittable windows since we will just use all the pointers from the input event. + // The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no + // valid pointer property from the input event. if (newPointerIds.isEmpty()) { setDefaultPointerTransform(transform); return; diff --git a/services/inputflinger/dispatcher/TouchedWindow.h b/services/inputflinger/dispatcher/TouchedWindow.h index 4c31ec3acd..6783022ca1 100644 --- a/services/inputflinger/dispatcher/TouchedWindow.h +++ b/services/inputflinger/dispatcher/TouchedWindow.h @@ -29,7 +29,7 @@ namespace inputdispatcher { struct TouchedWindow { sp<gui::WindowInfoHandle> windowHandle; int32_t targetFlags; - BitSet32 pointerIds; // zero unless target flag FLAG_SPLIT is set + BitSet32 pointerIds; }; } // namespace inputdispatcher diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp index ba5083bec3..989700f6cf 100644 --- a/services/inputflinger/reader/InputDevice.cpp +++ b/services/inputflinger/reader/InputDevice.cpp @@ -110,7 +110,8 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { dump += "<none>\n"; } dump += StringPrintf(INDENT2 "HasMic: %s\n", toString(mHasMic)); - dump += StringPrintf(INDENT2 "Sources: 0x%08x\n", deviceInfo.getSources()); + dump += StringPrintf(INDENT2 "Sources: %s\n", + inputEventSourceToString(deviceInfo.getSources()).c_str()); dump += StringPrintf(INDENT2 "KeyboardType: %d\n", deviceInfo.getKeyboardType()); dump += StringPrintf(INDENT2 "ControllerNum: %d\n", deviceInfo.getControllerNumber()); @@ -128,10 +129,10 @@ void InputDevice::dump(std::string& dump, const std::string& eventHubDevStr) { snprintf(name, sizeof(name), "%d", range.axis); } dump += StringPrintf(INDENT3 - "%s: source=0x%08x, " + "%s: source=%s, " "min=%0.3f, max=%0.3f, flat=%0.3f, fuzz=%0.3f, resolution=%0.3f\n", - name, range.source, range.min, range.max, range.flat, range.fuzz, - range.resolution); + name, inputEventSourceToString(range.source).c_str(), range.min, + range.max, range.flat, range.fuzz, range.resolution); } } @@ -231,6 +232,10 @@ void InputDevice::addEventHubDevice(int32_t eventHubId, bool populateMappers) { } void InputDevice::removeEventHubDevice(int32_t eventHubId) { + if (mController != nullptr && mController->getEventHubId() == eventHubId) { + // Delete mController, since the corresponding eventhub device is going away + mController = nullptr; + } mDevices.erase(eventHubId); } diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 9c5a129213..9bcf463c36 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -196,9 +196,9 @@ void InputReader::addDeviceLocked(nsecs_t when, int32_t eventHubId) { "(ignored non-input device)", device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str()); } else { - ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=0x%08x", + ALOGI("Device added: id=%d, eventHubId=%d, name='%s', descriptor='%s',sources=%s", device->getId(), eventHubId, identifier.name.c_str(), identifier.descriptor.c_str(), - device->getSources()); + inputEventSourceToString(device->getSources()).c_str()); } mDevices.emplace(eventHubId, device); @@ -250,9 +250,10 @@ void InputReader::removeDeviceLocked(nsecs_t when, int32_t eventHubId) { device->getId(), eventHubId, device->getName().c_str(), device->getDescriptor().c_str()); } else { - ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=0x%08x", + ALOGI("Device removed: id=%d, eventHubId=%d, name='%s', descriptor='%s', sources=%s", device->getId(), eventHubId, device->getName().c_str(), - device->getDescriptor().c_str(), device->getSources()); + device->getDescriptor().c_str(), + inputEventSourceToString(device->getSources()).c_str()); } device->removeEventHubDevice(eventHubId); diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index a6934960c9..8065f57524 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -524,4 +524,8 @@ std::optional<int32_t> PeripheralController::getLightPlayerId(int32_t lightId) { return light->getLightPlayerId(); } +int32_t PeripheralController::getEventHubId() const { + return getDeviceContext().getEventHubId(); +} + } // namespace android diff --git a/services/inputflinger/reader/controller/PeripheralController.h b/services/inputflinger/reader/controller/PeripheralController.h index b1bc8c732c..ac951ebe2a 100644 --- a/services/inputflinger/reader/controller/PeripheralController.h +++ b/services/inputflinger/reader/controller/PeripheralController.h @@ -31,6 +31,7 @@ public: explicit PeripheralController(InputDeviceContext& deviceContext); ~PeripheralController() override; + int32_t getEventHubId() const override; void populateDeviceInfo(InputDeviceInfo* deviceInfo) override; void dump(std::string& dump) override; bool setLightColor(int32_t lightId, int32_t color) override; @@ -43,6 +44,7 @@ public: private: inline int32_t getDeviceId() { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; } InputDeviceContext& mDeviceContext; void configureLights(); diff --git a/services/inputflinger/reader/controller/PeripheralControllerInterface.h b/services/inputflinger/reader/controller/PeripheralControllerInterface.h index 7688a431d1..306e36119b 100644 --- a/services/inputflinger/reader/controller/PeripheralControllerInterface.h +++ b/services/inputflinger/reader/controller/PeripheralControllerInterface.h @@ -33,6 +33,8 @@ public: PeripheralControllerInterface() {} virtual ~PeripheralControllerInterface() {} + virtual int32_t getEventHubId() const = 0; + // Interface methods for Battery virtual std::optional<int32_t> getBatteryCapacity(int32_t batteryId) = 0; virtual std::optional<int32_t> getBatteryStatus(int32_t batteryId) = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index a9a4c71c02..8233682a32 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -25,6 +25,8 @@ #include "PointerControllerInterface.h" #include "TouchCursorInputMapperCommon.h" +#include "input/PrintTools.h" + namespace android { // The default velocity control parameters that has no effect. @@ -76,7 +78,7 @@ uint32_t CursorInputMapper::getSources() const { void CursorInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); - if (mParameters.mode == Parameters::MODE_POINTER) { + if (mParameters.mode == Parameters::Mode::POINTER) { float minX, minY, maxX, maxY; if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) { info->addMotionRange(AMOTION_EVENT_AXIS_X, mSource, minX, maxX, 0.0f, 0.0f, 0.0f); @@ -113,6 +115,7 @@ void CursorInputMapper::dump(std::string& dump) { toString(mCursorScrollAccumulator.haveRelativeHWheel())); dump += StringPrintf(INDENT3 "VWheelScale: %0.3f\n", mVWheelScale); dump += StringPrintf(INDENT3 "HWheelScale: %0.3f\n", mHWheelScale); + dump += StringPrintf(INDENT3 "DisplayId: %s\n", toString(mDisplayId).c_str()); dump += StringPrintf(INDENT3 "Orientation: %d\n", mOrientation); dump += StringPrintf(INDENT3 "ButtonState: 0x%08x\n", mButtonState); dump += StringPrintf(INDENT3 "Down: %s\n", toString(isPointerDown(mButtonState))); @@ -131,12 +134,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* // Configure device mode. switch (mParameters.mode) { - case Parameters::MODE_POINTER_RELATIVE: + case Parameters::Mode::POINTER_RELATIVE: // Should not happen during first time configuration. ALOGE("Cannot start a device in MODE_POINTER_RELATIVE, starting in MODE_POINTER"); - mParameters.mode = Parameters::MODE_POINTER; + mParameters.mode = Parameters::Mode::POINTER; [[fallthrough]]; - case Parameters::MODE_POINTER: + case Parameters::Mode::POINTER: mSource = AINPUT_SOURCE_MOUSE; mXPrecision = 1.0f; mYPrecision = 1.0f; @@ -144,7 +147,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mYScale = 1.0f; mPointerController = getContext()->getPointerController(getDeviceId()); break; - case Parameters::MODE_NAVIGATION: + case Parameters::Mode::NAVIGATION: mSource = AINPUT_SOURCE_TRACKBALL; mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; @@ -157,12 +160,13 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mHWheelScale = 1.0f; } - const bool configurePointerCapture = (!changes && config->pointerCaptureRequest.enable) || - (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE); + const bool configurePointerCapture = mParameters.mode != Parameters::Mode::NAVIGATION && + ((!changes && config->pointerCaptureRequest.enable) || + (changes & InputReaderConfiguration::CHANGE_POINTER_CAPTURE)); if (configurePointerCapture) { if (config->pointerCaptureRequest.enable) { - if (mParameters.mode == Parameters::MODE_POINTER) { - mParameters.mode = Parameters::MODE_POINTER_RELATIVE; + if (mParameters.mode == Parameters::Mode::POINTER) { + mParameters.mode = Parameters::Mode::POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; // Keep PointerController around in order to preserve the pointer position. mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); @@ -170,8 +174,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); } } else { - if (mParameters.mode == Parameters::MODE_POINTER_RELATIVE) { - mParameters.mode = Parameters::MODE_POINTER; + if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { + mParameters.mode = Parameters::Mode::POINTER; mSource = AINPUT_SOURCE_MOUSE; } else { ALOGE("Cannot release pointer capture, device is not in MODE_POINTER_RELATIVE"); @@ -186,8 +190,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED) || configurePointerCapture) { - if (config->pointerCaptureRequest.enable) { - // Disable any acceleration or scaling when Pointer Capture is enabled. + if (mParameters.mode == Parameters::Mode::POINTER_RELATIVE) { + // Disable any acceleration or scaling for the pointer when Pointer Capture is enabled. mPointerVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelXVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); mWheelYVelocityControl.setParameters(FLAT_VELOCITY_CONTROL_PARAMS); @@ -198,21 +202,36 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } } - if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { + if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO) || + configurePointerCapture) { + const bool isPointer = mParameters.mode == Parameters::Mode::POINTER; + + mDisplayId = ADISPLAY_ID_NONE; + if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) { + // This InputDevice is associated with a viewport. + // Only generate events for the associated display. + const bool mismatchedPointerDisplay = + isPointer && (viewport->displayId != mPointerController->getDisplayId()); + mDisplayId = mismatchedPointerDisplay ? std::nullopt + : std::make_optional(viewport->displayId); + } else if (isPointer) { + // The InputDevice is not associated with a viewport, but it controls the mouse pointer. + mDisplayId = mPointerController->getDisplayId(); + } + mOrientation = DISPLAY_ORIENTATION_0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); - // InputReader works in the un-rotated display coordinate space, so we don't need to do // anything if the device is already orientation-aware. If the device is not // orientation-aware, then we need to apply the inverse rotation of the display so that // when the display rotation is applied later as a part of the per-window transform, we - // get the expected screen coordinates. - if (!isOrientedDevice) { - std::optional<DisplayViewport> internalViewport = - config->getDisplayViewportByType(ViewportType::INTERNAL); - if (internalViewport) { - mOrientation = getInverseRotation(internalViewport->orientation); + // get the expected screen coordinates. When pointer capture is enabled, we do not apply any + // rotations and report values directly from the input device. + if (!isOrientedDevice && mDisplayId && + mParameters.mode != Parameters::Mode::POINTER_RELATIVE) { + if (auto viewport = config->getDisplayViewportById(*mDisplayId); viewport) { + mOrientation = getInverseRotation(viewport->orientation); } } @@ -221,12 +240,12 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* } void CursorInputMapper::configureParameters() { - mParameters.mode = Parameters::MODE_POINTER; + mParameters.mode = Parameters::Mode::POINTER; String8 cursorModeString; if (getDeviceContext().getConfiguration().tryGetProperty(String8("cursor.mode"), cursorModeString)) { if (cursorModeString == "navigation") { - mParameters.mode = Parameters::MODE_NAVIGATION; + mParameters.mode = Parameters::Mode::NAVIGATION; } else if (cursorModeString != "pointer" && cursorModeString != "default") { ALOGW("Invalid value for cursor.mode: '%s'", cursorModeString.string()); } @@ -237,7 +256,7 @@ void CursorInputMapper::configureParameters() { mParameters.orientationAware); mParameters.hasAssociatedDisplay = false; - if (mParameters.mode == Parameters::MODE_POINTER || mParameters.orientationAware) { + if (mParameters.mode == Parameters::Mode::POINTER || mParameters.orientationAware) { mParameters.hasAssociatedDisplay = true; } } @@ -246,21 +265,7 @@ void CursorInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; dump += StringPrintf(INDENT4 "HasAssociatedDisplay: %s\n", toString(mParameters.hasAssociatedDisplay)); - - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - dump += INDENT4 "Mode: pointer\n"; - break; - case Parameters::MODE_POINTER_RELATIVE: - dump += INDENT4 "Mode: relative pointer\n"; - break; - case Parameters::MODE_NAVIGATION: - dump += INDENT4 "Mode: navigation\n"; - break; - default: - ALOG_ASSERT(false); - } - + dump += StringPrintf(INDENT4 "Mode: %s\n", ftl::enum_string(mParameters.mode).c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); } @@ -290,6 +295,11 @@ void CursorInputMapper::process(const RawEvent* rawEvent) { } void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { + if (!mDisplayId) { + // Ignore events when there is no target display configured. + return; + } + int32_t lastButtonState = mButtonState; int32_t currentButtonState = mCursorButtonAccumulator.getButtonState(); mButtonState = currentButtonState; @@ -335,7 +345,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mPointerVelocityControl.move(when, &deltaX, &deltaY); - int32_t displayId = ADISPLAY_ID_NONE; float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { @@ -359,7 +368,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - displayId = mPointerController->getDisplayId(); } else { // Pointer capture and navigation modes pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, deltaX); @@ -381,7 +389,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { // Synthesize key down from buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_DOWN, when, readTime, getDeviceId(), - mSource, displayId, policyFlags, lastButtonState, currentButtonState); + mSource, *mDisplayId, policyFlags, lastButtonState, currentButtonState); // Send motion event. if (downChanged || moved || scrolled || buttonsChanged) { @@ -402,7 +410,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { int32_t actionButton = BitSet32::valueForBit(released.clearFirstMarkedBit()); buttonState &= ~actionButton; NotifyMotionArgs releaseArgs(getContext()->getNextId(), when, readTime, - getDeviceId(), mSource, displayId, policyFlags, + getDeviceId(), mSource, *mDisplayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -414,7 +422,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } NotifyMotionArgs args(getContext()->getNextId(), when, readTime, getDeviceId(), mSource, - displayId, policyFlags, motionEventAction, 0, 0, metaState, + *mDisplayId, policyFlags, motionEventAction, 0, 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, &pointerCoords, mXPrecision, mYPrecision, xCursorPosition, yCursorPosition, downTime, @@ -427,7 +435,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { int32_t actionButton = BitSet32::valueForBit(pressed.clearFirstMarkedBit()); buttonState |= actionButton; NotifyMotionArgs pressArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, displayId, policyFlags, + mSource, *mDisplayId, policyFlags, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, 0, metaState, buttonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -443,7 +451,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { // Send hover move after UP to tell the application that the mouse is hovering now. if (motionEventAction == AMOTION_EVENT_ACTION_UP && (mSource == AINPUT_SOURCE_MOUSE)) { NotifyMotionArgs hoverArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, displayId, policyFlags, + mSource, *mDisplayId, policyFlags, AMOTION_EVENT_ACTION_HOVER_MOVE, 0, 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -458,7 +466,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_HSCROLL, hscroll); NotifyMotionArgs scrollArgs(getContext()->getNextId(), when, readTime, getDeviceId(), - mSource, displayId, policyFlags, + mSource, *mDisplayId, policyFlags, AMOTION_EVENT_ACTION_SCROLL, 0, 0, metaState, currentButtonState, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1, &pointerProperties, @@ -470,7 +478,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { // Synthesize key up from buttons if needed. synthesizeButtonKeys(getContext(), AKEY_EVENT_ACTION_UP, when, readTime, getDeviceId(), mSource, - displayId, policyFlags, lastButtonState, currentButtonState); + *mDisplayId, policyFlags, lastButtonState, currentButtonState); mCursorMotionAccumulator.finishSync(); mCursorScrollAccumulator.finishSync(); @@ -485,16 +493,7 @@ int32_t CursorInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCod } std::optional<int32_t> CursorInputMapper::getAssociatedDisplayId() { - if (mParameters.hasAssociatedDisplay) { - if (mParameters.mode == Parameters::MODE_POINTER) { - return std::make_optional(mPointerController->getDisplayId()); - } else { - // If the device is orientationAware and not a mouse, - // it expects to dispatch events to any display - return std::make_optional(ADISPLAY_ID_NONE); - } - } - return std::nullopt; + return mDisplayId; } } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index c84c6c4229..60b3dd9ee0 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -74,10 +74,17 @@ private: // Immutable configuration parameters. struct Parameters { - enum Mode { - MODE_POINTER, - MODE_POINTER_RELATIVE, - MODE_NAVIGATION, + enum class Mode { + // In POINTER mode, the device is a mouse that controls the mouse cursor on the screen, + // reporting absolute screen locations using SOURCE_MOUSE. + POINTER, + // A mouse device in POINTER mode switches to the POINTER_RELATIVE mode when Pointer + // Capture is enabled, and reports relative values only using SOURCE_MOUSE_RELATIVE. + POINTER_RELATIVE, + // A device in NAVIGATION mode emits relative values using SOURCE_TRACKBALL. + NAVIGATION, + + ftl_last = NAVIGATION, }; Mode mode; @@ -104,6 +111,10 @@ private: VelocityControl mWheelXVelocityControl; VelocityControl mWheelYVelocityControl; + // The display that events generated by this mapper should target. This can be set to + // ADISPLAY_ID_NONE to target the focused display. If there is no display target (i.e. + // std::nullopt), all events will be ignored. + std::optional<int32_t> mDisplayId; int32_t mOrientation; std::shared_ptr<PointerControllerInterface> mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 637b1cb263..428fe10156 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -44,6 +44,8 @@ static constexpr nsecs_t STYLUS_DATA_LATENCY = ms2ns(10); // --- Static Definitions --- +static const DisplayViewport kUninitializedViewport; + template <typename T> inline static void swap(T& a, T& b) { T temp = a; @@ -390,6 +392,10 @@ void TouchInputMapper::configure(nsecs_t when, const InputReaderConfiguration* c } if (changes && resetNeeded) { + // If the device needs to be reset, cancel any ongoing gestures and reset the state. + cancelTouch(when, when); + reset(when); + // Send reset, unless this is the first time the device has been configured, // in which case the reader will call reset itself after all mappers are ready. NotifyDeviceResetArgs args(getContext()->getNextId(), when, getDeviceId()); @@ -877,7 +883,7 @@ void TouchInputMapper::initializeOrientedRanges() { } void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) { - DeviceMode oldDeviceMode = mDeviceMode; + const DeviceMode oldDeviceMode = mDeviceMode; resolveExternalStylusPresence(); @@ -906,42 +912,37 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) mDeviceMode = DeviceMode::UNSCALED; } - // Ensure we have valid X and Y axes. + const std::optional<DisplayViewport> newViewportOpt = findViewport(); + + // Ensure the device is valid and can be used. if (!mRawPointerAxes.x.valid || !mRawPointerAxes.y.valid) { ALOGW("Touch device '%s' did not report support for X or Y axis! " "The device will be inoperable.", getDeviceName().c_str()); mDeviceMode = DeviceMode::DISABLED; - return; - } - - // Get associated display dimensions. - std::optional<DisplayViewport> newViewport = findViewport(); - if (!newViewport) { + } else if (!newViewportOpt) { ALOGI("Touch device '%s' could not query the properties of its associated " "display. The device will be inoperable until the display size " "becomes available.", getDeviceName().c_str()); mDeviceMode = DeviceMode::DISABLED; - return; - } - - if (!newViewport->isActive) { + } else if (!newViewportOpt->isActive) { ALOGI("Disabling %s (device %i) because the associated viewport is not active", getDeviceName().c_str(), getDeviceId()); mDeviceMode = DeviceMode::DISABLED; - return; } // Raw width and height in the natural orientation. const int32_t rawWidth = mRawPointerAxes.getRawWidth(); const int32_t rawHeight = mRawPointerAxes.getRawHeight(); - const bool viewportChanged = mViewport != *newViewport; + const DisplayViewport& newViewport = newViewportOpt.value_or(kUninitializedViewport); + const bool viewportChanged = mViewport != newViewport; bool skipViewportUpdate = false; if (viewportChanged) { - const bool viewportOrientationChanged = mViewport.orientation != newViewport->orientation; - mViewport = *newViewport; + const bool viewportOrientationChanged = mViewport.orientation != newViewport.orientation; + const bool viewportDisplayIdChanged = mViewport.displayId != newViewport.displayId; + mViewport = newViewport; if (mDeviceMode == DeviceMode::DIRECT || mDeviceMode == DeviceMode::POINTER) { // Convert rotated viewport to the natural orientation. @@ -1016,8 +1017,9 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) : getInverseRotation(mViewport.orientation); // For orientation-aware devices that work in the un-rotated coordinate space, the // viewport update should be skipped if it is only a change in the orientation. - skipViewportUpdate = mParameters.orientationAware && mDisplayWidth == oldDisplayWidth && - mDisplayHeight == oldDisplayHeight && viewportOrientationChanged; + skipViewportUpdate = !viewportDisplayIdChanged && mParameters.orientationAware && + mDisplayWidth == oldDisplayWidth && mDisplayHeight == oldDisplayHeight && + viewportOrientationChanged; // Apply the input device orientation for the device. mInputDeviceOrientation = @@ -1094,10 +1096,6 @@ void TouchInputMapper::configureInputDevice(nsecs_t when, bool* outResetNeeded) // of the diagonal axis of the touch pad. Touches that are wider than this are // translated into freeform gestures. mPointerGestureMaxSwipeWidth = mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; - - // Abort current pointer usages because the state has changed. - const nsecs_t readTime = when; // synthetic event - abortPointerUsage(when, readTime, 0 /*policyFlags*/); } // Inform the dispatcher about the changes. @@ -1926,6 +1924,10 @@ void TouchInputMapper::dispatchVirtualKey(nsecs_t when, nsecs_t readTime, uint32 } void TouchInputMapper::abortTouches(nsecs_t when, nsecs_t readTime, uint32_t policyFlags) { + if (mCurrentMotionAborted) { + // Current motion event was already aborted. + return; + } BitSet32 currentIdBits = mCurrentCookedState.cookedPointerData.touchingIdBits; if (!currentIdBits.isEmpty()) { int32_t metaState = getContext()->getGlobalMetaState(); diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 76a7c19086..75cd9da782 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -60,6 +60,7 @@ cc_test { }, static_libs: [ "libc++fs", + "libgmock", ], require_root: true, test_suites: ["device-tests"], diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index df4307101f..df1a230002 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -2293,6 +2293,35 @@ TEST_F(InputDispatcherTest, ActionOutsideSentOnlyWhenAWindowIsTouched) { thirdWindow->consumeMotionDown(); } +TEST_F(InputDispatcherTest, OnWindowInfosChanged_RemoveAllWindowsOnDisplay) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + sp<FakeWindowHandle> window = + new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT); + window->setFocusable(true); + + mDispatcher->onWindowInfosChanged({*window->getInfo()}, {}); + setFocusedWindow(window); + + window->consumeFocusEvent(true); + + NotifyKeyArgs keyDown = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT); + NotifyKeyArgs keyUp = generateKeyArgs(AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT); + mDispatcher->notifyKey(&keyDown); + mDispatcher->notifyKey(&keyUp); + + window->consumeKeyDown(ADISPLAY_ID_DEFAULT); + window->consumeKeyUp(ADISPLAY_ID_DEFAULT); + + // All windows are removed from the display. Ensure that we can no longer dispatch to it. + mDispatcher->onWindowInfosChanged({}, {}); + + window->consumeFocusEvent(false); + + mDispatcher->notifyKey(&keyDown); + mDispatcher->notifyKey(&keyUp); + window->assertNoEvents(); +} + /** * Ensure the correct coordinate spaces are used by InputDispatcher. * @@ -6096,6 +6125,8 @@ protected: sp<FakeWindowHandle> mWindow; sp<FakeWindowHandle> mSecondWindow; sp<FakeWindowHandle> mDragWindow; + // Mouse would force no-split, set the id as non-zero to verify if drag state could track it. + static constexpr int32_t MOUSE_POINTER_ID = 1; void SetUp() override { InputDispatcherTest::SetUp(); @@ -6110,11 +6141,41 @@ protected: mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}}); } - void injectDown() { - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {50, 50})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { + switch (fromSource) { + case AINPUT_SOURCE_TOUCHSCREEN: + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {50, 50})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + break; + case AINPUT_SOURCE_STYLUS: + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent( + mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, + AINPUT_SOURCE_STYLUS) + .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) + .pointer(PointerBuilder(0, AMOTION_EVENT_TOOL_TYPE_STYLUS) + .x(50) + .y(50)) + .build())); + break; + case AINPUT_SOURCE_MOUSE: + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent( + mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, + AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(50) + .y(50)) + .build())); + break; + default: + FAIL() << "Source " << fromSource << " doesn't support drag and drop"; + } // Window should receive motion event. mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); @@ -6123,9 +6184,9 @@ protected: // Start performing drag, we will create a drag window and transfer touch to it. // @param sendDown : if true, send a motion down on first window before perform drag and drop. // Returns true on success. - bool performDrag(bool sendDown = true) { + bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { if (sendDown) { - injectDown(); + injectDown(fromSource); } // The drag window covers the entire display @@ -6143,36 +6204,10 @@ protected: } return transferred; } - - // Start performing drag, we will create a drag window and transfer touch to it. - void performStylusDrag() { - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, - MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, - AINPUT_SOURCE_STYLUS) - .buttonState(AMOTION_EVENT_BUTTON_STYLUS_PRIMARY) - .pointer(PointerBuilder(0, - AMOTION_EVENT_TOOL_TYPE_STYLUS) - .x(50) - .y(50)) - .build())); - mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); - - // The drag window covers the entire display - mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT); - mDispatcher->setInputWindows( - {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}}); - - // Transfer touch focus to the drag window - mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(), - true /* isDragDrop */); - mWindow->consumeMotionCancel(); - mDragWindow->consumeMotionDown(); - } }; TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { - performDrag(); + startDrag(); // Move on window. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -6210,7 +6245,7 @@ TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) { } TEST_F(InputDispatcherDragTests, DragAndDrop) { - performDrag(); + startDrag(); // Move on window. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -6242,7 +6277,7 @@ TEST_F(InputDispatcherDragTests, DragAndDrop) { } TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { - performStylusDrag(); + startDrag(true, AINPUT_SOURCE_STYLUS); // Move on window and keep button pressed. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -6289,7 +6324,7 @@ TEST_F(InputDispatcherDragTests, StylusDragAndDrop) { } TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { - performDrag(); + startDrag(); // Set second window invisible. mSecondWindow->setVisible(false); @@ -6325,6 +6360,9 @@ TEST_F(InputDispatcherDragTests, DragAndDropOnInvalidWindow) { } TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) { + // Ensure window could track pointerIds if it didn't support split touch. + mWindow->setPreventSplitting(true); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) @@ -6345,7 +6383,7 @@ TEST_F(InputDispatcherDragTests, NoDragAndDropWhenMultiFingers) { mWindow->consumeMotionPointerDown(1 /* pointerIndex */); // Should not perform drag and drop when window has multi fingers. - ASSERT_FALSE(performDrag(false)); + ASSERT_FALSE(startDrag(false)); } TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { @@ -6373,7 +6411,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); // Perform drag and drop from first window. - ASSERT_TRUE(performDrag(false)); + ASSERT_TRUE(startDrag(false)); // Move on window. const MotionEvent secondFingerMoveEvent = @@ -6408,7 +6446,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) { } TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { - performDrag(); + startDrag(); // Update window of second display. sp<FakeWindowHandle> windowInSecondary = @@ -6459,6 +6497,55 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenMultiDisplays) { mSecondWindow->assertNoEvents(); } +TEST_F(InputDispatcherDragTests, MouseDragAndDrop) { + startDrag(true, AINPUT_SOURCE_MOUSE); + // Move on window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, + AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(50) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(false, 50, 50); + mSecondWindow->assertNoEvents(); + + // Move to another window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) + .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) + .pointer(PointerBuilder(MOUSE_POINTER_ID, + AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT); + mWindow->consumeDragEvent(true, 150, 50); + mSecondWindow->consumeDragEvent(false, 50, 50); + + // drop to another window. + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, + MotionEventBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) + .buttonState(0) + .pointer(PointerBuilder(MOUSE_POINTER_ID, + AMOTION_EVENT_TOOL_TYPE_MOUSE) + .x(150) + .y(50)) + .build())) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + mFakePolicy->assertDropTargetEquals(mSecondWindow->getToken()); + mWindow->assertNoEvents(); + mSecondWindow->assertNoEvents(); +} + class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {}; TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) { diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index a26a0bcf67..74d4f3b99f 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -56,7 +56,9 @@ static constexpr nsecs_t READ_TIME = 4321; // Arbitrary display properties. static constexpr int32_t DISPLAY_ID = 0; +static const std::string DISPLAY_UNIQUE_ID = "local:1"; static constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; +static const std::string SECONDARY_DISPLAY_UNIQUE_ID = "local:2"; static constexpr int32_t DISPLAY_WIDTH = 480; static constexpr int32_t DISPLAY_HEIGHT = 800; static constexpr int32_t VIRTUAL_DISPLAY_ID = 1; @@ -91,6 +93,24 @@ static constexpr int32_t ACTION_POINTER_1_UP = // Error tolerance for floating point assertions. static const float EPSILON = 0.001f; +using ::testing::AllOf; + +MATCHER_P(WithAction, action, "InputEvent with specified action") { + return arg.action == action; +} + +MATCHER_P(WithSource, source, "InputEvent with specified source") { + return arg.source == source; +} + +MATCHER_P(WithDisplayId, displayId, "InputEvent with specified displayId") { + return arg.displayId == displayId; +} + +MATCHER_P2(WithCoords, x, y, "MotionEvent with specified action") { + return arg.pointerCoords[0].getX() == x && arg.pointerCoords[0].getY(); +} + template<typename T> static inline T min(T a, T b) { return a < b ? a : b; @@ -2137,6 +2157,8 @@ public: ~FakePeripheralController() override {} + int32_t getEventHubId() const { return getDeviceContext().getEventHubId(); } + void populateDeviceInfo(InputDeviceInfo* deviceInfo) override {} void dump(std::string& dump) override {} @@ -2170,6 +2192,7 @@ private: InputDeviceContext& mDeviceContext; inline int32_t getDeviceId() { return mDeviceContext.getId(); } inline InputDeviceContext& getDeviceContext() { return mDeviceContext; } + inline InputDeviceContext& getDeviceContext() const { return mDeviceContext; } }; TEST_F(InputReaderTest, BatteryGetCapacity) { @@ -2872,7 +2895,6 @@ TEST_F(InputDeviceTest, Configure_AssignsDisplayUniqueId) { // Device should be disabled because it is associated with a specific display, but the // corresponding display is not found. - const std::string DISPLAY_UNIQUE_ID = "displayUniqueId"; mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), InputReaderConfiguration::CHANGE_DISPLAY_INFO); @@ -2903,7 +2925,6 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, AINPUT_SOURCE_KEYBOARD); mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), 0); - const std::string DISPLAY_UNIQUE_ID = "displayUniqueId"; mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_ORIENTATION_0, /* isActive= */ true, DISPLAY_UNIQUE_ID, @@ -2913,6 +2934,21 @@ TEST_F(InputDeviceTest, Configure_UniqueId_CorrectlyMatches) { ASSERT_EQ(DISPLAY_UNIQUE_ID, mDevice->getAssociatedDisplayUniqueId()); } +/** + * This test reproduces a crash caused by a dangling reference that remains after device is added + * and removed. The reference is accessed in InputDevice::dump(..); + */ +TEST_F(InputDeviceTest, DumpDoesNotCrash) { + constexpr int32_t TEST_EVENTHUB_ID = 10; + mFakeEventHub->addDevice(TEST_EVENTHUB_ID, "Test EventHub device", InputDeviceClass::BATTERY); + + InputDevice device(mReader->getContext(), 1 /*id*/, 2 /*generation*/, {} /*identifier*/); + device.addEventHubDevice(TEST_EVENTHUB_ID, true /*populateMappers*/); + device.removeEventHubDevice(TEST_EVENTHUB_ID); + std::string dumpStr, eventHubDevStr; + device.dump(dumpStr, eventHubDevStr); +} + // --- InputMapperTest --- class InputMapperTest : public testing::Test { @@ -2938,6 +2974,8 @@ protected: mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy, *mFakeListener); mDevice = newDevice(DEVICE_ID, DEVICE_NAME, DEVICE_LOCATION, EVENTHUB_ID, classes); + // Consume the device reset notification generated when adding a new device. + mFakeListener->assertNotifyDeviceResetWasCalled(); } void SetUp() override { @@ -2962,6 +3000,8 @@ protected: mReader->loopOnce(); } mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes); + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); } std::shared_ptr<InputDevice> newDevice(int32_t deviceId, const std::string& name, @@ -2985,6 +3025,8 @@ protected: configureDevice(0); mDevice->reset(ARBITRARY_TIME); mapper.reset(ARBITRARY_TIME); + // Loop the reader to flush the input listener queue. + mReader->loopOnce(); return mapper; } @@ -3010,6 +3052,7 @@ protected: event.code = code; event.value = value; mapper.process(&event); + // Loop the reader to flush the input listener queue. mReader->loopOnce(); } @@ -4181,10 +4224,14 @@ protected: int32_t rotatedX, int32_t rotatedY); void prepareDisplay(int32_t orientation) { - const std::string uniqueId = "local:0"; - const ViewportType viewportType = ViewportType::INTERNAL; - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - orientation, uniqueId, NO_PORT, viewportType); + setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, + DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); + } + + void prepareSecondaryDisplay() { + setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, + DISPLAY_ORIENTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT, + ViewportType::EXTERNAL); } static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y, @@ -4461,6 +4508,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) { } TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotions) { + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); addConfigurationProperty("cursor.mode", "navigation"); // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not // need to be rotated. @@ -4479,11 +4527,13 @@ TEST_F(CursorInputMapperTest, Process_WhenOrientationAware_ShouldNotRotateMotion } TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) { + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); addConfigurationProperty("cursor.mode", "navigation"); // Since InputReader works in the un-rotated coordinate space, only devices that are not // orientation-aware are affected by display rotation. CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_0); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); @@ -4494,6 +4544,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_90); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); @@ -4504,6 +4555,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_180); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); @@ -4514,6 +4566,7 @@ TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotion ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); + clearViewports(); prepareDisplay(DISPLAY_ORIENTATION_270); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); @@ -4907,7 +4960,6 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_TRUE(mReader->getContext()->getGeneration() != generation); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); @@ -4969,33 +5021,118 @@ TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) { ASSERT_EQ(20, args.pointerCoords[0].getY()); } -TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { +TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { + addConfigurationProperty("cursor.mode", "pointer"); CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); - // Setup for second display. - constexpr int32_t SECOND_DISPLAY_ID = 1; - const std::string SECOND_DISPLAY_UNIQUE_ID = "local:1"; - mFakePolicy->addDisplayViewport(SECOND_DISPLAY_ID, 800, 480, DISPLAY_ORIENTATION_0, - true /*isActive*/, SECOND_DISPLAY_UNIQUE_ID, NO_PORT, - ViewportType::EXTERNAL); - mFakePolicy->setDefaultPointerDisplayId(SECOND_DISPLAY_ID); - configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); + ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); - mFakePointerController->setPosition(100, 200); - mFakePointerController->setButtonState(0); + // Ensure the display is rotated. + prepareDisplay(DISPLAY_ORIENTATION_90); NotifyMotionArgs args; + + // Verify that the coordinates are rotated. process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], - 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); + ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)); + ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + + // Enable Pointer Capture. + mFakePolicy->setPointerCapture(true); + configureDevice(InputReaderConfiguration::CHANGE_POINTER_CAPTURE); + NotifyPointerCaptureChangedArgs captureArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); + ASSERT_TRUE(captureArgs.request.enable); + + // Move and verify rotation is not applied. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); + ASSERT_EQ(10, args.pointerCoords[0].getX()); + ASSERT_EQ(20, args.pointerCoords[0].getY()); +} + +TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) { + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(DISPLAY_ORIENTATION_90); + + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + prepareSecondaryDisplay(); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + mFakePointerController->setButtonState(0); + + // Ensure input events are generated for the secondary display. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); +} + +TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) { + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(DISPLAY_ORIENTATION_90); + + // Set up the secondary display as the display on which the pointer should be shown, + // and associate the InputDevice with the secondary display. + prepareSecondaryDisplay(); + mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); + mFakePointerController->setPosition(100, 200); + mFakePointerController->setButtonState(0); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))); ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); - ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId); +} + +TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) { + CursorInputMapper& mapper = addMapperAndConfigure<CursorInputMapper>(); + + // Set up the default display as the display on which the pointer should be shown. + prepareDisplay(DISPLAY_ORIENTATION_90); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + + // Associate the InputDevice with the secondary display. + prepareSecondaryDisplay(); + mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + // The mapper should not generate any events because it is associated with a display that is + // different from the pointer display. + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } // --- TouchInputMapperTest --- @@ -6648,6 +6785,89 @@ TEST_F(SingleTouchInputMapperTest, Process_WhenAbsPressureIsPresent_HoversIfItsV toDisplayX(150), toDisplayY(250), 0, 0, 0, 0, 0, 0, 0, 0)); } +TEST_F(SingleTouchInputMapperTest, + Process_WhenViewportDisplayIdChanged_TouchIsCanceledAndDeviceIsReset) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + NotifyMotionArgs motionArgs; + + // Down. + processDown(mapper, 100, 200); + processSync(mapper); + + // We should receive a down event + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + + // Change display id + clearViewports(); + prepareSecondaryDisplay(ViewportType::INTERNAL); + + // We should receive a cancel event + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + // Then receive reset called + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); +} + +TEST_F(SingleTouchInputMapperTest, + Process_WhenViewportActiveStatusChanged_TouchIsCanceledAndDeviceIsReset) { + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + prepareButtons(); + prepareAxes(POSITION); + SingleTouchInputMapper& mapper = addMapperAndConfigure<SingleTouchInputMapper>(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + NotifyMotionArgs motionArgs; + + // Start a new gesture. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + + // Make the viewport inactive. This will put the device in disabled mode. + auto viewport = mFakePolicy->getDisplayViewportByType(ViewportType::INTERNAL); + viewport->isActive = false; + mFakePolicy->updateViewport(*viewport); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + // We should receive a cancel event for the ongoing gesture. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + // Then we should be notified that the device was reset. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + + // No events are generated while the viewport is inactive. + processMove(mapper, 101, 201); + processSync(mapper); + processDown(mapper, 102, 202); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Make the viewport active again. The device should resume processing events. + viewport->isActive = true; + mFakePolicy->updateViewport(*viewport); + configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); + + // The device is reset because it changes back to direct mode, without generating any events. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + + // Start a new gesture. + processDown(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + + // No more events. + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasNotCalled()); +} + // --- TouchDisplayProjectionTest --- class TouchDisplayProjectionTest : public SingleTouchInputMapperTest { @@ -8505,27 +8725,27 @@ TEST_F(MultiTouchInputMapperTest, Process_DeactivateViewport_AbortTouches) { ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport)); configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - // Finger move + // The ongoing touch should be canceled immediately + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + + // Finger move is ignored x += 10, y += 10; processPosition(mapper, x, y); processSync(mapper); - - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); - EXPECT_EQ(AMOTION_EVENT_ACTION_CANCEL, motionArgs.action); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); // Reactivate display viewport displayViewport.isActive = true; ASSERT_TRUE(mFakePolicy->updateViewport(displayViewport)); configureDevice(InputReaderConfiguration::CHANGE_DISPLAY_INFO); - // Finger move again + // Finger move again starts new gesture x += 10, y += 10; processPosition(mapper, x, y); processSync(mapper); - - // Gesture is aborted, so events after display is activated won't be dispatched until there is - // no pointer on the touch device. - mFakeListener->assertNotifyMotionWasNotCalled(); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + EXPECT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); } TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { @@ -8683,6 +8903,10 @@ TEST_F(MultiTouchInputMapperTest, VideoFrames_WhenNotOrientationAware_AreRotated // window's coordinate space. frames[0].rotate(getInverseRotation(orientation)); ASSERT_EQ(frames, motionArgs.videoFrames); + + // Release finger. + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); } } diff --git a/services/inputflinger/tests/TestInputListener.cpp b/services/inputflinger/tests/TestInputListener.cpp index 6a26c636d9..57b382c718 100644 --- a/services/inputflinger/tests/TestInputListener.cpp +++ b/services/inputflinger/tests/TestInputListener.cpp @@ -69,6 +69,13 @@ void TestInputListener::assertNotifyMotionWasCalled(NotifyMotionArgs* outEventAr "Expected notifyMotion() to have been called.")); } +void TestInputListener::assertNotifyMotionWasCalled( + const ::testing::Matcher<NotifyMotionArgs>& matcher) { + NotifyMotionArgs outEventArgs; + ASSERT_NO_FATAL_FAILURE(assertNotifyMotionWasCalled(&outEventArgs)); + ASSERT_THAT(outEventArgs, matcher); +} + void TestInputListener::assertNotifyMotionWasNotCalled() { ASSERT_NO_FATAL_FAILURE( assertNotCalled<NotifyMotionArgs>("notifyMotion() should not be called.")); diff --git a/services/inputflinger/tests/TestInputListener.h b/services/inputflinger/tests/TestInputListener.h index 626cdfc8c8..0bdfc6b9fb 100644 --- a/services/inputflinger/tests/TestInputListener.h +++ b/services/inputflinger/tests/TestInputListener.h @@ -18,6 +18,7 @@ #define _UI_TEST_INPUT_LISTENER_H #include <android-base/thread_annotations.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include "InputListener.h" @@ -48,6 +49,8 @@ public: void assertNotifyMotionWasCalled(NotifyMotionArgs* outEventArgs = nullptr); + void assertNotifyMotionWasCalled(const ::testing::Matcher<NotifyMotionArgs>& matcher); + void assertNotifyMotionWasNotCalled(); void assertNotifySwitchWasCalled(NotifySwitchArgs* outEventArgs = nullptr); diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index 0062f426d7..29fa001185 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -16,13 +16,17 @@ #include "../UnwantedInteractionBlocker.h" #include <android-base/silent_death_test.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/constants.h> #include <linux/input.h> #include <thread> +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" #include "TestInputListener.h" +using ::testing::AllOf; + namespace android { constexpr int32_t DEVICE_ID = 3; @@ -30,6 +34,8 @@ constexpr int32_t X_RESOLUTION = 11; constexpr int32_t Y_RESOLUTION = 11; constexpr int32_t MAJOR_RESOLUTION = 1; +const nsecs_t RESAMPLE_PERIOD = ::ui::kResamplePeriod.InNanoseconds(); + constexpr int POINTER_0_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); constexpr int POINTER_1_DOWN = @@ -47,6 +53,27 @@ constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; constexpr int UP = AMOTION_EVENT_ACTION_UP; constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; +constexpr int32_t FLAG_CANCELED = AMOTION_EVENT_FLAG_CANCELED; + +MATCHER_P(WithAction, action, "MotionEvent with specified action") { + bool result = true; + if (action == CANCEL) { + result &= (arg.flags & FLAG_CANCELED) != 0; + } + result &= arg.action == action; + *result_listener << "expected to receive " << MotionEvent::actionToString(action) + << " but received " << MotionEvent::actionToString(arg.action) << " instead."; + return result; +} + +MATCHER_P(WithFlags, flags, "MotionEvent with specified flags") { + return arg.flags == flags; +} + +static nsecs_t toNs(std::chrono::nanoseconds duration) { + return duration.count(); +} + struct PointerData { float x; float y; @@ -252,7 +279,7 @@ TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) { /*newSuppressedPointerIds*/ {1}); ASSERT_EQ(2u, result.size()); assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); } @@ -267,7 +294,7 @@ TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) { /*newSuppressedPointerIds*/ {0}); ASSERT_EQ(1u, result.size()); assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); } /** @@ -282,7 +309,7 @@ TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) { /*newSuppressedPointerIds*/ {1}); ASSERT_EQ(1u, result.size()); assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); } /** @@ -297,7 +324,7 @@ TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) { /*newSuppressedPointerIds*/ {0}); ASSERT_EQ(1u, result.size()); assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); } /** @@ -312,7 +339,7 @@ TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) { /*newSuppressedPointerIds*/ {0, 1}); ASSERT_EQ(1u, result.size()); assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); } /** @@ -328,7 +355,7 @@ TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) { /*newSuppressedPointerIds*/ {0, 1}); ASSERT_EQ(1u, result.size()); assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + ASSERT_EQ(FLAG_CANCELED, result[0].flags); } /** @@ -384,7 +411,7 @@ TEST(GetTouchesTest, ConvertDownEvent) { expected.reported_tool_type = ::ui::EventPointerType::kTouch; expected.stylus_button = false; - ASSERT_EQ(expected, touches[0]) << toString(touches[0]); + ASSERT_EQ(expected, touches[0]) << touches[0]; } // --- UnwantedInteractionBlockerTest --- @@ -569,6 +596,114 @@ TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) { dumpThread.join(); } +/** + * Heuristic filter that's present in the palm rejection model blocks touches early if the size + * of the touch is large. This is an integration test that checks that this filter kicks in. + */ +TEST_F(UnwantedInteractionBlockerTest, HeuristicFilterWorks) { + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + // Small touch down + NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&args1); + mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + + // Large touch oval on the next move + NotifyMotionArgs args2 = + generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}); + mBlocker->notifyMotion(&args2); + mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + + // Lift up the touch to force the model to decide on whether it's a palm + NotifyMotionArgs args3 = + generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}); + mBlocker->notifyMotion(&args3); + mTestListener.assertNotifyMotionWasCalled(WithAction(CANCEL)); +} + +/** + * Send a stylus event that would have triggered the heuristic palm detector if it were a touch + * event. However, since it's a stylus event, it should propagate without being canceled through + * the blocker. + * This is similar to `HeuristicFilterWorks` test, but for stylus tool. + */ +TEST_F(UnwantedInteractionBlockerTest, StylusIsNotBlocked) { + InputDeviceInfo info = generateTestDeviceInfo(); + info.addSource(AINPUT_SOURCE_STYLUS); + mBlocker->notifyInputDevicesChanged({info}); + NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + args1.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args1); + mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + + // Move the stylus, setting large TOUCH_MAJOR/TOUCH_MINOR dimensions + NotifyMotionArgs args2 = + generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, MOVE, {{4, 5, 200}}); + args2.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args2); + mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + + // Lift up the stylus. If it were a touch event, this would force the model to decide on whether + // it's a palm. + NotifyMotionArgs args3 = + generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}); + args3.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args3); + mTestListener.assertNotifyMotionWasCalled(WithAction(UP)); +} + +/** + * Send a mixed touch and stylus event. + * The touch event goes first, and is a palm. The stylus event goes down after. + * Stylus event should continue to work even after touch is detected as a palm. + */ +TEST_F(UnwantedInteractionBlockerTest, TouchIsBlockedWhenMixedWithStylus) { + InputDeviceInfo info = generateTestDeviceInfo(); + info.addSource(AINPUT_SOURCE_STYLUS); + mBlocker->notifyInputDevicesChanged({info}); + + // Touch down + NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&args1); + mTestListener.assertNotifyMotionWasCalled(WithAction(DOWN)); + + // Stylus pointer down + NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, RESAMPLE_PERIOD, POINTER_1_DOWN, + {{1, 2, 3}, {10, 20, 30}}); + args2.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args2); + mTestListener.assertNotifyMotionWasCalled(WithAction(POINTER_1_DOWN)); + + // Large touch oval on the next finger move + NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 * RESAMPLE_PERIOD, MOVE, + {{1, 2, 300}, {11, 21, 30}}); + args3.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args3); + mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + + // Lift up the finger pointer. It should be canceled due to the heuristic filter. + NotifyMotionArgs args4 = generateMotionArgs(0 /*downTime*/, 3 * RESAMPLE_PERIOD, POINTER_0_UP, + {{1, 2, 300}, {11, 21, 30}}); + args4.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args4); + mTestListener.assertNotifyMotionWasCalled( + AllOf(WithAction(POINTER_0_UP), WithFlags(FLAG_CANCELED))); + + NotifyMotionArgs args5 = + generateMotionArgs(0 /*downTime*/, 4 * RESAMPLE_PERIOD, MOVE, {{12, 22, 30}}); + args5.pointerProperties[0].id = args4.pointerProperties[1].id; + args5.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args5); + mTestListener.assertNotifyMotionWasCalled(WithAction(MOVE)); + + // Lift up the stylus pointer + NotifyMotionArgs args6 = + generateMotionArgs(0 /*downTime*/, 5 * RESAMPLE_PERIOD, UP, {{4, 5, 200}}); + args6.pointerProperties[0].id = args4.pointerProperties[1].id; + args6.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + mBlocker->notifyMotion(&args6); + mTestListener.assertNotifyMotionWasCalled(WithAction(UP)); +} + using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; /** @@ -630,138 +765,138 @@ TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) { */ TEST_F(PalmRejectorTest, TwoPointersAreCanceled) { std::vector<NotifyMotionArgs> argsList; - constexpr nsecs_t downTime = 255955749837000; + const nsecs_t downTime = toNs(0ms); mPalmRejector->processMotion( generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}})); + generateMotionArgs(downTime, toNs(8ms), MOVE, {{1406.0, 650.0, 52.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}})); + generateMotionArgs(downTime, toNs(16ms), MOVE, {{1429.0, 672.0, 46.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}})); + generateMotionArgs(downTime, toNs(24ms), MOVE, {{1417.0, 685.0, 41.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN, + generateMotionArgs(downTime, toNs(32ms), POINTER_1_DOWN, {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955783039000, MOVE, + generateMotionArgs(downTime, toNs(40ms), MOVE, {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955792536000, MOVE, + generateMotionArgs(downTime, toNs(48ms), MOVE, {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955799474000, MOVE, + generateMotionArgs(downTime, toNs(56ms), MOVE, {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955809177000, MOVE, + generateMotionArgs(downTime, toNs(64ms), MOVE, {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955816131000, MOVE, + generateMotionArgs(downTime, toNs(72ms), MOVE, {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}})); argsList = mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955825907000, MOVE, + generateMotionArgs(downTime, toNs(80ms), MOVE, {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}})); ASSERT_EQ(1u, argsList.size()); ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags); argsList = mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955832736000, MOVE, + generateMotionArgs(downTime, toNs(88ms), MOVE, {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}})); ASSERT_EQ(2u, argsList.size()); ASSERT_EQ(POINTER_0_UP, argsList[0].action); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(FLAG_CANCELED, argsList[0].flags); ASSERT_EQ(MOVE, argsList[1].action); ASSERT_EQ(1u, argsList[1].pointerCount); ASSERT_EQ(0, argsList[1].flags); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955842432000, MOVE, + generateMotionArgs(downTime, toNs(96ms), MOVE, {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955849380000, MOVE, + generateMotionArgs(downTime, toNs(104ms), MOVE, {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955859046000, MOVE, + generateMotionArgs(downTime, toNs(112ms), MOVE, {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}})); argsList = mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955869823000, MOVE, + generateMotionArgs(downTime, toNs(120ms), MOVE, {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}})); ASSERT_EQ(1u, argsList.size()); ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955875641000, MOVE, + generateMotionArgs(downTime, toNs(128ms), MOVE, {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955882693000, MOVE, + generateMotionArgs(downTime, toNs(136ms), MOVE, {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955892324000, MOVE, + generateMotionArgs(downTime, toNs(144ms), MOVE, {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955899425000, MOVE, + generateMotionArgs(downTime, toNs(152ms), MOVE, {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955909400000, MOVE, + generateMotionArgs(downTime, toNs(160ms), MOVE, {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955915885000, MOVE, + generateMotionArgs(downTime, toNs(168ms), MOVE, {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955925607000, MOVE, + generateMotionArgs(downTime, toNs(176ms), MOVE, {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955932580000, MOVE, + generateMotionArgs(downTime, toNs(184ms), MOVE, {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955942231000, MOVE, + generateMotionArgs(downTime, toNs(192ms), MOVE, {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955949204000, MOVE, + generateMotionArgs(downTime, toNs(200ms), MOVE, {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955959103000, MOVE, + generateMotionArgs(downTime, toNs(208ms), MOVE, {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955965884000, MOVE, + generateMotionArgs(downTime, toNs(216ms), MOVE, {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955975649000, MOVE, + generateMotionArgs(downTime, toNs(224ms), MOVE, {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955982537000, MOVE, + generateMotionArgs(downTime, toNs(232ms), MOVE, {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955992284000, MOVE, + generateMotionArgs(downTime, toNs(240ms), MOVE, {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}})); argsList = mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955999348000, POINTER_1_UP, + generateMotionArgs(downTime, toNs(248ms), POINTER_1_UP, {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}})); ASSERT_TRUE(argsList.empty()); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}})); + generateMotionArgs(downTime, toNs(256ms), MOVE, {{1362.0, 716.0, 55.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}})); + generateMotionArgs(downTime, toNs(264ms), MOVE, {{1347.0, 707.0, 54.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}})); + generateMotionArgs(downTime, toNs(272ms), MOVE, {{1340.0, 698.0, 54.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}})); + generateMotionArgs(downTime, toNs(280ms), MOVE, {{1338.0, 694.0, 55.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}})); + generateMotionArgs(downTime, toNs(288ms), MOVE, {{1336.0, 690.0, 53.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}})); + generateMotionArgs(downTime, toNs(296ms), MOVE, {{1334.0, 685.0, 47.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}})); + generateMotionArgs(downTime, toNs(304ms), MOVE, {{1333.0, 679.0, 46.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}})); + generateMotionArgs(downTime, toNs(312ms), MOVE, {{1332.0, 672.0, 45.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}})); + generateMotionArgs(downTime, toNs(320ms), MOVE, {{1333.0, 666.0, 40.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}})); + generateMotionArgs(downTime, toNs(328ms), MOVE, {{1336.0, 661.0, 24.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}})); + generateMotionArgs(downTime, toNs(336ms), MOVE, {{1338.0, 656.0, 16.0}})); mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}})); + generateMotionArgs(downTime, toNs(344ms), MOVE, {{1341.0, 649.0, 1.0}})); argsList = mPalmRejector->processMotion( - generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}})); + generateMotionArgs(downTime, toNs(352ms), UP, {{1341.0, 649.0, 1.0}})); ASSERT_TRUE(argsList.empty()); } @@ -845,7 +980,7 @@ TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) { ASSERT_EQ(2u, argsList.size()); // First event - cancel pointer 1 ASSERT_EQ(POINTER_1_UP, argsList[0].action); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(FLAG_CANCELED, argsList[0].flags); // Second event - send MOVE for the remaining pointer ASSERT_EQ(MOVE, argsList[1].action); ASSERT_EQ(0, argsList[1].flags); @@ -886,7 +1021,7 @@ TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) { // Cancel all ASSERT_EQ(CANCEL, argsList[0].action); ASSERT_EQ(2u, argsList[0].pointerCount); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(FLAG_CANCELED, argsList[0].flags); // Future move events are ignored argsList = mPalmRejector->processMotion( @@ -932,7 +1067,7 @@ TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) { {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); ASSERT_EQ(1u, argsList.size()); ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(FLAG_CANCELED, argsList[0].flags); // Future move events should not go to the listener. argsList = mPalmRejector->processMotion( @@ -966,7 +1101,7 @@ TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) { {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}})); ASSERT_EQ(2u, argsList.size()); ASSERT_EQ(POINTER_1_UP, argsList[0].action); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(FLAG_CANCELED, argsList[0].flags); ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action); ASSERT_EQ(0, argsList[1].flags); diff --git a/services/sensorservice/AidlSensorHalWrapper.cpp b/services/sensorservice/AidlSensorHalWrapper.cpp index 4d1de96caa..f67c610550 100644 --- a/services/sensorservice/AidlSensorHalWrapper.cpp +++ b/services/sensorservice/AidlSensorHalWrapper.cpp @@ -726,7 +726,7 @@ status_t AidlSensorHalWrapper::registerDirectChannel(const sensors_direct_mem_t .type = type, .format = format, .size = static_cast<int32_t>(memory->size), - .memoryHandle = makeToAidl(memory->handle), + .memoryHandle = dupToAidl(memory->handle), }; return convertToStatus(mSensors->registerDirectChannel(mem, channelHandle)); diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp index 948692bd47..e0a4f034cb 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -1328,6 +1328,7 @@ Vector<Sensor> SensorService::getSensorList(const String16& opPackageName) { mSensors.getUserDebugSensors() : mSensors.getUserSensors(); Vector<Sensor> accessibleSensorList; + resetTargetSdkVersionCache(opPackageName); bool isCapped = isRateCappedBasedOnPermission(opPackageName); for (size_t i = 0; i < initialSensorList.size(); i++) { Sensor sensor = initialSensorList[i]; @@ -1367,6 +1368,7 @@ sp<ISensorEventConnection> SensorService::createSensorEventConnection(const Stri if (requestedMode != NORMAL && requestedMode != DATA_INJECTION) { return nullptr; } + resetTargetSdkVersionCache(opPackageName); Mutex::Autolock _l(mLock); // To create a client in DATA_INJECTION mode to inject data, SensorService should already be @@ -1402,6 +1404,7 @@ int SensorService::isDataInjectionEnabled() { sp<ISensorEventConnection> SensorService::createSensorDirectConnection( const String16& opPackageName, uint32_t size, int32_t type, int32_t format, const native_handle *resource) { + resetTargetSdkVersionCache(opPackageName); ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); // No new direct connections are allowed when sensor privacy is enabled @@ -1643,14 +1646,6 @@ void SensorService::cleanupConnection(SensorEventConnection* c) { checkWakeLockStateLocked(&connLock); } - { - Mutex::Autolock packageLock(sPackageTargetVersionLock); - auto iter = sPackageTargetVersion.find(c->mOpPackageName); - if (iter != sPackageTargetVersion.end()) { - sPackageTargetVersion.erase(iter); - } - } - SensorDevice& dev(SensorDevice::getInstance()); dev.notifyConnectionDestroyed(c); } @@ -2091,6 +2086,14 @@ int SensorService::getTargetSdkVersion(const String16& opPackageName) { return targetSdkVersion; } +void SensorService::resetTargetSdkVersionCache(const String16& opPackageName) { + Mutex::Autolock packageLock(sPackageTargetVersionLock); + auto iter = sPackageTargetVersion.find(opPackageName); + if (iter != sPackageTargetVersion.end()) { + sPackageTargetVersion.erase(iter); + } +} + void SensorService::checkWakeLockState() { ConnectionSafeAutolock connLock = mConnectionHolder.lock(mLock); checkWakeLockStateLocked(&connLock); diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h index 234dc9cd7d..4ba3c51985 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -377,6 +377,7 @@ private: const String16& opPackageName); static bool hasPermissionForSensor(const Sensor& sensor); static int getTargetSdkVersion(const String16& opPackageName); + static void resetTargetSdkVersionCache(const String16& opPackageName); // SensorService acquires a partial wakelock for delivering events from wake up sensors. This // method checks whether all the events from these wake up sensors have been delivered to the // corresponding applications, if yes the wakelock is released. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h index 16cb41b024..5e84be1841 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Display.h @@ -56,6 +56,9 @@ public: // similar requests if needed. virtual void createClientCompositionCache(uint32_t cacheSize) = 0; + // Sends the brightness setting to HWC + virtual void applyDisplayBrightness(const bool applyImmediately) = 0; + protected: ~Display() = default; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index db2fd1b500..2203639b1a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -27,6 +27,7 @@ #include <compositionengine/LayerFE.h> #include <renderengine/LayerSettings.h> #include <ui/Fence.h> +#include <ui/FenceTime.h> #include <ui/GraphicTypes.h> #include <ui/LayerStack.h> #include <ui/Region.h> @@ -311,6 +312,8 @@ protected: const Region& flashRegion, std::vector<LayerFE::LayerSettings>& clientCompositionLayers) = 0; virtual void setExpensiveRenderingExpected(bool enabled) = 0; + virtual void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) = 0; + virtual bool isPowerHintSessionEnabled() = 0; virtual void cacheClientCompositionRequests(uint32_t cacheSize) = 0; virtual bool canPredictCompositionStrategy(const CompositionRefreshArgs&) = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h index 61a0e6a356..33a10a36a7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Display.h @@ -72,6 +72,7 @@ public: const compositionengine::DisplayColorProfileCreationArgs&) override; void createRenderSurface(const compositionengine::RenderSurfaceCreationArgs&) override; void createClientCompositionCache(uint32_t cacheSize) override; + void applyDisplayBrightness(const bool applyImmediately) override; // Internal helpers used by chooseCompositionStrategy() using ChangedTypes = android::HWComposer::DeviceRequestedChanges::ChangedTypes; @@ -89,6 +90,8 @@ public: std::unique_ptr<compositionengine::OutputLayer> createOutputLayer(const sp<LayerFE>&) const; private: + bool isPowerHintSessionEnabled() override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; DisplayId mId; bool mIsDisconnected = false; Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 31c51e6a8d..df721cdc89 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -140,6 +140,8 @@ protected: std::vector<LayerFE*> &outLayerFEs) override; void appendRegionFlashRequests(const Region&, std::vector<LayerFE::LayerSettings>&) override; void setExpensiveRenderingExpected(bool enabled) override; + void setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) override; + bool isPowerHintSessionEnabled() override; void dumpBase(std::string&) const; // Implemented by the final implementation for the final state it uses. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h index e65aa7393c..8e4e9af48c 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/CachedSet.h @@ -115,7 +115,7 @@ public: // Renders the cached set with the supplied output composition state. void render(renderengine::RenderEngine& re, TexturePool& texturePool, - const OutputCompositionState& outputState); + const OutputCompositionState& outputState, bool deviceHandlesColorTransform); void dump(std::string& result) const; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h index 92cc484211..f934cb20a0 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Flattener.h @@ -106,7 +106,8 @@ public: // Renders the newest cached sets with the supplied output composition state void renderCachedSets(const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline); + std::optional<std::chrono::steady_clock::time_point> renderDeadline, + bool deviceHandlesColorTransform); void setTexturePoolEnabled(bool enabled) { mTexturePool.setEnabled(enabled); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h index b7ebca60fd..c968df708f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/planner/Planner.h @@ -65,7 +65,8 @@ public: // Rendering a pending cached set is optional: if the renderDeadline is not far enough in the // future then the planner may opt to skip rendering the cached set. void renderCachedSets(const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline); + std::optional<std::chrono::steady_clock::time_point> renderDeadline, + bool deviceHandlesColorTransform); void setTexturePoolEnabled(bool enabled) { mFlattener.setTexturePoolEnabled(enabled); } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h index 72e6f3bdbb..7e99ec2f5a 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Display.h @@ -41,6 +41,7 @@ public: MOCK_METHOD1(createDisplayColorProfile, void(const DisplayColorProfileCreationArgs&)); MOCK_METHOD1(createRenderSurface, void(const RenderSurfaceCreationArgs&)); MOCK_METHOD1(createClientCompositionCache, void(uint32_t)); + MOCK_METHOD1(applyDisplayBrightness, void(const bool)); MOCK_METHOD1(setPredictCompositionStrategy, void(bool)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index cb9fbad8dd..2a04949cff 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -133,6 +133,8 @@ public: MOCK_METHOD1(canPredictCompositionStrategy, bool(const CompositionRefreshArgs&)); MOCK_METHOD1(setPredictCompositionStrategy, void(bool)); MOCK_METHOD1(setTreat170mAsSrgb, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, ()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/src/Display.cpp b/services/surfaceflinger/CompositionEngine/src/Display.cpp index b79b46b6ee..1ec6449ed0 100644 --- a/services/surfaceflinger/CompositionEngine/src/Display.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Display.cpp @@ -203,23 +203,16 @@ void Display::setReleasedLayers(const compositionengine::CompositionRefreshArgs& setReleasedLayers(std::move(releasedLayers)); } -void Display::beginFrame() { - Output::beginFrame(); - - // If we don't have a HWC display, then we are done. - const auto halDisplayId = HalDisplayId::tryCast(mId); - if (!halDisplayId) { - return; - } - +void Display::applyDisplayBrightness(const bool applyImmediately) { auto& hwc = getCompositionEngine().getHwComposer(); + const auto halDisplayId = HalDisplayId::tryCast(*getDisplayId()); if (const auto physicalDisplayId = PhysicalDisplayId::tryCast(*halDisplayId); physicalDisplayId && getState().displayBrightness) { const status_t result = hwc.setDisplayBrightness(*physicalDisplayId, *getState().displayBrightness, getState().displayBrightnessNits, Hwc2::Composer::DisplayBrightnessOptions{ - .applyImmediately = false}) + .applyImmediately = applyImmediately}) .get(); ALOGE_IF(result != NO_ERROR, "setDisplayBrightness failed for %s: %d, (%s)", getName().c_str(), result, strerror(-result)); @@ -228,6 +221,18 @@ void Display::beginFrame() { editState().displayBrightness.reset(); } +void Display::beginFrame() { + Output::beginFrame(); + + // If we don't have a HWC display, then we are done. + const auto halDisplayId = HalDisplayId::tryCast(mId); + if (!halDisplayId) { + return; + } + + applyDisplayBrightness(false); +} + bool Display::chooseCompositionStrategy( std::optional<android::HWComposer::DeviceRequestedChanges>* outChanges) { ATRACE_CALL(); @@ -243,11 +248,14 @@ bool Display::chooseCompositionStrategy( return false; } + const nsecs_t startTime = systemTime(); + // Get any composition changes requested by the HWC device, and apply them. std::optional<android::HWComposer::DeviceRequestedChanges> changes; auto& hwc = getCompositionEngine().getHwComposer(); + const bool requiresClientComposition = anyLayersRequireClientComposition(); if (status_t result = - hwc.getDeviceCompositionChanges(*halDisplayId, anyLayersRequireClientComposition(), + hwc.getDeviceCompositionChanges(*halDisplayId, requiresClientComposition, getState().earliestPresentTime, getState().previousPresentFence, getState().expectedPresentTime, outChanges); @@ -257,6 +265,11 @@ bool Display::chooseCompositionStrategy( return false; } + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setHwcValidateTiming(mId, startTime, systemTime()); + mPowerAdvisor->setRequiresClientComposition(mId, requiresClientComposition); + } + return true; } @@ -356,9 +369,24 @@ compositionengine::Output::FrameFences Display::presentAndGetFrameFences() { } auto& hwc = getCompositionEngine().getHwComposer(); + + const nsecs_t startTime = systemTime(); + + if (isPowerHintSessionEnabled()) { + if (!getCompositionEngine().getHwComposer().getComposer()->isSupported( + Hwc2::Composer::OptionalFeature::ExpectedPresentTime) && + getState().previousPresentFence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) { + mPowerAdvisor->setHwcPresentDelayedTime(mId, getState().earliestPresentTime); + } + } + hwc.presentAndGetReleaseFences(*halDisplayIdOpt, getState().earliestPresentTime, getState().previousPresentFence); + if (isPowerHintSessionEnabled()) { + mPowerAdvisor->setHwcPresentTiming(mId, startTime, systemTime()); + } + fences.presentFence = hwc.getPresentFence(*halDisplayIdOpt); // TODO(b/121291683): Change HWComposer call to return entire map @@ -384,6 +412,14 @@ void Display::setExpensiveRenderingExpected(bool enabled) { } } +bool Display::isPowerHintSessionEnabled() { + return mPowerAdvisor != nullptr && mPowerAdvisor->usePowerHintSession(); +} + +void Display::setHintSessionGpuFence(std::unique_ptr<FenceTime>&& gpuFence) { + mPowerAdvisor->setGpuFenceTime(mId, std::move(gpuFence)); +} + void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refreshArgs, GpuCompositionResult&& result) { // We only need to actually compose the display if: @@ -396,6 +432,13 @@ void Display::finishFrame(const compositionengine::CompositionRefreshArgs& refre } impl::Output::finishFrame(refreshArgs, std::move(result)); + + if (isPowerHintSessionEnabled()) { + auto& hwc = getCompositionEngine().getHwComposer(); + if (auto halDisplayId = HalDisplayId::tryCast(mId)) { + mPowerAdvisor->setSkippedValidate(mId, hwc.getValidateSkipped(*halDisplayId)); + } + } } } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index c3385a8a8b..b724daa8ce 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -586,8 +586,29 @@ void Output::ensureOutputLayerIfVisible(sp<compositionengine::LayerFE>& layerFE, // Remove the transparent area from the visible region if (!layerFEState->isOpaque) { if (tr.preserveRects()) { - // transform the transparent region - transparentRegion = tr.transform(layerFEState->transparentRegionHint); + // Clip the transparent region to geomLayerBounds first + // The transparent region may be influenced by applications, for + // instance, by overriding ViewGroup#gatherTransparentRegion with a + // custom view. Once the layer stack -> display mapping is known, we + // must guard against very wrong inputs to prevent underflow or + // overflow errors. We do this here by constraining the transparent + // region to be within the pre-transform layer bounds, since the + // layer bounds are expected to play nicely with the full + // transform. + const Region clippedTransparentRegionHint = + layerFEState->transparentRegionHint.intersect( + Rect(layerFEState->geomLayerBounds)); + + if (clippedTransparentRegionHint.isEmpty()) { + if (!layerFEState->transparentRegionHint.isEmpty()) { + ALOGD("Layer: %s had an out of bounds transparent region", + layerFE->getDebugName()); + layerFEState->transparentRegionHint.dump("transparentRegionHint"); + } + transparentRegion.clear(); + } else { + transparentRegion = tr.transform(clippedTransparentRegionHint); + } } else { // transformation too complex, can't do the // transparent region optimization. @@ -1099,6 +1120,10 @@ void Output::finishFrame(const CompositionRefreshArgs& refreshArgs, GpuCompositi return; } + if (isPowerHintSessionEnabled()) { + // get fence end time to know when gpu is complete in display + setHintSessionGpuFence(std::make_unique<FenceTime>(new Fence(dup(optReadyFence->get())))); + } // swap buffers (presentation) mRenderSurface->queueBuffer(std::move(*optReadyFence)); } @@ -1218,7 +1243,8 @@ std::optional<base::unique_fd> Output::composeSurfaces( ATRACE_NAME("ClientCompositionCacheHit"); outputCompositionState.reusedClientComposition = true; setExpensiveRenderingExpected(false); - return base::unique_fd(); + // b/239944175 pass the fence associated with the buffer. + return base::unique_fd(std::move(fd)); } ATRACE_NAME("ClientCompositionCacheMiss"); mClientCompositionRequestCache->add(tex->getBuffer()->getId(), clientCompositionDisplay, @@ -1403,6 +1429,14 @@ void Output::setExpensiveRenderingExpected(bool) { // The base class does nothing with this call. } +void Output::setHintSessionGpuFence(std::unique_ptr<FenceTime>&&) { + // The base class does nothing with this call. +} + +bool Output::isPowerHintSessionEnabled() { + return false; +} + void Output::postFramebuffer() { ATRACE_CALL(); ALOGV(__FUNCTION__); @@ -1460,7 +1494,8 @@ void Output::postFramebuffer() { void Output::renderCachedSets(const CompositionRefreshArgs& refreshArgs) { if (mPlanner) { - mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime); + mPlanner->renderCachedSets(getState(), refreshArgs.scheduledFrameTime, + getState().usesDeviceComposition || getSkipColorTransform()); } } diff --git a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp index 641b806aec..d6f02ee42a 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/CachedSet.cpp @@ -159,7 +159,8 @@ void CachedSet::updateAge(std::chrono::steady_clock::time_point now) { } void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& texturePool, - const OutputCompositionState& outputState) { + const OutputCompositionState& outputState, + bool deviceHandlesColorTransform) { ATRACE_CALL(); const Rect& viewport = outputState.layerStackSpace.getContent(); const ui::Dataspace& outputDataspace = outputState.dataspace; @@ -170,6 +171,8 @@ void CachedSet::render(renderengine::RenderEngine& renderEngine, TexturePool& te .physicalDisplay = outputState.framebufferSpace.getContent(), .clip = viewport, .outputDataspace = outputDataspace, + .colorTransform = outputState.colorTransformMatrix, + .deviceHandlesColorTransform = deviceHandlesColorTransform, .orientation = orientation, .targetLuminanceNits = outputState.displayBrightnessNits, }; diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp index 1062b700dd..9175dd01a1 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Flattener.cpp @@ -99,7 +99,8 @@ NonBufferHash Flattener::flattenLayers(const std::vector<const LayerState*>& lay void Flattener::renderCachedSets( const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline) { + std::optional<std::chrono::steady_clock::time_point> renderDeadline, + bool deviceHandlesColorTransform) { ATRACE_CALL(); if (!mNewCachedSet) { @@ -136,7 +137,7 @@ void Flattener::renderCachedSets( } } - mNewCachedSet->render(mRenderEngine, mTexturePool, outputState); + mNewCachedSet->render(mRenderEngine, mTexturePool, outputState, deviceHandlesColorTransform); } void Flattener::dumpLayers(std::string& result) const { diff --git a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp index c8413eb8bc..54133d92b0 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/Planner.cpp @@ -201,11 +201,11 @@ void Planner::reportFinalPlan( finalPlan); } -void Planner::renderCachedSets( - const OutputCompositionState& outputState, - std::optional<std::chrono::steady_clock::time_point> renderDeadline) { +void Planner::renderCachedSets(const OutputCompositionState& outputState, + std::optional<std::chrono::steady_clock::time_point> renderDeadline, + bool deviceHandlesColorTransform) { ATRACE_CALL(); - mFlattener.renderCachedSets(outputState, renderDeadline); + mFlattener.renderCachedSets(outputState, renderDeadline, deviceHandlesColorTransform); } void Planner::dump(const Vector<String16>& args, std::string& result) { diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp index 0e5a7b6a99..344fea3331 100644 --- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp @@ -169,6 +169,7 @@ struct DisplayTestCommon : public testing::Test { EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine)); EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false)); EXPECT_CALL(mRenderEngine, isProtected()).WillRepeatedly(Return(false)); + EXPECT_CALL(mPowerAdvisor, usePowerHintSession()).WillRepeatedly(Return(false)); } DisplayCreationArgs getDisplayCreationArgsForPhysicalDisplay() { diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 9b12b08063..d7704a893d 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -137,6 +137,7 @@ public: MOCK_METHOD(bool, hasDisplayIdleTimerCapability, (PhysicalDisplayId), (const, override)); MOCK_METHOD(Hwc2::AidlTransform, getPhysicalDisplayOrientation, (PhysicalDisplayId), (const, override)); + MOCK_METHOD(bool, getValidateSkipped, (HalDisplayId), (const, override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h index 50adcfb827..c8bd5e436c 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockPowerAdvisor.h @@ -38,11 +38,33 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setHwcValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace mock diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 063726b5e2..cf12890310 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -1505,6 +1505,8 @@ struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test { static const Region kTransparentRegionHint; static const Region kTransparentRegionHintTwo; static const Region kTransparentRegionHintTwo90Rotation; + static const Region kTransparentRegionHintNegative; + static const Region kTransparentRegionHintNegativeIntersectsBounds; StrictMock<OutputPartialMock> mOutput; LayerFESet mGeomSnapshots; @@ -1528,6 +1530,10 @@ const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo = Region(Rect(25, 20, 50, 75)); const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintTwo90Rotation = Region(Rect(125, 25, 180, 50)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegative = + Region(Rect(INT32_MIN, INT32_MIN, INT32_MIN + 100, INT32_MIN + 200)); +const Region OutputEnsureOutputLayerIfVisibleTest::kTransparentRegionHintNegativeIntersectsBounds = + Region(Rect(INT32_MIN, INT32_MIN, 100, 100)); TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerIncluded) { EXPECT_CALL(mOutput, includesLayer(sp<LayerFE>(mLayer.layerFE))).WillOnce(Return(false)); @@ -1997,6 +2003,41 @@ TEST_F(OutputEnsureOutputLayerIfVisibleTest, blockingRegionIsInOutputSpace) { RegionEq(kTransparentRegionHintTwo90Rotation)); } +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionExcludesOutputLayer) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kFullBoundsNoRotation; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionIgnoredWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.geomLayerBounds = kFullBoundsNoRotation.bounds().toFloatRect(); + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegative; + + EXPECT_CALL(mOutput, ensureOutputLayer(_, _)).Times(0); +} + +TEST_F(OutputEnsureOutputLayerIfVisibleTest, transparentRegionClipsWhenOutsideBounds) { + mLayer.layerFEState.isOpaque = false; + mLayer.layerFEState.contentDirty = true; + mLayer.layerFEState.compositionType = + aidl::android::hardware::graphics::composer3::Composition::DISPLAY_DECORATION; + mLayer.layerFEState.transparentRegionHint = kTransparentRegionHintNegativeIntersectsBounds; + + EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u)); + EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer.layerFE))) + .WillOnce(Return(&mLayer.outputLayer)); + ensureOutputLayerIfVisible(); + + // Check that the blocking region clips an out-of-bounds transparent region. + EXPECT_THAT(mLayer.outputLayerState.outputSpaceBlockingRegionHint, + RegionEq(kTransparentRegionHint)); +} + /* * Output::present() */ @@ -3319,6 +3360,9 @@ struct OutputComposeSurfacesTest : public testing::Test { MOCK_METHOD2(appendRegionFlashRequests, void(const Region&, std::vector<LayerFE::LayerSettings>&)); MOCK_METHOD1(setExpensiveRenderingExpected, void(bool)); + MOCK_METHOD(void, setHintSessionGpuFence, (std::unique_ptr<FenceTime> && gpuFence), + (override)); + MOCK_METHOD(bool, isPowerHintSessionEnabled, (), (override)); }; OutputComposeSurfacesTest() { diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp index 8a99e4e2e8..0e9db369e8 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/CachedSetTest.cpp @@ -376,7 +376,7 @@ TEST_F(CachedSetTest, renderUnsecureOutput) { .WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = false; - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); @@ -429,7 +429,7 @@ TEST_F(CachedSetTest, renderSecureOutput) { .WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); @@ -477,7 +477,58 @@ TEST_F(CachedSetTest, renderWhitePoint) { .WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); mOutputState.isSecure = true; - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); + expectReadyBuffer(cachedSet); + + EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); + EXPECT_EQ(Rect(kOutputSize.width, kOutputSize.height), cachedSet.getTextureBounds()); + + // Now check that appending a new cached set properly cleans up RenderEngine resources. + CachedSet::Layer& layer3 = *mTestLayers[2]->cachedSetLayer.get(); + cachedSet.append(CachedSet(layer3)); +} + +TEST_F(CachedSetTest, renderWhitePointNoColorTransform) { + // Skip the 0th layer to ensure that the bounding box of the layers is offset from (0, 0) + // This is a duplicate of the "renderWhitePoint" test, but setting "deviceHandlesColorTransform" + // to false, in the render call. + + CachedSet::Layer& layer1 = *mTestLayers[1]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE1 = mTestLayers[1]->layerFE; + CachedSet::Layer& layer2 = *mTestLayers[2]->cachedSetLayer.get(); + sp<mock::LayerFE> layerFE2 = mTestLayers[2]->layerFE; + + CachedSet cachedSet(layer1); + cachedSet.append(CachedSet(layer2)); + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList1; + clientCompList1.push_back({}); + + std::vector<compositionengine::LayerFE::LayerSettings> clientCompList2; + clientCompList2.push_back({}); + + mOutputState.displayBrightnessNits = 400.f; + + const auto drawLayers = + [&](const renderengine::DisplaySettings& displaySettings, + const std::vector<renderengine::LayerSettings>&, + const std::shared_ptr<renderengine::ExternalTexture>&, const bool, + base::unique_fd&&) -> std::future<renderengine::RenderEngineResult> { + EXPECT_EQ(mOutputState.displayBrightnessNits, displaySettings.targetLuminanceNits); + return futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()}); + }; + + EXPECT_CALL(*layerFE1, + prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + mOutputState.displayBrightnessNits))) + .WillOnce(Return(clientCompList1)); + EXPECT_CALL(*layerFE2, + prepareClientCompositionList(ClientCompositionTargetSettingsWhitePointEq( + mOutputState.displayBrightnessNits))) + .WillOnce(Return(clientCompList2)); + EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); + mOutputState.isSecure = true; + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, false); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); @@ -527,7 +578,7 @@ TEST_F(CachedSetTest, rendersWithOffsetFramebufferContent) { EXPECT_CALL(*layerFE1, prepareClientCompositionList(_)).WillOnce(Return(clientCompList1)); EXPECT_CALL(*layerFE2, prepareClientCompositionList(_)).WillOnce(Return(clientCompList2)); EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); expectReadyBuffer(cachedSet); EXPECT_EQ(mOutputState.framebufferSpace, cachedSet.getOutputSpace()); @@ -767,7 +818,7 @@ TEST_F(CachedSetTest, addHolePunch) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } TEST_F(CachedSetTest, addHolePunch_noBuffer) { @@ -829,7 +880,7 @@ TEST_F(CachedSetTest, addHolePunch_noBuffer) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } TEST_F(CachedSetTest, append_removesHolePunch) { @@ -969,7 +1020,7 @@ TEST_F(CachedSetTest, addBlur) { }; EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).WillOnce(Invoke(drawLayers)); - cachedSet.render(mRenderEngine, mTexturePool, mOutputState); + cachedSet.render(mRenderEngine, mTexturePool, mOutputState, true); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp index 50e3a288cb..96021ec104 100644 --- a/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/planner/FlattenerTest.cpp @@ -159,13 +159,13 @@ void FlattenerTest::initializeFlattener(const std::vector<const LayerState*>& la initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // same geometry, update the internal layer stack initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); } void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState*>& layers) { @@ -177,7 +177,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -187,7 +187,7 @@ void FlattenerTest::expectAllLayersFlattened(const std::vector<const LayerState* initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); const auto buffer = layers[0]->getOutputLayer()->getState().overrideInfo.buffer; EXPECT_NE(nullptr, buffer); @@ -222,7 +222,7 @@ TEST_F(FlattenerTest, flattenLayers_ActiveLayersAreNotFlattened) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); } TEST_F(FlattenerTest, flattenLayers_ActiveLayersWithLowFpsAreFlattened) { @@ -284,7 +284,7 @@ TEST_F(FlattenerTest, flattenLayers_FlattenedLayersStayFlattenWhenNoUpdate) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -389,7 +389,7 @@ TEST_F(FlattenerTest, flattenLayers_addLayerToFlattenedCauseReset) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -428,7 +428,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -437,7 +437,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_NE(nullptr, overrideBuffer2); @@ -452,7 +452,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_NE(nullptr, overrideBuffer2); @@ -461,7 +461,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateToFlatten) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -505,7 +505,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -521,7 +521,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.setOrientation(ui::ROTATION_90); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -534,7 +534,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.setOrientation(ui::ROTATION_180); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -550,7 +550,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -562,7 +562,7 @@ TEST_F(FlattenerTest, flattenLayers_BufferUpdateForMiddleLayer) { EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); mOutputState.framebufferSpace.setOrientation(ui::ROTATION_270); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -603,7 +603,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -616,7 +616,7 @@ TEST_F(FlattenerTest, flattenLayers_pipRequiresRoundedCorners) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -669,7 +669,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -682,7 +682,7 @@ TEST_F(FlattenerTest, flattenLayers_pip) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -743,7 +743,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer0); @@ -753,7 +753,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleLayer) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer0); // got overridden EXPECT_EQ(nullptr, overrideBuffer1); // did not @@ -815,7 +815,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer0); @@ -825,7 +825,7 @@ TEST_F(FlattenerTest, flattenLayers_holePunchSingleColorLayer) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer0); // got overridden EXPECT_EQ(nullptr, overrideBuffer1); // did not @@ -871,7 +871,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -881,7 +881,7 @@ TEST_F(FlattenerTest, flattenLayers_flattensBlurBehindRunIfFirstRun) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); EXPECT_EQ(nullptr, overrideBuffer3); @@ -917,7 +917,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -928,7 +928,7 @@ TEST_F(FlattenerTest, flattenLayers_doesNotFlattenBlurBehindRun) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); } @@ -971,7 +971,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); for (const auto layer : layers) { EXPECT_EQ(nullptr, layer->getOutputLayer()->getState().overrideInfo.buffer); @@ -981,7 +981,7 @@ TEST_F(FlattenerTest, flattenLayers_flattenSkipsLayerWithBlurBehind) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, blurOverrideBuffer); EXPECT_NE(nullptr, overrideBuffer3); @@ -1020,7 +1020,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); const auto& cachedSet = mFlattener->getNewCachedSetForTesting(); ASSERT_NE(std::nullopt, cachedSet); @@ -1034,7 +1034,7 @@ TEST_F(FlattenerTest, flattenLayers_whenBlurLayerIsChanging_appliesBlurToInactiv initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer2, overrideBuffer1); EXPECT_EQ(nullptr, blurOverrideBuffer); @@ -1063,7 +1063,7 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { initializeOverrideBuffer(layers); EXPECT_EQ(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -1071,12 +1071,12 @@ TEST_F(FlattenerTest, flattenLayers_renderCachedSets_doesNotRenderTwice) { // Simulate attempting to render prior to merging the new cached set with the layer stack. // Here we should not try to re-render. EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We provide the override buffer now that it's rendered EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer2, overrideBuffer1); @@ -1120,7 +1120,8 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)).Times(0); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - - (kCachedSetRenderDuration + 10ms)); + (kCachedSetRenderDuration + 10ms), + true); } EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) @@ -1128,7 +1129,8 @@ TEST_F(FlattenerRenderSchedulingTest, flattenLayers_renderCachedSets_defersUpToM futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); mFlattener->renderCachedSets(mOutputState, std::chrono::steady_clock::now() - - (kCachedSetRenderDuration + 10ms)); + (kCachedSetRenderDuration + 10ms), + true); } TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { @@ -1162,7 +1164,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -1175,7 +1177,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsBT601_625) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -1213,7 +1215,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -1226,7 +1228,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -1264,7 +1266,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -1277,7 +1279,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsHDR2) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); @@ -1318,7 +1320,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -1332,7 +1334,7 @@ TEST_F(FlattenerTest, flattenLayers_skipsColorLayers) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_EQ(nullptr, overrideBuffer1); EXPECT_EQ(nullptr, overrideBuffer2); @@ -1371,7 +1373,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) { EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, _, _)) .WillOnce(Return(ByMove( futureOf<renderengine::RenderEngineResult>({NO_ERROR, base::unique_fd()})))); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); // We've rendered a CachedSet, but we haven't merged it in. EXPECT_EQ(nullptr, overrideBuffer1); @@ -1384,7 +1386,7 @@ TEST_F(FlattenerTest, flattenLayers_includes_DISPLAY_DECORATION) { initializeOverrideBuffer(layers); EXPECT_NE(getNonBufferHash(layers), mFlattener->flattenLayers(layers, getNonBufferHash(layers), mTime)); - mFlattener->renderCachedSets(mOutputState, std::nullopt); + mFlattener->renderCachedSets(mOutputState, std::nullopt, true); EXPECT_NE(nullptr, overrideBuffer1); EXPECT_EQ(overrideBuffer1, overrideBuffer2); diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index a915b615d9..26fbd55d6e 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -104,7 +104,7 @@ DisplayDevice::DisplayDevice(DisplayDeviceCreationArgs& args) mCompositionDisplay->getRenderSurface()->initialize(); - setPowerMode(args.initialPowerMode); + if (args.initialPowerMode.has_value()) setPowerMode(args.initialPowerMode.value()); // initialize the display orientation transform. setProjection(ui::ROTATION_0, Rect::INVALID_RECT, Rect::INVALID_RECT); @@ -173,20 +173,31 @@ auto DisplayDevice::getInputInfo() const -> InputInfo { } void DisplayDevice::setPowerMode(hal::PowerMode mode) { + if (mode == hal::PowerMode::OFF || mode == hal::PowerMode::ON) { + if (mStagedBrightness && mBrightness != *mStagedBrightness) { + getCompositionDisplay()->setNextBrightness(*mStagedBrightness); + mBrightness = *mStagedBrightness; + } + mStagedBrightness = std::nullopt; + getCompositionDisplay()->applyDisplayBrightness(true); + } + mPowerMode = mode; - getCompositionDisplay()->setCompositionEnabled(mPowerMode != hal::PowerMode::OFF); + + getCompositionDisplay()->setCompositionEnabled(mPowerMode.has_value() && + *mPowerMode != hal::PowerMode::OFF); } void DisplayDevice::enableLayerCaching(bool enable) { getCompositionDisplay()->setLayerCachingEnabled(enable); } -hal::PowerMode DisplayDevice::getPowerMode() const { +std::optional<hal::PowerMode> DisplayDevice::getPowerMode() const { return mPowerMode; } bool DisplayDevice::isPoweredOn() const { - return mPowerMode != hal::PowerMode::OFF; + return mPowerMode && *mPowerMode != hal::PowerMode::OFF; } void DisplayDevice::setActiveMode(DisplayModeId id) { @@ -211,6 +222,7 @@ status_t DisplayDevice::initiateModeChange(const ActiveModeInfo& info, to_string(getId()).c_str()); return BAD_VALUE; } + mNumModeSwitchesInPolicy++; mUpcomingActiveMode = info; ATRACE_INT(mActiveModeFPSHwcTrace.c_str(), info.mode->getFps().getIntValue()); return mHwComposer.setActiveModeWithConstraints(getPhysicalId(), info.mode->getHwcId(), @@ -324,8 +336,10 @@ void DisplayDevice::stageBrightness(float brightness) { } void DisplayDevice::persistBrightness(bool needsComposite) { - if (needsComposite && mStagedBrightness && mBrightness != *mStagedBrightness) { - getCompositionDisplay()->setNextBrightness(*mStagedBrightness); + if (mStagedBrightness && mBrightness != *mStagedBrightness) { + if (needsComposite) { + getCompositionDisplay()->setNextBrightness(*mStagedBrightness); + } mBrightness = *mStagedBrightness; } mStagedBrightness = std::nullopt; @@ -372,7 +386,7 @@ void DisplayDevice::dump(std::string& result) const { } result += "\n powerMode="s; - result += to_string(mPowerMode); + result += mPowerMode.has_value() ? to_string(mPowerMode.value()) : "OFF(reset)"; result += '\n'; if (mRefreshRateConfigs) { @@ -537,6 +551,27 @@ void DisplayDevice::clearDesiredActiveModeState() { mDesiredActiveModeChanged = false; } +status_t DisplayDevice::setRefreshRatePolicy( + const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, bool overridePolicy) { + const auto oldPolicy = mRefreshRateConfigs->getCurrentPolicy(); + const status_t setPolicyResult = overridePolicy + ? mRefreshRateConfigs->setOverridePolicy(policy) + : mRefreshRateConfigs->setDisplayManagerPolicy(*policy); + + if (setPolicyResult == OK) { + const int numModeChanges = mNumModeSwitchesInPolicy.exchange(0); + + ALOGI("Display %s policy changed\n" + "Previous: {%s}\n" + "Current: {%s}\n" + "%d mode changes were performed under the previous policy", + to_string(getId()).c_str(), oldPolicy.toString().c_str(), + policy ? policy->toString().c_str() : "null", numModeChanges); + } + + return setPolicyResult; +} + std::atomic<int32_t> DisplayDeviceState::sNextSequenceId(1); } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index d5d87b40de..fc24a9ce49 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -181,7 +181,7 @@ public: /* ------------------------------------------------------------------------ * Display power mode management. */ - hardware::graphics::composer::hal::PowerMode getPowerMode() const; + std::optional<hardware::graphics::composer::hal::PowerMode> getPowerMode() const; void setPowerMode(hardware::graphics::composer::hal::PowerMode mode); bool isPoweredOn() const; @@ -249,6 +249,10 @@ public: nsecs_t getVsyncPeriodFromHWC() const; nsecs_t getRefreshTimestamp() const; + status_t setRefreshRatePolicy( + const std::optional<scheduler::RefreshRateConfigs::Policy>& policy, + bool overridePolicy); + // release HWC resources (if any) for removable displays void disconnect(); @@ -277,8 +281,8 @@ private: static ui::Transform::RotationFlags sPrimaryDisplayRotationFlags; - hardware::graphics::composer::hal::PowerMode mPowerMode = - hardware::graphics::composer::hal::PowerMode::OFF; + // 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; @@ -303,6 +307,8 @@ private: TracedOrdinal<bool> mDesiredActiveModeChanged GUARDED_BY(mActiveModeLock) = {"DesiredActiveModeChanged", false}; ActiveModeInfo mUpcomingActiveMode GUARDED_BY(kMainThreadContext); + + std::atomic_int mNumModeSwitchesInPolicy = 0; }; struct DisplayDeviceState { @@ -360,8 +366,7 @@ struct DisplayDeviceCreationArgs { HdrCapabilities hdrCapabilities; int32_t supportedPerFrameMetadata{0}; std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hwcColorModes; - hardware::graphics::composer::hal::PowerMode initialPowerMode{ - hardware::graphics::composer::hal::PowerMode::ON}; + std::optional<hardware::graphics::composer::hal::PowerMode> initialPowerMode; bool isPrimary{false}; DisplayModes supportedModes; DisplayModeId activeModeId; diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 0da8ecea85..a6aee1f2f5 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -738,6 +738,13 @@ ftl::Future<status_t> HWComposer::setDisplayBrightness( }); } +bool HWComposer::getValidateSkipped(HalDisplayId displayId) const { + if (mDisplayData.count(displayId) == 0) { + return false; + } + return mDisplayData.at(displayId).validateWasSkipped; +} + status_t HWComposer::setBootDisplayMode(PhysicalDisplayId displayId, hal::HWConfigId displayModeId) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 4c0ecd8502..92a8f30f1b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -199,6 +199,9 @@ public: PhysicalDisplayId, float brightness, float brightnessNits, const Hwc2::Composer::DisplayBrightnessOptions&) = 0; + // Get whether the display skipped validation on the latest present + virtual bool getValidateSkipped(HalDisplayId displayId) const = 0; + // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected @@ -397,6 +400,8 @@ public: status_t setActiveColorMode(PhysicalDisplayId, ui::ColorMode, ui::RenderIntent) override; + bool getValidateSkipped(HalDisplayId displayId) const override; + // Composer 2.4 ui::DisplayConnectionType getDisplayConnectionType(PhysicalDisplayId) const override; bool isVsyncPeriodSwitchSupported(PhysicalDisplayId) const override; diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index b5678b49b8..a0350b717a 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -187,7 +187,7 @@ bool PowerAdvisor::supportsPowerHintSession() { if (!mSupportsPowerHint.has_value()) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); - mSupportsPowerHint = halWrapper->supportsPowerHintSession(); + mSupportsPowerHint = halWrapper && halWrapper->supportsPowerHintSession(); } return *mSupportsPowerHint; } @@ -196,7 +196,7 @@ bool PowerAdvisor::isPowerHintSessionRunning() { return mPowerHintSessionRunning; } -void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { +void PowerAdvisor::setTargetWorkDuration(int64_t targetDuration) { if (!usePowerHintSession()) { ALOGV("Power hint session target duration cannot be set, skipping"); return; @@ -205,26 +205,45 @@ void PowerAdvisor::setTargetWorkDuration(int64_t targetDurationNanos) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->setTargetWorkDuration(targetDurationNanos - kTargetSafetyMargin.count()); + halWrapper->setTargetWorkDuration(targetDuration); } } } -void PowerAdvisor::sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) { +void PowerAdvisor::sendActualWorkDuration() { if (!mBootFinished || !usePowerHintSession()) { ALOGV("Actual work duration power hint cannot be sent, skipping"); return; } - { + const std::optional<nsecs_t> actualDuration = estimateWorkDuration(false); + if (actualDuration.has_value()) { std::lock_guard lock(mPowerHalMutex); HalWrapper* const halWrapper = getPowerHal(); if (halWrapper != nullptr) { - halWrapper->sendActualWorkDuration(actualDurationNanos, timeStampNanos); + halWrapper->sendActualWorkDuration(*actualDuration + kTargetSafetyMargin.count(), + systemTime()); + } + } +} + +void PowerAdvisor::sendPredictedWorkDuration() { + if (!mBootFinished || !usePowerHintSession()) { + ALOGV("Actual work duration power hint cannot be sent, skipping"); + return; + } + + const std::optional<nsecs_t> predictedDuration = estimateWorkDuration(true); + + if (predictedDuration.has_value()) { + std::lock_guard lock(mPowerHalMutex); + HalWrapper* const halWrapper = getPowerHal(); + if (halWrapper != nullptr) { + halWrapper->sendActualWorkDuration(*predictedDuration + kTargetSafetyMargin.count(), + systemTime()); } } } -// needs to be set after the flag is known but before PowerAdvisor enters onBootFinished void PowerAdvisor::enablePowerHint(bool enabled) { mPowerHintEnabled = enabled; } @@ -244,6 +263,302 @@ bool PowerAdvisor::startPowerHintSession(const std::vector<int32_t>& threadIds) return mPowerHintSessionRunning; } +void PowerAdvisor::setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + if (displayData.gpuEndFenceTime) { + nsecs_t signalTime = displayData.gpuEndFenceTime->getSignalTime(); + if (signalTime != Fence::SIGNAL_TIME_INVALID && signalTime != Fence::SIGNAL_TIME_PENDING) { + for (auto&& [_, otherDisplayData] : mDisplayTimingData) { + // If the previous display started before us but ended after we should have + // started, then it likely delayed our start time and we must compensate for that. + // Displays finishing earlier should have already made their way through this call + // and swapped their timing into "lastValid" from "latest", so we check that here. + if (!otherDisplayData.lastValidGpuStartTime.has_value()) continue; + if ((*otherDisplayData.lastValidGpuStartTime < *displayData.gpuStartTime) && + (*otherDisplayData.lastValidGpuEndTime > *displayData.gpuStartTime)) { + displayData.lastValidGpuStartTime = *otherDisplayData.lastValidGpuEndTime; + break; + } + } + displayData.lastValidGpuStartTime = displayData.gpuStartTime; + displayData.lastValidGpuEndTime = signalTime; + } + } + displayData.gpuEndFenceTime = std::move(fenceTime); + displayData.gpuStartTime = systemTime(); +} + +void PowerAdvisor::setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, + nsecs_t validateEndTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + displayData.hwcValidateStartTime = validateStartTime; + displayData.hwcValidateEndTime = validateEndTime; +} + +void PowerAdvisor::setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) { + DisplayTimingData& displayData = mDisplayTimingData[displayId]; + displayData.hwcPresentStartTime = presentStartTime; + displayData.hwcPresentEndTime = presentEndTime; +} + +void PowerAdvisor::setSkippedValidate(DisplayId displayId, bool skipped) { + mDisplayTimingData[displayId].skippedValidate = skipped; +} + +void PowerAdvisor::setRequiresClientComposition(DisplayId displayId, + bool requiresClientComposition) { + mDisplayTimingData[displayId].usedClientComposition = requiresClientComposition; +} + +void PowerAdvisor::setExpectedPresentTime(nsecs_t expectedPresentTime) { + mExpectedPresentTimes.append(expectedPresentTime); +} + +void PowerAdvisor::setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) { + mLastSfPresentEndTime = presentEndTime; + mLastPresentFenceTime = presentFenceTime; +} + +void PowerAdvisor::setFrameDelay(nsecs_t frameDelayDuration) { + mFrameDelayDuration = frameDelayDuration; +} + +void PowerAdvisor::setHwcPresentDelayedTime( + DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) { + mDisplayTimingData[displayId].hwcPresentDelayedTime = + (earliestFrameStartTime - std::chrono::steady_clock::now()).count() + systemTime(); +} + +void PowerAdvisor::setCommitStart(nsecs_t commitStartTime) { + mCommitStartTimes.append(commitStartTime); +} + +void PowerAdvisor::setCompositeEnd(nsecs_t compositeEnd) { + mLastPostcompDuration = compositeEnd - mLastSfPresentEndTime; +} + +void PowerAdvisor::setDisplays(std::vector<DisplayId>& displayIds) { + mDisplayIds = displayIds; +} + +void PowerAdvisor::setTotalFrameTargetWorkDuration(nsecs_t targetDuration) { + mTotalFrameTargetDuration = targetDuration; +} + +std::vector<DisplayId> PowerAdvisor::getOrderedDisplayIds( + std::optional<nsecs_t> DisplayTimingData::*sortBy) { + std::vector<DisplayId> sortedDisplays; + std::copy_if(mDisplayIds.begin(), mDisplayIds.end(), std::back_inserter(sortedDisplays), + [&](DisplayId id) { + return mDisplayTimingData.count(id) && + (mDisplayTimingData[id].*sortBy).has_value(); + }); + std::sort(sortedDisplays.begin(), sortedDisplays.end(), [&](DisplayId idA, DisplayId idB) { + return *(mDisplayTimingData[idA].*sortBy) < *(mDisplayTimingData[idB].*sortBy); + }); + return sortedDisplays; +} + +std::optional<nsecs_t> PowerAdvisor::estimateWorkDuration(bool earlyHint) { + if (earlyHint && (!mExpectedPresentTimes.isFull() || !mCommitStartTimes.isFull())) { + return std::nullopt; + } + + // Tracks when we finish presenting to hwc + nsecs_t estimatedEndTime = mCommitStartTimes[0]; + + // How long we spent this frame not doing anything, waiting for fences or vsync + nsecs_t idleDuration = 0; + + // Most recent previous gpu end time in the current frame, probably from a prior display, used + // as the start time for the next gpu operation if it ran over time since it probably blocked + std::optional<nsecs_t> previousValidGpuEndTime; + + // The currently estimated gpu end time for the frame, + // used to accumulate gpu time as we iterate over the active displays + std::optional<nsecs_t> estimatedGpuEndTime; + + // If we're predicting at the start of the frame, we use last frame as our reference point + // If we're predicting at the end of the frame, we use the current frame as a reference point + nsecs_t referenceFrameStartTime = (earlyHint ? mCommitStartTimes[-1] : mCommitStartTimes[0]); + + // When the prior frame should be presenting to the display + // If we're predicting at the start of the frame, we use last frame's expected present time + // If we're predicting at the end of the frame, the present fence time is already known + nsecs_t lastFramePresentTime = (earlyHint ? mExpectedPresentTimes[-1] : mLastPresentFenceTime); + + // The timing info for the previously calculated display, if there was one + std::optional<DisplayTimeline> previousDisplayReferenceTiming; + std::vector<DisplayId>&& displayIds = + getOrderedDisplayIds(&DisplayTimingData::hwcPresentStartTime); + DisplayTimeline referenceTiming, estimatedTiming; + + // Iterate over the displays that use hwc in the same order they are presented + for (DisplayId displayId : displayIds) { + if (mDisplayTimingData.count(displayId) == 0) { + continue; + } + + auto& displayData = mDisplayTimingData.at(displayId); + + // mLastPresentFenceTime should always be the time of the reference frame, since it will be + // the previous frame's present fence if called at the start, and current frame's if called + // at the end + referenceTiming = displayData.calculateDisplayTimeline(mLastPresentFenceTime); + + // If this is the first display, include the duration before hwc present starts + if (!previousDisplayReferenceTiming.has_value()) { + estimatedEndTime += referenceTiming.hwcPresentStartTime - referenceFrameStartTime; + } else { // Otherwise add the time since last display's hwc present finished + estimatedEndTime += referenceTiming.hwcPresentStartTime - + previousDisplayReferenceTiming->hwcPresentEndTime; + } + + // Late hint can re-use reference timing here since it's estimating its own reference frame + estimatedTiming = earlyHint + ? referenceTiming.estimateTimelineFromReference(lastFramePresentTime, + estimatedEndTime) + : referenceTiming; + + // Update predicted present finish time with this display's present time + estimatedEndTime = estimatedTiming.hwcPresentEndTime; + + // Track how long we spent waiting for the fence, can be excluded from the timing estimate + idleDuration += estimatedTiming.probablyWaitsForPresentFence + ? lastFramePresentTime - estimatedTiming.presentFenceWaitStartTime + : 0; + + // Track how long we spent waiting to present, can be excluded from the timing estimate + idleDuration += earlyHint ? 0 : referenceTiming.hwcPresentDelayDuration; + + // Estimate the reference frame's gpu timing + auto gpuTiming = displayData.estimateGpuTiming(previousValidGpuEndTime); + if (gpuTiming.has_value()) { + previousValidGpuEndTime = gpuTiming->startTime + gpuTiming->duration; + + // Estimate the prediction frame's gpu end time from the reference frame + estimatedGpuEndTime = + std::max(estimatedTiming.hwcPresentStartTime, estimatedGpuEndTime.value_or(0)) + + gpuTiming->duration; + } + previousDisplayReferenceTiming = referenceTiming; + } + ATRACE_INT64("Idle duration", idleDuration); + + nsecs_t estimatedFlingerEndTime = earlyHint ? estimatedEndTime : mLastSfPresentEndTime; + + // Don't count time spent idly waiting in the estimate as we could do more work in that time + estimatedEndTime -= idleDuration; + estimatedFlingerEndTime -= idleDuration; + + // We finish the frame when both present and the gpu are done, so wait for the later of the two + // Also add the frame delay duration since the target did not move while we were delayed + nsecs_t totalDuration = mFrameDelayDuration + + std::max(estimatedEndTime, estimatedGpuEndTime.value_or(0)) - mCommitStartTimes[0]; + + // We finish SurfaceFlinger when post-composition finishes, so add that in here + nsecs_t flingerDuration = + estimatedFlingerEndTime + mLastPostcompDuration - mCommitStartTimes[0]; + + // Combine the two timings into a single normalized one + nsecs_t combinedDuration = combineTimingEstimates(totalDuration, flingerDuration); + + return std::make_optional(combinedDuration); +} + +nsecs_t PowerAdvisor::combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration) { + nsecs_t targetDuration; + { + std::lock_guard lock(mPowerHalMutex); + targetDuration = *getPowerHal()->getTargetWorkDuration(); + } + if (!mTotalFrameTargetDuration.has_value()) return flingerDuration; + + // Normalize total to the flinger target (vsync period) since that's how often we actually send + // hints + nsecs_t normalizedTotalDuration = (targetDuration * totalDuration) / *mTotalFrameTargetDuration; + return std::max(flingerDuration, normalizedTotalDuration); +} + +PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimeline::estimateTimelineFromReference( + nsecs_t fenceTime, nsecs_t displayStartTime) { + DisplayTimeline estimated; + estimated.hwcPresentStartTime = displayStartTime; + + // We don't predict waiting for vsync alignment yet + estimated.hwcPresentDelayDuration = 0; + + // How long we expect to run before we start waiting for the fence + // For now just re-use last frame's post-present duration and assume it will not change much + // Excludes time spent waiting for vsync since that's not going to be consistent + estimated.presentFenceWaitStartTime = estimated.hwcPresentStartTime + + (presentFenceWaitStartTime - (hwcPresentStartTime + hwcPresentDelayDuration)); + estimated.probablyWaitsForPresentFence = fenceTime > estimated.presentFenceWaitStartTime; + estimated.hwcPresentEndTime = postPresentFenceHwcPresentDuration + + (estimated.probablyWaitsForPresentFence ? fenceTime + : estimated.presentFenceWaitStartTime); + return estimated; +} + +PowerAdvisor::DisplayTimeline PowerAdvisor::DisplayTimingData::calculateDisplayTimeline( + nsecs_t fenceTime) { + DisplayTimeline timeline; + // How long between calling hwc present and trying to wait on the fence + const nsecs_t fenceWaitStartDelay = + (skippedValidate ? kFenceWaitStartDelaySkippedValidate : kFenceWaitStartDelayValidated) + .count(); + + // Did our reference frame wait for an appropriate vsync before calling into hwc + const bool waitedOnHwcPresentTime = hwcPresentDelayedTime.has_value() && + *hwcPresentDelayedTime > *hwcPresentStartTime && + *hwcPresentDelayedTime < *hwcPresentEndTime; + + // Use validate start here if we skipped it because we did validate + present together + timeline.hwcPresentStartTime = skippedValidate ? *hwcValidateStartTime : *hwcPresentStartTime; + + // Use validate end here if we skipped it because we did validate + present together + timeline.hwcPresentEndTime = skippedValidate ? *hwcValidateEndTime : *hwcPresentEndTime; + + // How long hwc present was delayed waiting for the next appropriate vsync + timeline.hwcPresentDelayDuration = + (waitedOnHwcPresentTime ? *hwcPresentDelayedTime - *hwcPresentStartTime : 0); + // When we started waiting for the present fence after calling into hwc present + timeline.presentFenceWaitStartTime = + timeline.hwcPresentStartTime + timeline.hwcPresentDelayDuration + fenceWaitStartDelay; + timeline.probablyWaitsForPresentFence = fenceTime > timeline.presentFenceWaitStartTime && + fenceTime < timeline.hwcPresentEndTime; + + // How long we ran after we finished waiting for the fence but before hwc present finished + timeline.postPresentFenceHwcPresentDuration = timeline.hwcPresentEndTime - + (timeline.probablyWaitsForPresentFence ? fenceTime + : timeline.presentFenceWaitStartTime); + return timeline; +} + +std::optional<PowerAdvisor::GpuTimeline> PowerAdvisor::DisplayTimingData::estimateGpuTiming( + std::optional<nsecs_t> previousEnd) { + if (!(usedClientComposition && lastValidGpuStartTime.has_value() && gpuEndFenceTime)) { + return std::nullopt; + } + const nsecs_t latestGpuStartTime = std::max(previousEnd.value_or(0), *gpuStartTime); + const nsecs_t latestGpuEndTime = gpuEndFenceTime->getSignalTime(); + nsecs_t gpuDuration = 0; + if (latestGpuEndTime != Fence::SIGNAL_TIME_INVALID && + latestGpuEndTime != Fence::SIGNAL_TIME_PENDING) { + // If we know how long the most recent gpu duration was, use that + gpuDuration = latestGpuEndTime - latestGpuStartTime; + } else if (lastValidGpuEndTime.has_value()) { + // If we don't have the fence data, use the most recent information we do have + gpuDuration = *lastValidGpuEndTime - *lastValidGpuStartTime; + if (latestGpuEndTime == Fence::SIGNAL_TIME_PENDING) { + // If pending but went over the previous duration, use current time as the end + gpuDuration = std::max(gpuDuration, systemTime() - latestGpuStartTime); + } + } + return GpuTimeline{.duration = gpuDuration, .startTime = latestGpuStartTime}; +} + class HidlPowerHalWrapper : public PowerAdvisor::HalWrapper { public: HidlPowerHalWrapper(sp<V1_3::IPower> powerHal) : mPowerHal(std::move(powerHal)) {} @@ -325,6 +640,9 @@ AidlPowerHalWrapper::AidlPowerHalWrapper(sp<IPower> powerHal) : mPowerHal(std::m } mSupportsPowerHint = checkPowerHintSessionSupported(); + + // Currently set to 0 to disable rate limiter by default + mAllowedActualDeviation = base::GetIntProperty<nsecs_t>("debug.sf.allowed_actual_deviation", 0); } AidlPowerHalWrapper::~AidlPowerHalWrapper() { @@ -332,7 +650,7 @@ AidlPowerHalWrapper::~AidlPowerHalWrapper() { mPowerHintSession->close(); mPowerHintSession = nullptr; } -}; +} std::unique_ptr<PowerAdvisor::HalWrapper> AidlPowerHalWrapper::connect() { // This only waits if the service is actually declared @@ -370,7 +688,7 @@ bool AidlPowerHalWrapper::notifyDisplayUpdateImminent() { return ret.isOk(); } -// only version 2+ of the aidl supports power hint sessions, hidl has no support +// Only version 2+ of the aidl supports power hint sessions, hidl has no support bool AidlPowerHalWrapper::supportsPowerHintSession() { return mSupportsPowerHint; } @@ -424,30 +742,14 @@ bool AidlPowerHalWrapper::startPowerHintSession() { return isPowerHintSessionRunning(); } -bool AidlPowerHalWrapper::shouldSetTargetDuration(int64_t targetDurationNanos) { - if (targetDurationNanos <= 0) { - return false; - } - // report if the change in target from our last submission to now exceeds the threshold - return abs(1.0 - - static_cast<double>(mLastTargetDurationSent) / - static_cast<double>(targetDurationNanos)) >= kAllowedTargetDeviationPercent; -} - -void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { +void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDuration) { ATRACE_CALL(); - mTargetDuration = targetDurationNanos; - if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDurationNanos); - if (!sNormalizeTarget && isPowerHintSessionRunning() && - shouldSetTargetDuration(targetDurationNanos)) { - if (mLastActualDurationSent.has_value()) { - // update the error term here since we are actually sending an update to powerhal - if (sTraceHintSessionData) - ATRACE_INT64("Target error term", targetDurationNanos - *mLastActualDurationSent); - } - ALOGV("Sending target time: %" PRId64 "ns", targetDurationNanos); - mLastTargetDurationSent = targetDurationNanos; - auto ret = mPowerHintSession->updateTargetWorkDuration(targetDurationNanos); + mTargetDuration = targetDuration; + if (sTraceHintSessionData) ATRACE_INT64("Time target", targetDuration); + if (isPowerHintSessionRunning() && (targetDuration != mLastTargetDurationSent)) { + ALOGV("Sending target time: %" PRId64 "ns", targetDuration); + mLastTargetDurationSent = targetDuration; + auto ret = mPowerHintSession->updateTargetWorkDuration(targetDuration); if (!ret.isOk()) { ALOGW("Failed to set power hint target work duration with error: %s", ret.exceptionMessage().c_str()); @@ -456,8 +758,8 @@ void AidlPowerHalWrapper::setTargetWorkDuration(int64_t targetDurationNanos) { } } -bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { - // report if we have never reported before or are approaching a stale session +bool AidlPowerHalWrapper::shouldReportActualDurations() { + // Report if we have never reported before or are approaching a stale session if (!mLastActualDurationSent.has_value() || (systemTime() - mLastActualReportTimestamp) > kStaleTimeout.count()) { return true; @@ -466,65 +768,42 @@ bool AidlPowerHalWrapper::shouldReportActualDurationsNow() { if (!mActualDuration.has_value()) { return false; } - - // duration of most recent timing - const double mostRecentActualDuration = static_cast<double>(*mActualDuration); - // duration of the last timing actually reported to the powerhal - const double lastReportedActualDuration = static_cast<double>(*mLastActualDurationSent); - - // report if the change in duration from then to now exceeds the threshold - return abs(1.0 - mostRecentActualDuration / lastReportedActualDuration) >= - kAllowedActualDeviationPercent; + // Report if the change in actual duration exceeds the threshold + return abs(*mActualDuration - *mLastActualDurationSent) > mAllowedActualDeviation; } -void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) { +void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDuration, nsecs_t timestamp) { ATRACE_CALL(); - if (actualDurationNanos < 0 || !isPowerHintSessionRunning()) { + if (actualDuration < 0 || !isPowerHintSessionRunning()) { ALOGV("Failed to send actual work duration, skipping"); return; } - nsecs_t reportedDuration = actualDurationNanos; + const nsecs_t reportedDuration = actualDuration; - // normalize the sent values to a pre-set target - if (sNormalizeTarget) { - reportedDuration += mLastTargetDurationSent - mTargetDuration; - } else { - // when target duration change is within deviation and not updated, adjust the actual - // duration proportionally based on the difference, e.g. if new target is 5ms longer than - // last reported but actual duration is the same as last target, we want to report a smaller - // actual work duration now to indicate that we are overshooting - if (mLastTargetDurationSent != kDefaultTarget.count() && mTargetDuration != 0) { - reportedDuration = - static_cast<int64_t>(static_cast<long double>(mLastTargetDurationSent) / - mTargetDuration * actualDurationNanos); - mActualDuration = reportedDuration; - } - } mActualDuration = reportedDuration; WorkDuration duration; duration.durationNanos = reportedDuration; - duration.timeStampNanos = timeStampNanos; + duration.timeStampNanos = timestamp; mPowerHintQueue.push_back(duration); if (sTraceHintSessionData) { - ATRACE_INT64("Measured duration", actualDurationNanos); - ATRACE_INT64("Target error term", mTargetDuration - actualDurationNanos); + ATRACE_INT64("Measured duration", actualDuration); + ATRACE_INT64("Target error term", actualDuration - mTargetDuration); ATRACE_INT64("Reported duration", reportedDuration); ATRACE_INT64("Reported target", mLastTargetDurationSent); - ATRACE_INT64("Reported target error term", mLastTargetDurationSent - reportedDuration); + ATRACE_INT64("Reported target error term", reportedDuration - mLastTargetDurationSent); } ALOGV("Sending actual work duration of: %" PRId64 " on reported target: %" PRId64 " with error: %" PRId64, - reportedDuration, mLastTargetDurationSent, mLastTargetDurationSent - reportedDuration); + reportedDuration, mLastTargetDurationSent, reportedDuration - mLastTargetDurationSent); // This rate limiter queues similar duration reports to the powerhal into // batches to avoid excessive binder calls. The criteria to send a given batch // are outlined in shouldReportActualDurationsNow() - if (shouldReportActualDurationsNow()) { + if (shouldReportActualDurations()) { ALOGV("Sending hint update batch"); mLastActualReportTimestamp = systemTime(); auto ret = mPowerHintSession->reportActualWorkDuration(mPowerHintQueue); @@ -534,8 +813,8 @@ void AidlPowerHalWrapper::sendActualWorkDuration(int64_t actualDurationNanos, mShouldReconnectHal = true; } mPowerHintQueue.clear(); - // we save the non-normalized value here to detect % changes - mLastActualDurationSent = reportedDuration; + // We save the actual duration here for rate limiting + mLastActualDurationSent = actualDuration; } } @@ -551,76 +830,73 @@ std::optional<int64_t> AidlPowerHalWrapper::getTargetWorkDuration() { return mTargetDuration; } +void AidlPowerHalWrapper::setAllowedActualDeviation(nsecs_t allowedDeviation) { + mAllowedActualDeviation = allowedDeviation; +} + const bool AidlPowerHalWrapper::sTraceHintSessionData = base::GetBoolProperty(std::string("debug.sf.trace_hint_sessions"), false); -const bool AidlPowerHalWrapper::sNormalizeTarget = - base::GetBoolProperty(std::string("debug.sf.normalize_hint_session_durations"), false); - PowerAdvisor::HalWrapper* PowerAdvisor::getPowerHal() { - static std::unique_ptr<HalWrapper> sHalWrapper = nullptr; - static bool sHasHal = true; - - if (!sHasHal) { + if (!mHasHal) { return nullptr; } - // grab old hint session values before we destroy any existing wrapper + // Grab old hint session values before we destroy any existing wrapper std::vector<int32_t> oldPowerHintSessionThreadIds; std::optional<int64_t> oldTargetWorkDuration; - if (sHalWrapper != nullptr) { - oldPowerHintSessionThreadIds = sHalWrapper->getPowerHintSessionThreadIds(); - oldTargetWorkDuration = sHalWrapper->getTargetWorkDuration(); + if (mHalWrapper != nullptr) { + oldPowerHintSessionThreadIds = mHalWrapper->getPowerHintSessionThreadIds(); + oldTargetWorkDuration = mHalWrapper->getTargetWorkDuration(); } // If we used to have a HAL, but it stopped responding, attempt to reconnect if (mReconnectPowerHal) { - sHalWrapper = nullptr; + mHalWrapper = nullptr; mReconnectPowerHal = false; } - if (sHalWrapper != nullptr) { - auto wrapper = sHalWrapper.get(); - // if the wrapper is fine, return it, but if it indicates a reconnect, remake it + if (mHalWrapper != nullptr) { + auto wrapper = mHalWrapper.get(); + // If the wrapper is fine, return it, but if it indicates a reconnect, remake it if (!wrapper->shouldReconnectHAL()) { return wrapper; } ALOGD("Reconnecting Power HAL"); - sHalWrapper = nullptr; + mHalWrapper = nullptr; } - // at this point, we know for sure there is no running session + // At this point, we know for sure there is no running session mPowerHintSessionRunning = false; // First attempt to connect to the AIDL Power HAL - sHalWrapper = AidlPowerHalWrapper::connect(); + mHalWrapper = AidlPowerHalWrapper::connect(); // If that didn't succeed, attempt to connect to the HIDL Power HAL - if (sHalWrapper == nullptr) { - sHalWrapper = HidlPowerHalWrapper::connect(); + if (mHalWrapper == nullptr) { + mHalWrapper = HidlPowerHalWrapper::connect(); } else { ALOGD("Successfully connecting AIDL Power HAL"); - // if AIDL, pass on any existing hint session values - // thread ids always safe to set - sHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); - // only set duration and start if duration is defined + // If AIDL, pass on any existing hint session values + mHalWrapper->setPowerHintSessionThreadIds(oldPowerHintSessionThreadIds); + // Only set duration and start if duration is defined if (oldTargetWorkDuration.has_value()) { - sHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration); - // only start if possible to run and both threadids and duration are defined + mHalWrapper->setTargetWorkDuration(*oldTargetWorkDuration); + // Only start if possible to run and both threadids and duration are defined if (usePowerHintSession() && !oldPowerHintSessionThreadIds.empty()) { - mPowerHintSessionRunning = sHalWrapper->startPowerHintSession(); + mPowerHintSessionRunning = mHalWrapper->startPowerHintSession(); } } } // If we make it to this point and still don't have a HAL, it's unlikely we // will, so stop trying - if (sHalWrapper == nullptr) { - sHasHal = false; + if (mHalWrapper == nullptr) { + mHasHal = false; } - return sHalWrapper.get(); + return mHalWrapper.get(); } } // namespace impl diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 7c10e19e11..6e25f787d7 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -18,11 +18,15 @@ #include <atomic> #include <chrono> +#include <unordered_map> #include <unordered_set> +#include <ui/DisplayId.h> +#include <ui/FenceTime.h> #include <utils/Mutex.h> #include <android/hardware/power/IPower.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <ui/DisplayIdentification.h> #include "../Scheduler/OneShotTimer.h" @@ -44,13 +48,50 @@ public: virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; virtual bool isUsingExpensiveRendering() = 0; virtual void notifyDisplayUpdateImminent() = 0; + // Checks both if it supports and if it's enabled virtual bool usePowerHintSession() = 0; virtual bool supportsPowerHintSession() = 0; virtual bool isPowerHintSessionRunning() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) = 0; + // Sends a power hint that updates to the target work duration for the frame + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + // Sends a power hint for the actual known work duration at the end of the frame + virtual void sendActualWorkDuration() = 0; + // Sends a power hint for the upcoming frame predicted from previous frame timing + virtual void sendPredictedWorkDuration() = 0; + // Sets whether the power hint session is enabled virtual void enablePowerHint(bool enabled) = 0; + // Initializes the power hint session virtual bool startPowerHintSession(const std::vector<int32_t>& threadIds) = 0; + // Provides PowerAdvisor with a copy of the gpu fence so it can determine the gpu end time + virtual void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime) = 0; + // Reports the start and end times of a hwc validate call this frame for a given display + virtual void setHwcValidateTiming(DisplayId displayId, nsecs_t validateStartTime, + nsecs_t validateEndTime) = 0; + // Reports the start and end times of a hwc present call this frame for a given display + virtual void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) = 0; + // Reports the expected time that the current frame will present to the display + virtual void setExpectedPresentTime(nsecs_t expectedPresentTime) = 0; + // Reports the most recent present fence time and end time once known + virtual void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) = 0; + // Reports whether a display used client composition this frame + virtual void setRequiresClientComposition(DisplayId displayId, + bool requiresClientComposition) = 0; + // Reports whether a given display skipped validation this frame + virtual void setSkippedValidate(DisplayId displayId, bool skipped) = 0; + // Reports when a hwc present is delayed, and the time that it will resume + virtual void setHwcPresentDelayedTime( + DisplayId displayId, std::chrono::steady_clock::time_point earliestFrameStartTime) = 0; + // Reports the start delay for SurfaceFlinger this frame + virtual void setFrameDelay(nsecs_t frameDelayDuration) = 0; + // Reports the SurfaceFlinger commit start time this frame + virtual void setCommitStart(nsecs_t commitStartTime) = 0; + // Reports the SurfaceFlinger composite end time this frame + virtual void setCompositeEnd(nsecs_t compositeEndTime) = 0; + // Reports the list of the currently active displays + virtual void setDisplays(std::vector<DisplayId>& displayIds) = 0; + // Sets the target duration for the entire pipeline including the gpu + virtual void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) = 0; }; namespace impl { @@ -70,12 +111,11 @@ public: virtual void restartPowerHintSession() = 0; virtual void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) = 0; virtual bool startPowerHintSession() = 0; - virtual void setTargetWorkDuration(int64_t targetDurationNanos) = 0; - virtual void sendActualWorkDuration(int64_t actualDurationNanos, - nsecs_t timeStampNanos) = 0; + virtual void setTargetWorkDuration(nsecs_t targetDuration) = 0; + virtual void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) = 0; virtual bool shouldReconnectHAL() = 0; virtual std::vector<int32_t> getPowerHintSessionThreadIds() = 0; - virtual std::optional<int64_t> getTargetWorkDuration() = 0; + virtual std::optional<nsecs_t> getTargetWorkDuration() = 0; }; PowerAdvisor(SurfaceFlinger& flinger); @@ -89,26 +129,43 @@ public: bool usePowerHintSession() override; bool supportsPowerHintSession() override; bool isPowerHintSessionRunning() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timestamp) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration() override; + void sendPredictedWorkDuration() override; void enablePowerHint(bool enabled) override; bool startPowerHintSession(const std::vector<int32_t>& threadIds) override; + void setGpuFenceTime(DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime); + void setHwcValidateTiming(DisplayId displayId, nsecs_t valiateStartTime, + nsecs_t validateEndTime) override; + void setHwcPresentTiming(DisplayId displayId, nsecs_t presentStartTime, + nsecs_t presentEndTime) override; + void setSkippedValidate(DisplayId displayId, bool skipped) override; + void setRequiresClientComposition(DisplayId displayId, bool requiresClientComposition) override; + void setExpectedPresentTime(nsecs_t expectedPresentTime) override; + void setSfPresentTiming(nsecs_t presentFenceTime, nsecs_t presentEndTime) override; + void setHwcPresentDelayedTime( + DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime) override; + + void setFrameDelay(nsecs_t frameDelayDuration) override; + void setCommitStart(nsecs_t commitStartTime) override; + void setCompositeEnd(nsecs_t compositeEndTime) override; + void setDisplays(std::vector<DisplayId>& displayIds) override; + void setTotalFrameTargetWorkDuration(nsecs_t targetDuration) override; private: + friend class PowerAdvisorTest; + + // Tracks if powerhal exists + bool mHasHal = true; + // Holds the hal wrapper for getPowerHal + std::unique_ptr<HalWrapper> mHalWrapper GUARDED_BY(mPowerHalMutex) = nullptr; + HalWrapper* getPowerHal() REQUIRES(mPowerHalMutex); bool mReconnectPowerHal GUARDED_BY(mPowerHalMutex) = false; std::mutex mPowerHalMutex; std::atomic_bool mBootFinished = false; - std::optional<bool> mPowerHintEnabled; - std::optional<bool> mSupportsPowerHint; - bool mPowerHintSessionRunning = false; - - // An adjustable safety margin which moves the "target" earlier to allow flinger to - // go a bit over without dropping a frame, especially since we can't measure - // the exact time HWC finishes composition so "actual" durations are measured - // from the end of present() instead, which is a bit later. - static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 2ms; std::unordered_set<DisplayId> mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; @@ -117,6 +174,110 @@ private: std::atomic_bool mSendUpdateImminent = true; std::atomic<nsecs_t> mLastScreenUpdatedTime = 0; std::optional<scheduler::OneShotTimer> mScreenUpdateTimer; + + // Higher-level timing data used for estimation + struct DisplayTimeline { + // The start of hwc present, or the start of validate if it happened there instead + nsecs_t hwcPresentStartTime = -1; + // The end of hwc present or validate, whichever one actually presented + nsecs_t hwcPresentEndTime = -1; + // How long the actual hwc present was delayed after hwcPresentStartTime + nsecs_t hwcPresentDelayDuration = 0; + // When we think we started waiting for the present fence after calling into hwc present and + // after potentially waiting for the earliest present time + nsecs_t presentFenceWaitStartTime = -1; + // How long we ran after we finished waiting for the fence but before hwc present finished + nsecs_t postPresentFenceHwcPresentDuration = 0; + // Are we likely to have waited for the present fence during composition + bool probablyWaitsForPresentFence = false; + // Estimate one frame's timeline from that of a previous frame + DisplayTimeline estimateTimelineFromReference(nsecs_t fenceTime, nsecs_t displayStartTime); + }; + + struct GpuTimeline { + nsecs_t duration = 0; + nsecs_t startTime = -1; + }; + + // Power hint session data recorded from the pipeline + struct DisplayTimingData { + std::unique_ptr<FenceTime> gpuEndFenceTime; + std::optional<nsecs_t> gpuStartTime; + std::optional<nsecs_t> lastValidGpuEndTime; + std::optional<nsecs_t> lastValidGpuStartTime; + std::optional<nsecs_t> hwcPresentStartTime; + std::optional<nsecs_t> hwcPresentEndTime; + std::optional<nsecs_t> hwcValidateStartTime; + std::optional<nsecs_t> hwcValidateEndTime; + std::optional<nsecs_t> hwcPresentDelayedTime; + bool usedClientComposition = false; + bool skippedValidate = false; + // Calculate high-level timing milestones from more granular display timing data + DisplayTimeline calculateDisplayTimeline(nsecs_t fenceTime); + // Estimate the gpu duration for a given display from previous gpu timing data + std::optional<GpuTimeline> estimateGpuTiming(std::optional<nsecs_t> previousEnd); + }; + + template <class T, size_t N> + class RingBuffer { + std::array<T, N> elements = {}; + size_t mIndex = 0; + size_t numElements = 0; + + public: + void append(T item) { + mIndex = (mIndex + 1) % N; + numElements = std::min(N, numElements + 1); + elements[mIndex] = item; + } + bool isFull() const { return numElements == N; } + // Allows access like [0] == current, [-1] = previous, etc.. + T& operator[](int offset) { + size_t positiveOffset = + static_cast<size_t>((offset % static_cast<int>(N)) + static_cast<int>(N)); + return elements[(mIndex + positiveOffset) % N]; + } + }; + + // Filter and sort the display ids by a given property + std::vector<DisplayId> getOrderedDisplayIds(std::optional<nsecs_t> DisplayTimingData::*sortBy); + // Estimates a frame's total work duration including gpu time. + // Runs either at the beginning or end of a frame, using the most recent data available + std::optional<nsecs_t> estimateWorkDuration(bool earlyHint); + // There are two different targets and actual work durations we care about, + // this normalizes them together and takes the max of the two + nsecs_t combineTimingEstimates(nsecs_t totalDuration, nsecs_t flingerDuration); + + std::unordered_map<DisplayId, DisplayTimingData> mDisplayTimingData; + + // Current frame's delay + nsecs_t mFrameDelayDuration = 0; + // Last frame's post-composition duration + nsecs_t mLastPostcompDuration = 0; + // Buffer of recent commit start times + RingBuffer<nsecs_t, 2> mCommitStartTimes; + // Buffer of recent expected present times + RingBuffer<nsecs_t, 2> mExpectedPresentTimes; + // Most recent present fence time, set at the end of the frame once known + nsecs_t mLastPresentFenceTime = -1; + // Most recent present fence time, set at the end of the frame once known + nsecs_t mLastSfPresentEndTime = -1; + // Target for the entire pipeline including gpu + std::optional<nsecs_t> mTotalFrameTargetDuration; + // Updated list of display IDs + std::vector<DisplayId> mDisplayIds; + + std::optional<bool> mPowerHintEnabled; + std::optional<bool> mSupportsPowerHint; + bool mPowerHintSessionRunning = false; + + // An adjustable safety margin which pads the "actual" value sent to PowerHAL, + // encouraging more aggressive boosting to give SurfaceFlinger a larger margin for error + static constexpr const std::chrono::nanoseconds kTargetSafetyMargin = 1ms; + + // How long we expect hwc to run after the present call until it waits for the fence + static constexpr const std::chrono::nanoseconds kFenceWaitStartDelayValidated = 150us; + static constexpr const std::chrono::nanoseconds kFenceWaitStartDelaySkippedValidate = 250us; }; class AidlPowerHalWrapper : public PowerAdvisor::HalWrapper { @@ -133,50 +294,50 @@ public: void restartPowerHintSession() override; void setPowerHintSessionThreadIds(const std::vector<int32_t>& threadIds) override; bool startPowerHintSession() override; - void setTargetWorkDuration(int64_t targetDurationNanos) override; - void sendActualWorkDuration(int64_t actualDurationNanos, nsecs_t timeStampNanos) override; + void setTargetWorkDuration(nsecs_t targetDuration) override; + void sendActualWorkDuration(nsecs_t actualDuration, nsecs_t timestamp) override; bool shouldReconnectHAL() override; std::vector<int32_t> getPowerHintSessionThreadIds() override; - std::optional<int64_t> getTargetWorkDuration() override; + std::optional<nsecs_t> getTargetWorkDuration() override; private: + friend class AidlPowerHalWrapperTest; + bool checkPowerHintSessionSupported(); void closePowerHintSession(); - bool shouldReportActualDurationsNow(); - bool shouldSetTargetDuration(int64_t targetDurationNanos); + bool shouldReportActualDurations(); + + // Used for testing + void setAllowedActualDeviation(nsecs_t); const sp<hardware::power::IPower> mPowerHal = nullptr; bool mHasExpensiveRendering = false; bool mHasDisplayUpdateImminent = false; // Used to indicate an error state and need for reconstruction bool mShouldReconnectHal = false; - // This is not thread safe, but is currently protected by mPowerHalMutex so it needs no lock + + // Power hint session data + + // Concurrent access for this is protected by mPowerHalMutex sp<hardware::power::IPowerHintSession> mPowerHintSession = nullptr; // Queue of actual durations saved to report std::vector<hardware::power::WorkDuration> mPowerHintQueue; - // The latest un-normalized values we have received for target and actual - int64_t mTargetDuration = kDefaultTarget.count(); - std::optional<int64_t> mActualDuration; + // The latest values we have received for target and actual + nsecs_t mTargetDuration = kDefaultTarget.count(); + std::optional<nsecs_t> mActualDuration; // The list of thread ids, stored so we can restart the session from this class if needed std::vector<int32_t> mPowerHintThreadIds; - bool mSupportsPowerHint; + bool mSupportsPowerHint = false; // Keep track of the last messages sent for rate limiter change detection - std::optional<int64_t> mLastActualDurationSent; - // timestamp of the last report we sent, used to avoid stale sessions - int64_t mLastActualReportTimestamp = 0; - int64_t mLastTargetDurationSent = kDefaultTarget.count(); - // Whether to normalize all the actual values as error terms relative to a constant target - // This saves a binder call by not setting the target, and should not affect the pid values - static const bool sNormalizeTarget; + std::optional<nsecs_t> mLastActualDurationSent; + // Timestamp of the last report we sent, used to avoid stale sessions + nsecs_t mLastActualReportTimestamp = 0; + nsecs_t mLastTargetDurationSent = kDefaultTarget.count(); + // Max amount the error term can vary without causing an actual value report + nsecs_t mAllowedActualDeviation = -1; // Whether we should emit ATRACE_INT data for hint sessions static const bool sTraceHintSessionData; - - // Max percent the actual duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedActualDeviationPercent = 0.1; - // Max percent the target duration can vary without causing a report (eg: 0.1 = 10%) - static constexpr double kAllowedTargetDeviationPercent = 0.1; - // Target used for init and normalization, the actual value does not really matter - static constexpr const std::chrono::nanoseconds kDefaultTarget = 50ms; + static constexpr const std::chrono::nanoseconds kDefaultTarget = 16ms; // Amount of time after the last message was sent before the session goes stale // actually 100ms but we use 80 here to ideally avoid going stale static constexpr const std::chrono::nanoseconds kStaleTimeout = 80ms; diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index be16942d40..bcc94d3146 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -81,6 +81,7 @@ namespace android { namespace { constexpr int kDumpTableRowLength = 159; +const ui::Transform kIdentityTransform; } // namespace using namespace ftl::flag_operators; @@ -2162,7 +2163,8 @@ void Layer::writeToProtoCommonState(LayerProto* layerInfo, LayerVector::StateSet if ((traceFlags & LayerTracing::TRACE_INPUT) && needsInputInfo()) { WindowInfo info; if (useDrawing) { - info = fillInputInfo(ui::Transform(), /* displayIsSecure */ true); + info = fillInputInfo( + InputDisplayArgs{.transform = &kIdentityTransform, .isSecure = true}); } else { info = state.inputInfo; } @@ -2369,7 +2371,7 @@ void Layer::handleDropInputMode(gui::WindowInfo& info) const { } } -WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure) { +WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) { if (!hasInputInfo()) { mDrawingState.inputInfo.name = getName(); mDrawingState.inputInfo.ownerUid = mOwnerUid; @@ -2378,12 +2380,21 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp mDrawingState.inputInfo.displayId = getLayerStack().id; } + const ui::Transform& displayTransform = + displayArgs.transform != nullptr ? *displayArgs.transform : kIdentityTransform; + WindowInfo info = mDrawingState.inputInfo; info.id = sequence; info.displayId = getLayerStack().id; fillInputFrameInfo(info, displayTransform); + if (displayArgs.transform == nullptr) { + // Do not let the window receive touches if it is not associated with a valid display + // transform. We still allow the window to receive keys and prevent ANRs. + 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 @@ -2401,7 +2412,7 @@ WindowInfo Layer::fillInputInfo(const ui::Transform& displayTransform, bool disp // If the window will be blacked out on a display because the display does not have the secure // flag and the layer has the secure flag set, then drop input. - if (!displayIsSecure && isSecure()) { + if (!displayArgs.isSecure && isSecure()) { info.inputConfig |= WindowInfo::InputConfig::DROP_INPUT; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 24abad9b45..c547da06cb 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -844,7 +844,11 @@ public: bool getPremultipledAlpha() const; void setInputInfo(const gui::WindowInfo& info); - gui::WindowInfo fillInputInfo(const ui::Transform& displayTransform, bool displayIsSecure); + struct InputDisplayArgs { + const ui::Transform* transform = nullptr; + bool isSecure = false; + }; + gui::WindowInfo fillInputInfo(const InputDisplayArgs& displayArgs); /** * Returns whether this layer has an explicitly set input-info. diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index d4435c2818..a9180d4483 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -16,9 +16,10 @@ #include <algorithm> -#include "RefreshRateOverlay.h" +#include "BackgroundExecutor.h" #include "Client.h" #include "Layer.h" +#include "RefreshRateOverlay.h" #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wconversion" @@ -56,6 +57,14 @@ SurfaceComposerClient::Transaction createTransaction(const sp<SurfaceControl>& s } // namespace +SurfaceControlHolder::~SurfaceControlHolder() { + // Hand the sp<SurfaceControl> to the helper thread to release the last + // reference. This makes sure that the SurfaceControl is destructed without + // SurfaceFlinger::mStateLock held. + BackgroundExecutor::getInstance().sendCallbacks( + {[sc = std::move(mSurfaceControl)]() mutable { sc.clear(); }}); +} + void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left, SkColor color, SkCanvas& canvas) { const SkRect rect = [&]() { @@ -210,21 +219,27 @@ auto RefreshRateOverlay::SevenSegmentDrawer::draw(int number, SkColor color, return buffers; } +std::unique_ptr<SurfaceControlHolder> createSurfaceControlHolder() { + sp<SurfaceControl> surfaceControl = + SurfaceComposerClient::getDefault() + ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, kBufferHeight, + PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceBufferState); + return std::make_unique<SurfaceControlHolder>(std::move(surfaceControl)); +} + RefreshRateOverlay::RefreshRateOverlay(FpsRange fpsRange, bool showSpinner) : mFpsRange(fpsRange), mShowSpinner(showSpinner), - mSurfaceControl(SurfaceComposerClient::getDefault() - ->createSurface(String8("RefreshRateOverlay"), kBufferWidth, - kBufferHeight, PIXEL_FORMAT_RGBA_8888, - ISurfaceComposerClient::eFXSurfaceBufferState)) { + mSurfaceControl(createSurfaceControlHolder()) { if (!mSurfaceControl) { ALOGE("%s: Failed to create buffer state layer", __func__); return; } - createTransaction(mSurfaceControl) - .setLayer(mSurfaceControl, INT32_MAX - 2) - .setTrustedOverlay(mSurfaceControl, true) + createTransaction(mSurfaceControl->get()) + .setLayer(mSurfaceControl->get(), INT32_MAX - 2) + .setTrustedOverlay(mSurfaceControl->get(), true) .apply(); } @@ -233,7 +248,7 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& { if (!mSurfaceControl) return kNoBuffers; const auto transformHint = - static_cast<ui::Transform::RotationFlags>(mSurfaceControl->getTransformHint()); + static_cast<ui::Transform::RotationFlags>(mSurfaceControl->get()->getTransformHint()); // Tell SurfaceFlinger about the pre-rotation on the buffer. const auto transform = [&] { @@ -247,7 +262,9 @@ auto RefreshRateOverlay::getOrCreateBuffers(Fps fps) -> const Buffers& { } }(); - createTransaction(mSurfaceControl).setTransform(mSurfaceControl, transform).apply(); + createTransaction(mSurfaceControl->get()) + .setTransform(mSurfaceControl->get(), transform) + .apply(); BufferCache::const_iterator it = mBufferCache.find({fps.getIntValue(), transformHint}); if (it == mBufferCache.end()) { @@ -289,21 +306,21 @@ void RefreshRateOverlay::setViewport(ui::Size viewport) { Rect frame((3 * width) >> 4, height >> 5); frame.offsetBy(width >> 5, height >> 4); - createTransaction(mSurfaceControl) - .setMatrix(mSurfaceControl, frame.getWidth() / static_cast<float>(kBufferWidth), 0, 0, - frame.getHeight() / static_cast<float>(kBufferHeight)) - .setPosition(mSurfaceControl, frame.left, frame.top) + createTransaction(mSurfaceControl->get()) + .setMatrix(mSurfaceControl->get(), frame.getWidth() / static_cast<float>(kBufferWidth), + 0, 0, frame.getHeight() / static_cast<float>(kBufferHeight)) + .setPosition(mSurfaceControl->get(), frame.left, frame.top) .apply(); } void RefreshRateOverlay::setLayerStack(ui::LayerStack stack) { - createTransaction(mSurfaceControl).setLayerStack(mSurfaceControl, stack).apply(); + createTransaction(mSurfaceControl->get()).setLayerStack(mSurfaceControl->get(), stack).apply(); } void RefreshRateOverlay::changeRefreshRate(Fps fps) { mCurrentFps = fps; const auto buffer = getOrCreateBuffers(fps)[mFrame]; - createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply(); + createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply(); } void RefreshRateOverlay::animate() { @@ -312,7 +329,7 @@ void RefreshRateOverlay::animate() { const auto& buffers = getOrCreateBuffers(*mCurrentFps); mFrame = (mFrame + 1) % buffers.size(); const auto buffer = buffers[mFrame]; - createTransaction(mSurfaceControl).setBuffer(mSurfaceControl, buffer).apply(); + createTransaction(mSurfaceControl->get()).setBuffer(mSurfaceControl->get(), buffer).apply(); } } // namespace android diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h index a465a36747..a2966e654e 100644 --- a/services/surfaceflinger/RefreshRateOverlay.h +++ b/services/surfaceflinger/RefreshRateOverlay.h @@ -33,6 +33,20 @@ namespace android { class GraphicBuffer; class SurfaceControl; +class SurfaceFlinger; + +// Helper class to delete the SurfaceControl on a helper thread as +// SurfaceControl assumes its destruction happens without SurfaceFlinger::mStateLock held. +class SurfaceControlHolder { +public: + explicit SurfaceControlHolder(sp<SurfaceControl> sc) : mSurfaceControl(std::move(sc)){}; + ~SurfaceControlHolder(); + + const sp<SurfaceControl>& get() const { return mSurfaceControl; } + +private: + sp<SurfaceControl> mSurfaceControl; +}; class RefreshRateOverlay { public: @@ -75,7 +89,7 @@ private: const FpsRange mFpsRange; // For color interpolation. const bool mShowSpinner; - const sp<SurfaceControl> mSurfaceControl; + const std::unique_ptr<SurfaceControlHolder> mSurfaceControl; }; } // namespace android diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index 747032be0d..4af1f5c65f 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -188,10 +188,10 @@ void DispSyncSource::onVsyncCallback(nsecs_t vsyncTime, nsecs_t targetWakeupTime VSyncSource::VSyncData DispSyncSource::getLatestVSyncData() const { std::lock_guard lock(mVsyncMutex); - nsecs_t expectedPresentTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom( + nsecs_t expectedPresentationTime = mVSyncTracker.nextAnticipatedVSyncTimeFrom( systemTime() + mWorkDuration.get().count() + mReadyDuration.count()); - nsecs_t deadline = expectedPresentTime - mWorkDuration.get().count() - mReadyDuration.count(); - return {expectedPresentTime, deadline}; + nsecs_t deadline = expectedPresentationTime - mReadyDuration.count(); + return {expectedPresentationTime, deadline}; } void DispSyncSource::dump(std::string& result) const { diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp index ca8349636b..a48c921378 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp @@ -40,22 +40,26 @@ namespace { struct RefreshRateScore { DisplayModeIterator modeIt; - float score; + float overallScore; + struct { + float modeBelowThreshold; + float modeAboveThreshold; + } fixedRateBelowThresholdLayersScore; }; template <typename Iterator> const DisplayModePtr& getMaxScoreRefreshRate(Iterator begin, Iterator end) { const auto it = std::max_element(begin, end, [](RefreshRateScore max, RefreshRateScore current) { - const auto& [modeIt, score] = current; + const auto& [modeIt, overallScore, _] = current; std::string name = to_string(modeIt->second->getFps()); - ALOGV("%s scores %.2f", name.c_str(), score); + ALOGV("%s scores %.2f", name.c_str(), overallScore); - ATRACE_INT(name.c_str(), static_cast<int>(std::round(score * 100))); + ATRACE_INT(name.c_str(), static_cast<int>(std::round(overallScore * 100))); constexpr float kEpsilon = 0.0001f; - return score > max.score * (1 + kEpsilon); + return overallScore > max.overallScore * (1 + kEpsilon); }); return it->modeIt->second; @@ -151,31 +155,6 @@ std::pair<nsecs_t, nsecs_t> RefreshRateConfigs::getDisplayFrames(nsecs_t layerPe return {quotient, remainder}; } -bool RefreshRateConfigs::isVoteAllowed(const LayerRequirement& layer, Fps refreshRate) const { - using namespace fps_approx_ops; - - switch (layer.vote) { - case LayerVoteType::ExplicitExactOrMultiple: - case LayerVoteType::Heuristic: - if (mConfig.frameRateMultipleThreshold != 0 && - refreshRate >= Fps::fromValue(mConfig.frameRateMultipleThreshold) && - layer.desiredRefreshRate < Fps::fromValue(mConfig.frameRateMultipleThreshold / 2)) { - // Don't vote high refresh rates past the threshold for layers with a low desired - // refresh rate. For example, desired 24 fps with 120 Hz threshold means no vote for - // 120 Hz, but desired 60 fps should have a vote. - return false; - } - break; - case LayerVoteType::ExplicitDefault: - case LayerVoteType::ExplicitExact: - case LayerVoteType::Max: - case LayerVoteType::Min: - case LayerVoteType::NoVote: - break; - } - return true; -} - float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate) const { constexpr float kScoreForFractionalPairs = .8f; @@ -240,10 +219,6 @@ float RefreshRateConfigs::calculateNonExactMatchingLayerScoreLocked(const LayerR float RefreshRateConfigs::calculateLayerScoreLocked(const LayerRequirement& layer, Fps refreshRate, bool isSeamlessSwitch) const { - if (!isVoteAllowed(layer, refreshRate)) { - return 0; - } - // Slightly prefer seamless switches. constexpr float kSeamedSwitchPenalty = 0.95f; const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; @@ -300,6 +275,7 @@ auto RefreshRateConfigs::getBestRefreshRate(const std::vector<LayerRequirement>& auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequirement>& layers, GlobalSignals signals) const -> std::pair<DisplayModePtr, GlobalSignals> { + using namespace fps_approx_ops; ATRACE_CALL(); ALOGV("%s: %zu layers", __func__, layers.size()); @@ -409,7 +385,7 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire const auto weight = layer.weight; - for (auto& [modeIt, score] : scores) { + for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { const auto& [id, mode] = *modeIt; const bool isSeamlessSwitch = mode->getGroup() == mActiveModeIt->second->getGroup(); @@ -451,18 +427,92 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire continue; } - const auto layerScore = + const float layerScore = calculateLayerScoreLocked(layer, mode->getFps(), isSeamlessSwitch); - ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(), - to_string(mode->getFps()).c_str(), layerScore); + const float weightedLayerScore = weight * layerScore; + + // Layer with fixed source has a special consideration which depends on the + // mConfig.frameRateMultipleThreshold. We don't want these layers to score + // refresh rates above the threshold, but we also don't want to favor the lower + // ones by having a greater number of layers scoring them. Instead, we calculate + // the score independently for these layers and later decide which + // refresh rates to add it. For example, desired 24 fps with 120 Hz threshold should not + // score 120 Hz, but desired 60 fps should contribute to the score. + const bool fixedSourceLayer = [](LayerVoteType vote) { + switch (vote) { + case LayerVoteType::ExplicitExactOrMultiple: + case LayerVoteType::Heuristic: + return true; + case LayerVoteType::NoVote: + case LayerVoteType::Min: + case LayerVoteType::Max: + case LayerVoteType::ExplicitDefault: + case LayerVoteType::ExplicitExact: + return false; + } + }(layer.vote); + const bool layerBelowThreshold = mConfig.frameRateMultipleThreshold != 0 && + layer.desiredRefreshRate < + Fps::fromValue(mConfig.frameRateMultipleThreshold / 2); + if (fixedSourceLayer && layerBelowThreshold) { + const bool modeAboveThreshold = + mode->getFps() >= Fps::fromValue(mConfig.frameRateMultipleThreshold); + if (modeAboveThreshold) { + ALOGV("%s gives %s fixed source (above threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), + layerScore); + fixedRateBelowThresholdLayersScore.modeAboveThreshold += weightedLayerScore; + } else { + ALOGV("%s gives %s fixed source (below threshold) score of %.4f", + formatLayerInfo(layer, weight).c_str(), to_string(mode->getFps()).c_str(), + layerScore); + fixedRateBelowThresholdLayersScore.modeBelowThreshold += weightedLayerScore; + } + } else { + ALOGV("%s gives %s score of %.4f", formatLayerInfo(layer, weight).c_str(), + to_string(mode->getFps()).c_str(), layerScore); + overallScore += weightedLayerScore; + } + } + } + + // We want to find the best refresh rate without the fixed source layers, + // so we could know whether we should add the modeAboveThreshold scores or not. + // If the best refresh rate is already above the threshold, it means that + // some non-fixed source layers already scored it, so we can just add the score + // for all fixed source layers, even the ones that are above the threshold. + const bool maxScoreAboveThreshold = [&] { + if (mConfig.frameRateMultipleThreshold == 0 || scores.empty()) { + return false; + } + + const auto maxScoreIt = + std::max_element(scores.begin(), scores.end(), + [](RefreshRateScore max, RefreshRateScore current) { + const auto& [modeIt, overallScore, _] = current; + return overallScore > max.overallScore; + }); + ALOGV("%s is the best refresh rate without fixed source layers. It is %s the threshold for " + "refresh rate multiples", + to_string(maxScoreIt->modeIt->second->getFps()).c_str(), + maxScoreAboveThreshold ? "above" : "below"); + return maxScoreIt->modeIt->second->getFps() >= + Fps::fromValue(mConfig.frameRateMultipleThreshold); + }(); - score += weight * layerScore; + // Now we can add the fixed rate layers score + for (auto& [modeIt, overallScore, fixedRateBelowThresholdLayersScore] : scores) { + overallScore += fixedRateBelowThresholdLayersScore.modeBelowThreshold; + if (maxScoreAboveThreshold) { + overallScore += fixedRateBelowThresholdLayersScore.modeAboveThreshold; } + ALOGV("%s adjusted overallScore is %.4f", to_string(modeIt->second->getFps()).c_str(), + overallScore); } - // Now that we scored all the refresh rates we need to pick the one that got the highest score. - // In case of a tie we will pick the higher refresh rate if any of the layers wanted Max, - // or the lower otherwise. + // Now that we scored all the refresh rates we need to pick the one that got the highest + // overallScore. In case of a tie we will pick the higher refresh rate if any of the layers + // wanted Max, or the lower otherwise. const DisplayModePtr& bestRefreshRate = maxVoteLayers > 0 ? getMaxScoreRefreshRate(scores.rbegin(), scores.rend()) : getMaxScoreRefreshRate(scores.begin(), scores.end()); @@ -471,7 +521,7 @@ auto RefreshRateConfigs::getBestRefreshRateLocked(const std::vector<LayerRequire // If we never scored any layers, then choose the rate from the primary // range instead of picking a random score from the app range. if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.score == 0; })) { + [](RefreshRateScore score) { return score.overallScore == 0; })) { const DisplayModePtr& max = getMaxRefreshRateByPolicyLocked(anchorGroup); ALOGV("layers not scored - choose %s", to_string(max->getFps()).c_str()); return {max, kNoSignals}; @@ -575,7 +625,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr continue; } - for (auto& [_, score] : scores) { + for (auto& [_, score, _1] : scores) { score = 0; } @@ -587,7 +637,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr LOG_ALWAYS_FATAL_IF(layer->vote != LayerVoteType::ExplicitDefault && layer->vote != LayerVoteType::ExplicitExactOrMultiple && layer->vote != LayerVoteType::ExplicitExact); - for (auto& [modeIt, score] : scores) { + for (auto& [modeIt, score, _] : scores) { constexpr bool isSeamlessSwitch = true; const auto layerScore = calculateLayerScoreLocked(*layer, modeIt->second->getFps(), isSeamlessSwitch); @@ -605,7 +655,7 @@ RefreshRateConfigs::UidToFrameRateOverride RefreshRateConfigs::getFrameRateOverr // If we never scored any layers, we don't have a preferred frame rate if (std::all_of(scores.begin(), scores.end(), - [](RefreshRateScore score) { return score.score == 0; })) { + [](RefreshRateScore score) { return score.overallScore == 0; })) { continue; } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 05a8692f51..a79002e959 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -353,9 +353,6 @@ private: const Policy* getCurrentPolicyLocked() const REQUIRES(mLock); bool isPolicyValidLocked(const Policy& policy) const REQUIRES(mLock); - // Returns whether the layer is allowed to vote for the given refresh rate. - bool isVoteAllowed(const LayerRequirement&, Fps) const; - // calculates a score for a layer. Used to determine the display refresh rate // and the frame rate override for certains applications. float calculateLayerScoreLocked(const LayerRequirement&, Fps refreshRate, diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 37f0fec425..727cb0817e 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -25,6 +25,7 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> +#include <ftl/fake_guard.h> #include <gui/WindowInfo.h> #include <system/window.h> #include <ui/DisplayStatInfo.h> @@ -94,9 +95,13 @@ void Scheduler::startTimers() { } void Scheduler::setRefreshRateConfigs(std::shared_ptr<RefreshRateConfigs> configs) { + // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. { - // The current RefreshRateConfigs instance may outlive this call, so unbind its idle timer. - std::scoped_lock lock(mRefreshRateConfigsLock); + // mRefreshRateConfigsLock is not locked here to avoid the deadlock + // as the callback can attempt to acquire the lock before stopIdleTimer can finish + // the execution. It's safe to FakeGuard as main thread is the only thread that + // writes to the mRefreshRateConfigs. + ftl::FakeGuard guard(mRefreshRateConfigsLock); if (mRefreshRateConfigs) { mRefreshRateConfigs->stopIdleTimer(); mRefreshRateConfigs->clearIdleTimerCallbacks(); @@ -554,11 +559,12 @@ void Scheduler::onTouchHint() { } } -void Scheduler::setDisplayPowerState(bool normal) { +void Scheduler::setDisplayPowerMode(hal::PowerMode powerMode) { { std::lock_guard<std::mutex> lock(mPolicyLock); - mPolicy.isDisplayPowerStateNormal = normal; + mPolicy.displayPowerMode = powerMode; } + mVsyncSchedule->getController().setDisplayPowerMode(powerMode); if (mDisplayPowerTimer) { mDisplayPowerTimer->reset(); @@ -706,7 +712,8 @@ auto Scheduler::chooseDisplayMode() -> std::pair<DisplayModePtr, GlobalSignals> // If Display Power is not in normal operation we want to be in performance mode. When coming // back to normal mode, a grace period is given with DisplayPowerTimer. if (mDisplayPowerTimer && - (!mPolicy.isDisplayPowerStateNormal || mPolicy.displayPowerTimer == TimerState::Reset)) { + (mPolicy.displayPowerMode != hal::PowerMode::ON || + mPolicy.displayPowerTimer == TimerState::Reset)) { constexpr GlobalSignals kNoSignals; return {configs->getMaxRefreshRateByPolicy(), kNoSignals}; } diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index 0c72124119..a8043bf94c 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -186,7 +186,7 @@ public: // Indicates that touch interaction is taking place. void onTouchHint(); - void setDisplayPowerState(bool normal); + void setDisplayPowerMode(hal::PowerMode powerMode); VSyncDispatch& getVsyncDispatch() { return mVsyncSchedule->getDispatch(); } @@ -325,7 +325,7 @@ private: TimerState idleTimer = TimerState::Reset; TouchState touch = TouchState::Inactive; TimerState displayPowerTimer = TimerState::Expired; - bool isDisplayPowerStateNormal = true; + hal::PowerMode displayPowerMode = hal::PowerMode::ON; // Chosen display mode. DisplayModePtr mode; diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp index bdcab515f2..13cd304a5f 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp @@ -146,6 +146,11 @@ bool VSyncReactor::periodConfirmed(nsecs_t vsync_timestamp, std::optional<nsecs_ return false; } + if (mDisplayPowerMode == hal::PowerMode::DOZE || + mDisplayPowerMode == hal::PowerMode::DOZE_SUSPEND) { + return true; + } + if (!mLastHwVsync && !HwcVsyncPeriod) { return false; } @@ -206,6 +211,11 @@ bool VSyncReactor::addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> return mMoreSamplesNeeded; } +void VSyncReactor::setDisplayPowerMode(hal::PowerMode powerMode) { + std::scoped_lock lock(mMutex); + mDisplayPowerMode = powerMode; +} + void VSyncReactor::dump(std::string& result) const { std::lock_guard lock(mMutex); StringAppendF(&result, "VsyncReactor in use\n"); diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h index 6a1950ac77..4501487392 100644 --- a/services/surfaceflinger/Scheduler/VSyncReactor.h +++ b/services/surfaceflinger/Scheduler/VSyncReactor.h @@ -49,6 +49,8 @@ public: bool addHwVsyncTimestamp(nsecs_t timestamp, std::optional<nsecs_t> hwcVsyncPeriod, bool* periodFlushed) final; + void setDisplayPowerMode(hal::PowerMode powerMode) final; + void dump(std::string& result) const final; private: @@ -73,6 +75,8 @@ private: std::optional<nsecs_t> mPeriodTransitioningTo GUARDED_BY(mMutex); std::optional<nsecs_t> mLastHwVsync GUARDED_BY(mMutex); + hal::PowerMode mDisplayPowerMode GUARDED_BY(mMutex) = hal::PowerMode::ON; + const bool mSupportKernelIdleTimer = false; }; diff --git a/services/surfaceflinger/Scheduler/VsyncController.h b/services/surfaceflinger/Scheduler/VsyncController.h index 59f65372a9..726a420649 100644 --- a/services/surfaceflinger/Scheduler/VsyncController.h +++ b/services/surfaceflinger/Scheduler/VsyncController.h @@ -18,7 +18,10 @@ #include <cstddef> #include <memory> +#include <mutex> +#include <DisplayHardware/HWComposer.h> +#include <DisplayHardware/Hal.h> #include <ui/FenceTime.h> #include <utils/Mutex.h> #include <utils/RefBase.h> @@ -70,6 +73,13 @@ public: */ virtual void setIgnorePresentFences(bool ignore) = 0; + /* + * Sets the primary display power mode to the controller. + * + * \param [in] powerMode + */ + virtual void setDisplayPowerMode(hal::PowerMode powerMode) = 0; + virtual void dump(std::string& result) const = 0; protected: diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 6a17cd8881..0e1acb4154 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -444,6 +444,11 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI } mIgnoreHdrCameraLayers = ignore_hdr_camera_layers(false); + + // Power hint session mode, representing which hint(s) to send: early, late, or both) + mPowerHintSessionMode = + {.late = base::GetBoolProperty("debug.sf.send_late_power_session_hint"s, true), + .early = base::GetBoolProperty("debug.sf.send_early_power_session_hint"s, false)}; } LatchUnsignaledConfig SurfaceFlinger::getLatchUnsignaledConfig() { @@ -1991,12 +1996,6 @@ nsecs_t SurfaceFlinger::calculateExpectedPresentTime(DisplayStatInfo stats) cons bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expectedVsyncTime) FTL_FAKE_GUARD(kMainThreadContext) { - // we set this once at the beginning of commit to ensure consistency throughout the whole frame - mPowerHintSessionData.sessionEnabled = mPowerAdvisor->usePowerHintSession(); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.commitStart = systemTime(); - } - // calculate the expected present time once and use the cached // value throughout this frame to make sure all layers are // seeing this same value. @@ -2010,10 +2009,6 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected const nsecs_t lastScheduledPresentTime = mScheduledPresentTime; mScheduledPresentTime = expectedVsyncTime; - if (mPowerHintSessionData.sessionEnabled) { - mPowerAdvisor->setTargetWorkDuration(mExpectedPresentTime - - mPowerHintSessionData.commitStart); - } const auto vsyncIn = [&] { if (!ATRACE_ENABLED()) return 0.f; return (mExpectedPresentTime - systemTime()) / 1e6f; @@ -2089,6 +2084,32 @@ bool SurfaceFlinger::commit(nsecs_t frameTime, int64_t vsyncId, nsecs_t expected } } + // Save this once per commit + composite to ensure consistency + // TODO (b/240619471): consider removing active display check once AOD is fixed + const auto activeDisplay = + FTL_FAKE_GUARD(mStateLock, getDisplayDeviceLocked(mActiveDisplayToken)); + mPowerHintSessionEnabled = mPowerAdvisor->usePowerHintSession() && activeDisplay && + activeDisplay->getPowerMode() == hal::PowerMode::ON; + if (mPowerHintSessionEnabled) { + const auto& display = FTL_FAKE_GUARD(mStateLock, getDefaultDisplayDeviceLocked()).get(); + // get stable vsync period from display mode + const nsecs_t vsyncPeriod = display->getActiveMode()->getVsyncPeriod(); + mPowerAdvisor->setCommitStart(frameTime); + mPowerAdvisor->setExpectedPresentTime(mExpectedPresentTime); + const nsecs_t idealSfWorkDuration = + mVsyncModulator->getVsyncConfig().sfWorkDuration.count(); + // Frame delay is how long we should have minus how long we actually have + mPowerAdvisor->setFrameDelay(idealSfWorkDuration - (mExpectedPresentTime - frameTime)); + mPowerAdvisor->setTotalFrameTargetWorkDuration(idealSfWorkDuration); + mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); + + // Send early hint here to make sure there's not another frame pending + if (mPowerHintSessionMode.early) { + // Send a rough prediction for this frame based on last frame's timing info + mPowerAdvisor->sendPredictedWorkDuration(); + } + } + if (mTracingEnabledChanged) { mLayerTracingEnabled = mLayerTracing.isEnabled(); mTracingEnabledChanged = false; @@ -2165,16 +2186,15 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) FTL_FAKE_GUARD(kMainThreadContext) { ATRACE_FORMAT("%s %" PRId64, __func__, vsyncId); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.compositeStart = systemTime(); - } - compositionengine::CompositionRefreshArgs refreshArgs; const auto& displays = FTL_FAKE_GUARD(mStateLock, mDisplays); refreshArgs.outputs.reserve(displays.size()); + std::vector<DisplayId> displayIds; for (const auto& [_, display] : displays) { refreshArgs.outputs.push_back(display->getCompositionDisplay()); + displayIds.push_back(display->getId()); } + mPowerAdvisor->setDisplays(displayIds); mDrawingState.traverseInZOrder([&refreshArgs](Layer* layer) { if (auto layerFE = layer->getCompositionEngineLayerFE()) refreshArgs.layers.push_back(layerFE); @@ -2222,12 +2242,17 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) mCompositionEngine->present(refreshArgs); - if (mPowerHintSessionData.sessionEnabled) { - mPowerHintSessionData.presentEnd = systemTime(); - } - mTimeStats->recordFrameDuration(frameTime, systemTime()); + // Send a power hint hint after presentation is finished + if (mPowerHintSessionEnabled) { + mPowerAdvisor->setSfPresentTiming(mPreviousPresentFences[0].fenceTime->getSignalTime(), + systemTime()); + if (mPowerHintSessionMode.late) { + mPowerAdvisor->sendActualWorkDuration(); + } + } + if (mScheduler->onPostComposition(presentTime)) { scheduleComposite(FrameHint::kNone); } @@ -2272,11 +2297,8 @@ void SurfaceFlinger::composite(nsecs_t frameTime, int64_t vsyncId) scheduleCommit(FrameHint::kNone); } - // calculate total render time for performance hinting if adpf cpu hint is enabled, - if (mPowerHintSessionData.sessionEnabled) { - const nsecs_t flingerDuration = - (mPowerHintSessionData.presentEnd - mPowerHintSessionData.commitStart); - mPowerAdvisor->sendActualWorkDuration(flingerDuration, mPowerHintSessionData.presentEnd); + if (mPowerHintSessionEnabled) { + mPowerAdvisor->setCompositeEnd(systemTime()); } } @@ -2867,7 +2889,8 @@ sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( ALOGV("Display Orientation: %s", toCString(creationArgs.physicalOrientation)); // virtual displays are always considered enabled - creationArgs.initialPowerMode = state.isVirtual() ? hal::PowerMode::ON : hal::PowerMode::OFF; + creationArgs.initialPowerMode = + state.isVirtual() ? std::make_optional(hal::PowerMode::ON) : std::nullopt; sp<DisplayDevice> display = getFactory().createDisplayDevice(creationArgs); @@ -3310,11 +3333,11 @@ void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos, mDrawingState.traverseInReverseZOrder([&](Layer* layer) { if (!layer->needsInputInfo()) return; - // Do not create WindowInfos for windows on displays that cannot receive input. - if (const auto opt = displayInputInfos.get(layer->getLayerStack())) { - const auto& info = opt->get(); - outWindowInfos.push_back(layer->fillInputInfo(info.transform, info.isSecure)); - } + const auto opt = displayInputInfos.get(layer->getLayerStack(), + [](const auto& info) -> Layer::InputDisplayArgs { + return {&info.transform, info.isSecure}; + }); + outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{}))); }); sNumWindowInfos = outWindowInfos.size(); @@ -4848,8 +4871,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: const auto displayId = display->getPhysicalId(); ALOGD("Setting power mode %d on display %s", mode, to_string(displayId).c_str()); - const hal::PowerMode currentMode = display->getPowerMode(); - if (mode == currentMode) { + std::optional<hal::PowerMode> currentMode = display->getPowerMode(); + if (currentMode.has_value() && mode == *currentMode) { return; } @@ -4865,7 +4888,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: mInterceptor->savePowerModeUpdate(display->getSequenceId(), static_cast<int32_t>(mode)); } const auto refreshRate = display->refreshRateConfigs().getActiveMode()->getFps(); - if (currentMode == hal::PowerMode::OFF) { + if (*currentMode == hal::PowerMode::OFF) { // Turn on the display if (display->isInternal() && (!activeDisplay || !activeDisplay->isPoweredOn())) { onActiveDisplayChangedLocked(display); @@ -4896,7 +4919,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (SurfaceFlinger::setSchedAttr(false) != NO_ERROR) { ALOGW("Couldn't set uclamp.min on display off: %s\n", strerror(errno)); } - if (isDisplayActiveLocked(display) && currentMode != hal::PowerMode::DOZE_SUSPEND) { + if (isDisplayActiveLocked(display) && *currentMode != hal::PowerMode::DOZE_SUSPEND) { mScheduler->disableHardwareVsync(true); mScheduler->onScreenReleased(mAppConnectionHandle); } @@ -4910,7 +4933,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: } else if (mode == hal::PowerMode::DOZE || mode == hal::PowerMode::ON) { // Update display while dozing getHwComposer().setPowerMode(displayId, mode); - if (isDisplayActiveLocked(display) && currentMode == hal::PowerMode::DOZE_SUSPEND) { + if (isDisplayActiveLocked(display) && *currentMode == hal::PowerMode::DOZE_SUSPEND) { mScheduler->onScreenAcquired(mAppConnectionHandle); mScheduler->resyncToHardwareVsync(true, refreshRate); } @@ -4929,7 +4952,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, hal: if (isDisplayActiveLocked(display)) { mTimeStats->setPowerMode(mode); mRefreshRateStats->setPowerMode(mode); - mScheduler->setDisplayPowerState(mode == hal::PowerMode::ON); + mScheduler->setDisplayPowerMode(mode); } ALOGD("Finished setting power mode %d on display %s", mode, to_string(displayId).c_str()); @@ -4982,6 +5005,25 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { const auto flag = args.empty() ? ""s : std::string(String8(args[0])); + // Traversal of drawing state must happen on the main thread. + // Otherwise, SortedVector may have shared ownership during concurrent + // traversals, which can result in use-after-frees. + std::string compositionLayers; + mScheduler + ->schedule([&] { + StringAppendF(&compositionLayers, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto* compositionState = layer->getCompositionState(); + if (!compositionState || !compositionState->isVisible) return; + + android::base::StringAppendF(&compositionLayers, "* Layer %p (%s)\n", layer, + layer->getDebugName() ? layer->getDebugName() + : "<unknown>"); + compositionState->dump(compositionLayers); + }); + }) + .get(); + bool dumpLayers = true; { TimedLock lock(mStateLock, s2ns(1), __func__); @@ -4994,7 +5036,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { (it->second)(args, asProto, result); dumpLayers = false; } else if (!asProto) { - dumpAllLocked(args, result); + dumpAllLocked(args, compositionLayers, result); } } @@ -5293,7 +5335,8 @@ void SurfaceFlinger::dumpOffscreenLayers(std::string& result) { result.append(future.get()); } -void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) const { +void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, + std::string& result) const { const bool colorize = !args.empty() && args[0] == String16("--color"); Colorizer colorizer(colorize); @@ -5344,18 +5387,7 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co StringAppendF(&result, "Visible layers (count = %zu)\n", mNumLayers.load()); colorizer.reset(result); - { - StringAppendF(&result, "Composition layers\n"); - mDrawingState.traverseInZOrder([&](Layer* layer) { - auto* compositionState = layer->getCompositionState(); - if (!compositionState || !compositionState->isVisible) return; - - android::base::StringAppendF(&result, "* Layer %p (%s)\n", layer, - layer->getDebugName() ? layer->getDebugName() - : "<unknown>"); - compositionState->dump(result); - }); - } + result.append(compositionLayers); colorizer.bold(result); StringAppendF(&result, "Displays (%zu entries)\n", mDisplays.size()); @@ -6620,8 +6652,13 @@ ftl::SharedFuture<FenceResult> SurfaceFlinger::captureScreenCommon( 1 /* layerCount */, usage, "screenshot"); const status_t bufferStatus = buffer->initCheck(); - LOG_ALWAYS_FATAL_IF(bufferStatus != OK, "captureScreenCommon: Buffer failed to allocate: %d", - bufferStatus); + if (bufferStatus != OK) { + // Animations may end up being really janky, but don't crash here. + // Otherwise an irreponsible process may cause an SF crash by allocating + // too much. + ALOGE("%s: Buffer failed to allocate: %d", __func__, bufferStatus); + return ftl::yield<FenceResult>(base::unexpected(bufferStatus)).share(); + } const std::shared_ptr<renderengine::ExternalTexture> texture = std::make_shared< renderengine::impl::ExternalTexture>(buffer, getRenderEngine(), renderengine::impl::ExternalTexture::Usage:: @@ -6895,9 +6932,7 @@ status_t SurfaceFlinger::setDesiredDisplayModeSpecsInternal( return NO_ERROR; } - status_t setPolicyResult = overridePolicy - ? display->refreshRateConfigs().setOverridePolicy(policy) - : display->refreshRateConfigs().setDisplayManagerPolicy(*policy); + const status_t setPolicyResult = display->setRefreshRatePolicy(policy, overridePolicy); if (setPolicyResult < 0) { return BAD_VALUE; } diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index f14c755c2b..9e0cee8fd4 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -1085,7 +1085,8 @@ private: /* * Debugging & dumpsys */ - void dumpAllLocked(const DumpArgs& args, std::string& result) const REQUIRES(mStateLock); + void dumpAllLocked(const DumpArgs& args, const std::string& compositionLayers, + std::string& result) const REQUIRES(mStateLock); void appendSfConfigString(std::string& result) const; void listLayersLocked(std::string& result) const; @@ -1435,12 +1436,12 @@ private: return mScheduler->getLayerFramerate(now, id); } + bool mPowerHintSessionEnabled; + struct { - bool sessionEnabled = false; - nsecs_t commitStart; - nsecs_t compositeStart; - nsecs_t presentEnd; - } mPowerHintSessionData GUARDED_BY(kMainThreadContext); + bool late = false; + bool early = false; + } mPowerHintSessionMode; nsecs_t mAnimationTransactionTimeout = s2ns(5); diff --git a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp index 9ab35d741a..53de4a6e56 100644 --- a/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp +++ b/services/surfaceflinger/tests/unittests/AidlPowerHalWrapperTest.cpp @@ -52,6 +52,8 @@ protected: void verifyAndClearExpectations(); void sendActualWorkDurationGroup(std::vector<WorkDuration> durations, std::chrono::nanoseconds sleepBeforeLastSend); + std::chrono::nanoseconds mAllowedDeviation; + std::chrono::nanoseconds mStaleTimeout; }; void AidlPowerHalWrapperTest::SetUp() { @@ -59,6 +61,9 @@ void AidlPowerHalWrapperTest::SetUp() { mMockSession = new NiceMock<MockIPowerHintSession>(); ON_CALL(*mMockHal.get(), getHintSessionPreferredRate(_)).WillByDefault(Return(Status::ok())); mWrapper = std::make_unique<AidlPowerHalWrapper>(mMockHal); + mWrapper->setAllowedActualDeviation(std::chrono::nanoseconds{10ms}.count()); + mAllowedDeviation = std::chrono::nanoseconds{mWrapper->mAllowedActualDeviation}; + mStaleTimeout = AidlPowerHalWrapper::kStaleTimeout; } void AidlPowerHalWrapperTest::verifyAndClearExpectations() { @@ -76,6 +81,7 @@ void AidlPowerHalWrapperTest::sendActualWorkDurationGroup( mWrapper->sendActualWorkDuration(duration.durationNanos, duration.timeStampNanos); } } + WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t timeStampNanos) { WorkDuration duration; duration.durationNanos = durationNanos.count(); @@ -83,6 +89,10 @@ WorkDuration toWorkDuration(std::chrono::nanoseconds durationNanos, int64_t time return duration; } +WorkDuration toWorkDuration(std::pair<std::chrono::nanoseconds, nsecs_t> timePair) { + return toWorkDuration(timePair.first, timePair.second); +} + std::string printWorkDurations(const ::std::vector<WorkDuration>& durations) { std::ostringstream os; for (auto duration : durations) { @@ -112,7 +122,7 @@ TEST_F(AidlPowerHalWrapperTest, startPowerHintSession) { EXPECT_FALSE(mWrapper->startPowerHintSession()); } -TEST_F(AidlPowerHalWrapperTest, restartNewPoserHintSessionWithNewThreadIds) { +TEST_F(AidlPowerHalWrapperTest, restartNewPowerHintSessionWithNewThreadIds) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); std::vector<int32_t> threadIds = {1, 2}; @@ -149,12 +159,8 @@ TEST_F(AidlPowerHalWrapperTest, setTargetWorkDuration) { std::chrono::nanoseconds base = 100ms; // test cases with target work duration and whether it should update hint against baseline 100ms - const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = {{0ms, false}, - {-1ms, false}, - {200ms, true}, - {2ms, true}, - {91ms, false}, - {109ms, false}}; + const std::vector<std::pair<std::chrono::nanoseconds, bool>> testCases = + {{0ms, true}, {-1ms, true}, {200ms, true}, {2ms, true}, {100ms, false}, {109ms, true}}; for (const auto& test : testCases) { // reset to 100ms baseline @@ -200,21 +206,21 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { // 100ms const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> testCases = {{{{-1ms, 100}}, false}, - {{{91ms, 100}}, false}, - {{{109ms, 100}}, false}, + {{{100ms - (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation / 2), 100}}, false}, + {{{100ms + (mAllowedDeviation + 1ms), 100}}, true}, + {{{100ms - (mAllowedDeviation + 1ms), 100}}, true}, {{{100ms, 100}, {200ms, 200}}, true}, {{{100ms, 500}, {100ms, 600}, {3ms, 600}}, true}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); auto raw = test.first; std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) .Times(test.second ? 1 : 0); sendActualWorkDurationGroup(durations, 0ms); @@ -222,40 +228,6 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration) { } } -TEST_F(AidlPowerHalWrapperTest, sendAdjustedActualWorkDuration) { - ASSERT_TRUE(mWrapper->supportsPowerHintSession()); - - std::vector<int32_t> threadIds = {1, 2}; - mWrapper->setPowerHintSessionThreadIds(threadIds); - EXPECT_CALL(*mMockHal.get(), createHintSession(_, _, threadIds, _, _)) - .WillOnce(DoAll(SetArgPointee<4>(mMockSession), Return(Status::ok()))); - ASSERT_TRUE(mWrapper->startPowerHintSession()); - verifyAndClearExpectations(); - - std::chrono::nanoseconds lastTarget = 100ms; - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(lastTarget.count())).Times(1); - mWrapper->setTargetWorkDuration(lastTarget.count()); - std::chrono::nanoseconds newTarget = 105ms; - mWrapper->setTargetWorkDuration(newTarget.count()); - EXPECT_CALL(*mMockSession.get(), updateTargetWorkDuration(newTarget.count())).Times(0); - std::chrono::nanoseconds actual = 21ms; - // 100 / 105 * 21ms = 20ms - std::chrono::nanoseconds expectedActualSent = 20ms; - std::vector<WorkDuration> expectedDurations = {toWorkDuration(expectedActualSent, 1)}; - - EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(_)) - .WillOnce(DoAll( - [expectedDurations](const ::std::vector<WorkDuration>& durationsSent) { - EXPECT_EQ(expectedDurations, durationsSent) - << base::StringPrintf("actual sent: %s vs expected: %s", - printWorkDurations(durationsSent).c_str(), - printWorkDurations(expectedDurations) - .c_str()); - }, - Return(Status::ok()))); - mWrapper->sendActualWorkDuration(actual.count(), 1); -} - TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { ASSERT_TRUE(mWrapper->supportsPowerHintSession()); @@ -269,22 +241,23 @@ TEST_F(AidlPowerHalWrapperTest, sendActualWorkDuration_exceedsStaleTime) { auto base = toWorkDuration(100ms, 0); // test cases with actual work durations and whether it should update hint against baseline // 100ms - const std::vector<std::pair<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, bool>> - testCases = {{{{91ms, 100}}, true}, {{{109ms, 100}}, true}}; + const std::vector<std::tuple<std::vector<std::pair<std::chrono::nanoseconds, nsecs_t>>, + std::chrono::nanoseconds, bool>> + testCases = {{{{100ms, 100}}, mStaleTimeout, true}, + {{{100ms + (mAllowedDeviation / 2), 100}}, mStaleTimeout, true}, + {{{100ms, 100}}, mStaleTimeout / 2, false}}; for (const auto& test : testCases) { // reset actual duration - sendActualWorkDurationGroup({base}, 80ms); + sendActualWorkDurationGroup({base}, mStaleTimeout); - auto raw = test.first; + auto raw = std::get<0>(test); std::vector<WorkDuration> durations(raw.size()); std::transform(raw.begin(), raw.end(), durations.begin(), - [](std::pair<std::chrono::nanoseconds, nsecs_t> d) { - return toWorkDuration(d.first, d.second); - }); + [](auto d) { return toWorkDuration(d); }); EXPECT_CALL(*mMockSession.get(), reportActualWorkDuration(durations)) - .Times(test.second ? 1 : 0); - sendActualWorkDurationGroup(durations, 80ms); + .Times(std::get<2>(test) ? 1 : 0); + sendActualWorkDurationGroup(durations, std::get<1>(test)); verifyAndClearExpectations(); } } diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index 7823363e1e..004f31c562 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -24,6 +24,7 @@ package { filegroup { name: "libsurfaceflinger_mock_sources", srcs: [ + "mock/DisplayHardware/MockAidlPowerHalWrapper.cpp", "mock/DisplayHardware/MockComposer.cpp", "mock/DisplayHardware/MockHWC2.cpp", "mock/DisplayHardware/MockIPower.cpp", @@ -95,6 +96,7 @@ cc_test { "LayerTest.cpp", "LayerTestUtils.cpp", "MessageQueueTest.cpp", + "PowerAdvisorTest.cpp", "SurfaceFlinger_CreateDisplayTest.cpp", "SurfaceFlinger_DestroyDisplayTest.cpp", "SurfaceFlinger_DisplayModeSwitching.cpp", diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp index ec27edac6e..67ace1af83 100644 --- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -292,9 +292,10 @@ TEST_F(DispSyncSourceTest, waitForCallbacksWithDurationChange) { TEST_F(DispSyncSourceTest, getLatestVsyncData) { const nsecs_t now = systemTime(); - const nsecs_t vsyncInternalDuration = mWorkDuration.count() + mReadyDuration.count(); + const nsecs_t expectedPresentationTime = + now + mWorkDuration.count() + mReadyDuration.count() + 1; EXPECT_CALL(*mVSyncTracker, nextAnticipatedVSyncTimeFrom(_)) - .WillOnce(Return(now + vsyncInternalDuration + 1)); + .WillOnce(Return(expectedPresentationTime)); { InSequence seq; EXPECT_CALL(*mVSyncDispatch, registerCallback(_, mName)).Times(1); @@ -306,10 +307,8 @@ TEST_F(DispSyncSourceTest, getLatestVsyncData) { EXPECT_TRUE(mDispSyncSource); const auto vsyncData = mDispSyncSource->getLatestVSyncData(); - ASSERT_GT(vsyncData.deadlineTimestamp, now); - ASSERT_GT(vsyncData.expectedPresentationTime, vsyncData.deadlineTimestamp); - EXPECT_EQ(vsyncData.deadlineTimestamp, - vsyncData.expectedPresentationTime - vsyncInternalDuration); + ASSERT_EQ(vsyncData.expectedPresentationTime, expectedPresentationTime); + EXPECT_EQ(vsyncData.deadlineTimestamp, expectedPresentationTime - mReadyDuration.count()); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp new file mode 100644 index 0000000000..8711a42b2b --- /dev/null +++ b/services/surfaceflinger/tests/unittests/PowerAdvisorTest.cpp @@ -0,0 +1,203 @@ +/* + * Copyright 2022 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. + */ + +#undef LOG_TAG +#define LOG_TAG "PowerAdvisorTest" + +#include <DisplayHardware/PowerAdvisor.h> +#include <compositionengine/Display.h> +#include <ftl/fake_guard.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> +#include <ui/DisplayId.h> +#include <chrono> +#include "TestableSurfaceFlinger.h" +#include "mock/DisplayHardware/MockAidlPowerHalWrapper.h" + +using namespace android; +using namespace android::Hwc2::mock; +using namespace android::hardware::power; +using namespace std::chrono_literals; +using namespace testing; + +namespace android::Hwc2::impl { + +class PowerAdvisorTest : public testing::Test { +public: + void SetUp() override; + void startPowerHintSession(); + void fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod); + void setExpectedTiming(nsecs_t startTime, nsecs_t vsyncPeriod); + nsecs_t getFenceWaitDelayDuration(bool skipValidate); + +protected: + TestableSurfaceFlinger mFlinger; + std::unique_ptr<PowerAdvisor> mPowerAdvisor; + NiceMock<MockAidlPowerHalWrapper>* mMockAidlWrapper; + nsecs_t kErrorMargin = std::chrono::nanoseconds(1ms).count(); +}; + +void PowerAdvisorTest::SetUp() FTL_FAKE_GUARD(mPowerAdvisor->mPowerHalMutex) { + std::unique_ptr<MockAidlPowerHalWrapper> mockAidlWrapper = + std::make_unique<NiceMock<MockAidlPowerHalWrapper>>(); + mPowerAdvisor = std::make_unique<PowerAdvisor>(*mFlinger.flinger()); + ON_CALL(*mockAidlWrapper.get(), supportsPowerHintSession()).WillByDefault(Return(true)); + ON_CALL(*mockAidlWrapper.get(), startPowerHintSession()).WillByDefault(Return(true)); + mPowerAdvisor->mHalWrapper = std::move(mockAidlWrapper); + mMockAidlWrapper = + reinterpret_cast<NiceMock<MockAidlPowerHalWrapper>*>(mPowerAdvisor->mHalWrapper.get()); +} + +void PowerAdvisorTest::startPowerHintSession() { + const std::vector<int32_t> threadIds = {1, 2, 3}; + mPowerAdvisor->enablePowerHint(true); + mPowerAdvisor->startPowerHintSession(threadIds); +} + +void PowerAdvisorTest::setExpectedTiming(nsecs_t totalFrameTarget, nsecs_t expectedPresentTime) { + mPowerAdvisor->setTotalFrameTargetWorkDuration(totalFrameTarget); + mPowerAdvisor->setExpectedPresentTime(expectedPresentTime); +} + +void PowerAdvisorTest::fakeBasicFrameTiming(nsecs_t startTime, nsecs_t vsyncPeriod) { + mPowerAdvisor->setCommitStart(startTime); + mPowerAdvisor->setFrameDelay(0); + mPowerAdvisor->setTargetWorkDuration(vsyncPeriod); +} + +nsecs_t PowerAdvisorTest::getFenceWaitDelayDuration(bool skipValidate) { + return (skipValidate ? PowerAdvisor::kFenceWaitStartDelaySkippedValidate + : PowerAdvisor::kFenceWaitStartDelayValidated) + .count(); +} + +namespace { + +TEST_F(PowerAdvisorTest, hintSessionUseHwcDisplay) { + mPowerAdvisor->onBootFinished(); + startPowerHintSession(); + + std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)}; + + // 60hz + const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; + const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count(); + const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); + + nsecs_t startTime = 100; + + // advisor only starts on frame 2 so do an initial no-op frame + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); + mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration); + + // increment the frame + startTime += vsyncPeriod; + + const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration; + EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); + + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000); + mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); + mPowerAdvisor->sendActualWorkDuration(); +} + +TEST_F(PowerAdvisorTest, hintSessionSubtractsHwcFenceTime) { + mPowerAdvisor->onBootFinished(); + startPowerHintSession(); + + std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u)}; + + // 60hz + const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; + const nsecs_t presentDuration = std::chrono::nanoseconds(5ms).count(); + const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); + const nsecs_t hwcBlockedDuration = std::chrono::nanoseconds(500us).count(); + + nsecs_t startTime = 100; + + // advisor only starts on frame 2 so do an initial no-op frame + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); + mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration); + + // increment the frame + startTime += vsyncPeriod; + + const nsecs_t expectedDuration = kErrorMargin + presentDuration + + getFenceWaitDelayDuration(false) - hwcBlockedDuration + postCompDuration; + EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); + + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 3000000); + // now report the fence as having fired during the display HWC time + mPowerAdvisor->setSfPresentTiming(startTime + 2000000 + hwcBlockedDuration, + startTime + presentDuration); + mPowerAdvisor->sendActualWorkDuration(); +} + +TEST_F(PowerAdvisorTest, hintSessionUsingSecondaryVirtualDisplays) { + mPowerAdvisor->onBootFinished(); + startPowerHintSession(); + + std::vector<DisplayId> displayIds{PhysicalDisplayId::fromPort(42u), GpuVirtualDisplayId(0), + GpuVirtualDisplayId(1)}; + + // 60hz + const nsecs_t vsyncPeriod = std::chrono::nanoseconds(1s).count() / 60; + // make present duration much later than the hwc display by itself will account for + const nsecs_t presentDuration = std::chrono::nanoseconds(10ms).count(); + const nsecs_t postCompDuration = std::chrono::nanoseconds(1ms).count(); + + nsecs_t startTime = 100; + + // advisor only starts on frame 2 so do an initial no-op frame + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); + mPowerAdvisor->setCompositeEnd(startTime + presentDuration + postCompDuration); + + // increment the frame + startTime += vsyncPeriod; + + const nsecs_t expectedDuration = kErrorMargin + presentDuration + postCompDuration; + EXPECT_CALL(*mMockAidlWrapper, sendActualWorkDuration(Eq(expectedDuration), _)).Times(1); + + fakeBasicFrameTiming(startTime, vsyncPeriod); + setExpectedTiming(vsyncPeriod, startTime + vsyncPeriod); + mPowerAdvisor->setDisplays(displayIds); + + // don't report timing for the gpu displays since they don't use hwc + mPowerAdvisor->setHwcValidateTiming(displayIds[0], startTime + 1000000, startTime + 1500000); + mPowerAdvisor->setHwcPresentTiming(displayIds[0], startTime + 2000000, startTime + 2500000); + mPowerAdvisor->setSfPresentTiming(startTime, startTime + presentDuration); + mPowerAdvisor->sendActualWorkDuration(); +} + +} // namespace +} // namespace android::Hwc2::impl diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp index fcde532b85..188fd58dea 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp @@ -564,9 +564,10 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu TestableRefreshRateConfigs configs(kModes_30_60_72_90_120, kModeId60, {.frameRateMultipleThreshold = 120}); - std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}, {.weight = 1.f}}; auto& lr1 = layers[0]; auto& lr2 = layers[1]; + auto& lr3 = layers[2]; lr1.desiredRefreshRate = 24_Hz; lr1.vote = LayerVoteType::ExplicitDefault; @@ -639,6 +640,48 @@ TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60_90_120_DifferentTypes_mu lr2.vote = LayerVoteType::ExplicitExactOrMultiple; lr2.name = "90Hz ExplicitExactOrMultiple"; EXPECT_EQ(kMode90, configs.getBestRefreshRate(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.vote = LayerVoteType::Max; + lr2.name = "Max"; + EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.name = "120Hz ExplicitDefault"; + EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + + lr1.desiredRefreshRate = 24_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "24Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::ExplicitExact; + lr2.name = "120Hz ExplicitExact"; + EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + + lr1.desiredRefreshRate = 10_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 120_Hz; + lr2.vote = LayerVoteType::Heuristic; + lr2.name = "120Hz ExplicitExact"; + EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); + + lr1.desiredRefreshRate = 30_Hz; + lr1.vote = LayerVoteType::ExplicitExactOrMultiple; + lr1.name = "30Hz ExplicitExactOrMultiple"; + lr2.desiredRefreshRate = 30_Hz; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.name = "30Hz ExplicitExactOrMultiple"; + lr3.vote = LayerVoteType::Heuristic; + lr3.desiredRefreshRate = 120_Hz; + lr3.name = "120Hz Heuristic"; + EXPECT_EQ(kMode120, configs.getBestRefreshRate(layers)); } TEST_F(RefreshRateConfigsTest, getBestRefreshRate_30_60) { diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index aab27957c3..93c809e2fd 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -159,8 +159,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentIsNoopWhenModeSwitchingIsNotSup mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); ASSERT_EQ(0u, mScheduler->getNumActiveLayers()); - constexpr bool kPowerStateNormal = true; - mScheduler->setDisplayPowerState(kPowerStateNormal); + constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; + mScheduler->setDisplayPowerMode(kPowerModeOn); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); @@ -226,8 +226,8 @@ TEST_F(SchedulerTest, chooseRefreshRateForContentSelectsMaxRefreshRate) { mScheduler->recordLayerHistory(layer.get(), 0, LayerHistory::LayerUpdateType::Buffer); - constexpr bool kPowerStateNormal = true; - mScheduler->setDisplayPowerState(kPowerStateNormal); + constexpr hal::PowerMode kPowerModeOn = hal::PowerMode::ON; + mScheduler->setDisplayPowerMode(kPowerModeOn); constexpr uint32_t kDisplayArea = 999'999; mScheduler->onActiveDisplayAreaChanged(kDisplayArea); diff --git a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp index 8de9e4be29..2c9888dd8e 100644 --- a/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp +++ b/services/surfaceflinger/tests/unittests/SurfaceFlinger_PowerHintTest.cpp @@ -74,6 +74,7 @@ void SurfaceFlingerPowerHintTest::SetUp() { mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats)); mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer)); + mFlinger.setPowerHintSessionMode(true, true); mFlinger.setupPowerAdvisor(std::unique_ptr<Hwc2::PowerAdvisor>(mPowerAdvisor)); static constexpr bool kIsPrimary = true; FakeHwcDisplayInjector(DEFAULT_DISPLAY_ID, hal::DisplayType::PHYSICAL, kIsPrimary) @@ -96,6 +97,7 @@ void SurfaceFlingerPowerHintTest::SetUp() { .setNativeWindow(mNativeWindow) .setPowerMode(hal::PowerMode::ON) .inject(); + mFlinger.mutableActiveDisplayToken() = mDisplay->getDisplayToken(); } void SurfaceFlingerPowerHintTest::setupScheduler() { @@ -142,10 +144,30 @@ TEST_F(SurfaceFlingerPowerHintTest, sendDurationsIncludingHwcWaitTime) { std::this_thread::sleep_for(mockHwcRunTime); return hardware::graphics::composer::V2_1::Error::NONE; }); - EXPECT_CALL(*mPowerAdvisor, - sendActualWorkDuration(Gt(mockHwcRunTime.count()), - Gt(now + mockHwcRunTime.count()))) + EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(1); + static constexpr bool kVsyncId = 123; // arbitrary + mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); +} + +TEST_F(SurfaceFlingerPowerHintTest, inactiveOnDisplayDoze) { + ON_CALL(*mPowerAdvisor, usePowerHintSession()).WillByDefault(Return(true)); + + mDisplay->setPowerMode(hal::PowerMode::DOZE); + + const std::chrono::nanoseconds mockVsyncPeriod = 15ms; + EXPECT_CALL(*mPowerAdvisor, setTargetWorkDuration(_)).Times(0); + + const nsecs_t now = systemTime(); + const std::chrono::nanoseconds mockHwcRunTime = 20ms; + EXPECT_CALL(*mDisplaySurface, + prepareFrame(compositionengine::DisplaySurface::CompositionType::Hwc)) .Times(1); + EXPECT_CALL(*mComposer, presentOrValidateDisplay(HWC_DISPLAY, _, _, _, _, _)) + .WillOnce([mockHwcRunTime] { + std::this_thread::sleep_for(mockHwcRunTime); + return hardware::graphics::composer::V2_1::Error::NONE; + }); + EXPECT_CALL(*mPowerAdvisor, sendActualWorkDuration()).Times(0); static constexpr bool kVsyncId = 123; // arbitrary mFlinger.commitAndComposite(now, kVsyncId, now + mockVsyncPeriod.count()); } diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index f1a69fb46e..283f9ca77b 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -330,6 +330,10 @@ public: layer->mDrawingParent = drawingParent; } + void setPowerHintSessionMode(bool early, bool late) { + mFlinger->mPowerHintSessionMode = {.late = late, .early = early}; + } + /* ------------------------------------------------------------------------ * Forwarding for functions being tested */ @@ -741,6 +745,7 @@ public: mHwcDisplayId(hwcDisplayId) { mCreationArgs.connectionType = connectionType; mCreationArgs.isPrimary = isPrimary; + mCreationArgs.initialPowerMode = hal::PowerMode::ON; } sp<IBinder> token() const { return mDisplayToken; } diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 4eb90558ab..30a3f9a4a7 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -349,6 +349,23 @@ TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) { } } +TEST_F(VSyncReactorTest, addHwVsyncTimestampDozePreempt) { + bool periodFlushed = false; + nsecs_t const newPeriod = 4000; + + mReactor.startPeriodTransition(newPeriod); + + auto time = 0; + // If the power mode is not DOZE or DOZE_SUSPEND, it is still collecting timestamps. + EXPECT_TRUE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); + EXPECT_FALSE(periodFlushed); + + // Set power mode to DOZE to trigger period flushing. + mReactor.setDisplayPowerMode(hal::PowerMode::DOZE); + EXPECT_FALSE(mReactor.addHwVsyncTimestamp(time, std::nullopt, &periodFlushed)); + EXPECT_TRUE(periodFlushed); +} + TEST_F(VSyncReactorTest, addPresentFenceWhileAwaitingPeriodConfirmationRequestsHwVsync) { auto time = 0; bool periodFlushed = false; diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp new file mode 100644 index 0000000000..5049b1d367 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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 "MockAidlPowerHalWrapper.h" +#include "MockIPower.h" + +namespace android::Hwc2::mock { + +MockAidlPowerHalWrapper::MockAidlPowerHalWrapper() + : AidlPowerHalWrapper(sp<testing::NiceMock<MockIPower>>::make()){}; +MockAidlPowerHalWrapper::~MockAidlPowerHalWrapper() = default; + +} // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h new file mode 100644 index 0000000000..657ced3d08 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockAidlPowerHalWrapper.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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 <gmock/gmock.h> + +#include "DisplayHardware/PowerAdvisor.h" + +namespace android { +namespace hardware { +namespace power { +class IPower; +} +} // namespace hardware +} // namespace android + +namespace android::Hwc2::mock { + +class MockAidlPowerHalWrapper : public Hwc2::impl::AidlPowerHalWrapper { +public: + MockAidlPowerHalWrapper(); + ~MockAidlPowerHalWrapper() override; + MOCK_METHOD(bool, setExpensiveRendering, (bool enabled), (override)); + MOCK_METHOD(bool, notifyDisplayUpdateImminent, (), (override)); + MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); + MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); + MOCK_METHOD(void, restartPowerHintSession, (), (override)); + MOCK_METHOD(void, setPowerHintSessionThreadIds, (const std::vector<int32_t>& threadIds), + (override)); + MOCK_METHOD(bool, startPowerHintSession, (), (override)); + MOCK_METHOD(void, setTargetWorkDuration, (nsecs_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (nsecs_t actualDuration, nsecs_t timestamp), + (override)); + MOCK_METHOD(bool, shouldReconnectHAL, (), (override)); +}; + +} // namespace android::Hwc2::mock
\ No newline at end of file diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index c598cbc28e..aede250db5 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -36,11 +36,33 @@ public: MOCK_METHOD(bool, usePowerHintSession, (), (override)); MOCK_METHOD(bool, supportsPowerHintSession, (), (override)); MOCK_METHOD(bool, isPowerHintSessionRunning, (), (override)); - MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDurationNanos), (override)); - MOCK_METHOD(void, sendActualWorkDuration, (int64_t actualDurationNanos, nsecs_t timestamp), - (override)); + MOCK_METHOD(void, setTargetWorkDuration, (int64_t targetDuration), (override)); + MOCK_METHOD(void, sendActualWorkDuration, (), (override)); + MOCK_METHOD(void, sendPredictedWorkDuration, (), (override)); MOCK_METHOD(void, enablePowerHint, (bool enabled), (override)); MOCK_METHOD(bool, startPowerHintSession, (const std::vector<int32_t>& threadIds), (override)); + MOCK_METHOD(void, setGpuFenceTime, + (DisplayId displayId, std::unique_ptr<FenceTime>&& fenceTime), (override)); + MOCK_METHOD(void, setHwcValidateTiming, + (DisplayId displayId, nsecs_t valiateStartTime, nsecs_t validateEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentTiming, + (DisplayId displayId, nsecs_t presentStartTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setSkippedValidate, (DisplayId displayId, bool skipped), (override)); + MOCK_METHOD(void, setRequiresClientComposition, + (DisplayId displayId, bool requiresClientComposition), (override)); + MOCK_METHOD(void, setExpectedPresentTime, (nsecs_t expectedPresentTime), (override)); + MOCK_METHOD(void, setSfPresentTiming, (nsecs_t presentFenceTime, nsecs_t presentEndTime), + (override)); + MOCK_METHOD(void, setHwcPresentDelayedTime, + (DisplayId displayId, + std::chrono::steady_clock::time_point earliestFrameStartTime)); + MOCK_METHOD(void, setFrameDelay, (nsecs_t frameDelayDuration), (override)); + MOCK_METHOD(void, setCommitStart, (nsecs_t commitStartTime), (override)); + MOCK_METHOD(void, setCompositeEnd, (nsecs_t compositeEndtime), (override)); + MOCK_METHOD(void, setDisplays, (std::vector<DisplayId> & displayIds), (override)); + MOCK_METHOD(void, setTotalFrameTargetWorkDuration, (int64_t targetDuration), (override)); }; } // namespace android::Hwc2::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h index 314f681545..4ef91dacb2 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncController.h @@ -31,6 +31,7 @@ public: MOCK_METHOD3(addHwVsyncTimestamp, bool(nsecs_t, std::optional<nsecs_t>, bool*)); MOCK_METHOD1(startPeriodTransition, void(nsecs_t)); MOCK_METHOD1(setIgnorePresentFences, void(bool)); + MOCK_METHOD(void, setDisplayPowerMode, (hal::PowerMode), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; |