diff options
175 files changed, 3823 insertions, 1140 deletions
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index c40caf56d8..c86adef118 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -353,7 +353,7 @@ static int otapreopt_chroot(const int argc, char **arg) { // Now go on and read dexopt lines from stdin and pass them on to otapreopt. int count = 1; - for (std::array<char, 1000> linebuf; + for (std::array<char, 10000> linebuf; std::cin.clear(), std::cin.getline(&linebuf[0], linebuf.size()); ++count) { // Subtract one from gcount() since getline() counts the newline. std::string line(&linebuf[0], std::cin.gcount() - 1); diff --git a/data/etc/android.software.opengles.deqp.level-latest.xml b/data/etc/android.software.opengles.deqp.level-latest.xml index bd15eb6eb2..62bb10161a 100644 --- a/data/etc/android.software.opengles.deqp.level-latest.xml +++ b/data/etc/android.software.opengles.deqp.level-latest.xml @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes OpenGL ES dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.opengles.deqp.level" version="132580097" /> + <feature name="android.software.opengles.deqp.level" version="132645633" /> </permissions> diff --git a/data/etc/android.software.vulkan.deqp.level-latest.xml b/data/etc/android.software.vulkan.deqp.level-latest.xml index 87be0709d6..0fc12b3b5f 100644 --- a/data/etc/android.software.vulkan.deqp.level-latest.xml +++ b/data/etc/android.software.vulkan.deqp.level-latest.xml @@ -17,5 +17,5 @@ <!-- This is the standard feature indicating that the device passes Vulkan dEQP tests associated with the most recent level for this Android version. --> <permissions> - <feature name="android.software.vulkan.deqp.level" version="132580097" /> + <feature name="android.software.vulkan.deqp.level" version="132645633" /> </permissions> diff --git a/include/input/InputEventBuilders.h b/include/input/InputEventBuilders.h index 9c0c10e603..2d23b97386 100644 --- a/include/input/InputEventBuilders.h +++ b/include/input/InputEventBuilders.h @@ -160,4 +160,90 @@ private: std::vector<PointerBuilder> mPointers; }; +class KeyEventBuilder { +public: + KeyEventBuilder(int32_t action, int32_t source) { + mAction = action; + mSource = source; + mEventTime = systemTime(SYSTEM_TIME_MONOTONIC); + mDownTime = mEventTime; + } + + KeyEventBuilder(const KeyEvent& event) { + mAction = event.getAction(); + mDeviceId = event.getDeviceId(); + mSource = event.getSource(); + mDownTime = event.getDownTime(); + mEventTime = event.getEventTime(); + mDisplayId = event.getDisplayId(); + mFlags = event.getFlags(); + mKeyCode = event.getKeyCode(); + mScanCode = event.getScanCode(); + mMetaState = event.getMetaState(); + mRepeatCount = event.getRepeatCount(); + } + + KeyEventBuilder& deviceId(int32_t deviceId) { + mDeviceId = deviceId; + return *this; + } + + KeyEventBuilder& downTime(nsecs_t downTime) { + mDownTime = downTime; + return *this; + } + + KeyEventBuilder& eventTime(nsecs_t eventTime) { + mEventTime = eventTime; + return *this; + } + + KeyEventBuilder& displayId(int32_t displayId) { + mDisplayId = displayId; + return *this; + } + + KeyEventBuilder& policyFlags(int32_t policyFlags) { + mPolicyFlags = policyFlags; + return *this; + } + + KeyEventBuilder& addFlag(uint32_t flags) { + mFlags |= flags; + return *this; + } + + KeyEventBuilder& keyCode(int32_t keyCode) { + mKeyCode = keyCode; + return *this; + } + + KeyEventBuilder& repeatCount(int32_t repeatCount) { + mRepeatCount = repeatCount; + return *this; + } + + KeyEvent build() const { + KeyEvent event{}; + event.initialize(InputEvent::nextId(), mDeviceId, mSource, mDisplayId, INVALID_HMAC, + mAction, mFlags, mKeyCode, mScanCode, mMetaState, mRepeatCount, mDownTime, + mEventTime); + return event; + } + +private: + int32_t mAction; + int32_t mDeviceId = DEFAULT_DEVICE_ID; + uint32_t mSource; + nsecs_t mDownTime; + nsecs_t mEventTime; + int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS; + int32_t mFlags{0}; + int32_t mKeyCode{AKEYCODE_UNKNOWN}; + int32_t mScanCode{0}; + int32_t mMetaState{AMETA_NONE}; + int32_t mRepeatCount{0}; +}; + } // namespace android diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 2e9949578d..ee7445544b 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -98,7 +98,7 @@ public: void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); // Adds movement information for all pointers in a MotionEvent, including historical samples. - void addMovement(const MotionEvent* event); + void addMovement(const MotionEvent& event); // Returns the velocity of the specified pointer id and axis in position units per second. // Returns empty optional if there is insufficient movement information for the pointer, or if diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp index f22e90a03f..824323857d 100644 --- a/libs/binder/Binder.cpp +++ b/libs/binder/Binder.cpp @@ -30,7 +30,6 @@ #include <binder/RecordedTransaction.h> #include <binder/RpcServer.h> #include <pthread.h> -#include <utils/misc.h> #include <inttypes.h> #include <stdio.h> @@ -271,7 +270,7 @@ public: bool mInheritRt = false; // for below objects - Mutex mLock; + RpcMutex mLock; std::set<sp<RpcServerLink>> mRpcServerLinks; BpBinder::ObjectManager mObjects; @@ -307,7 +306,7 @@ status_t BBinder::startRecordingTransactions(const Parcel& data) { return PERMISSION_DENIED; } Extras* e = getOrCreateExtras(); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { LOG(INFO) << "Could not start Binder recording. Another is already in progress."; return INVALID_OPERATION; @@ -337,7 +336,7 @@ status_t BBinder::stopRecordingTransactions() { return PERMISSION_DENIED; } Extras* e = getOrCreateExtras(); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { e->mRecordingFd.reset(); mRecordingOn = false; @@ -405,7 +404,7 @@ status_t BBinder::transact( if (kEnableKernelIpc && mRecordingOn && code != START_RECORDING_TRANSACTION) [[unlikely]] { Extras* e = mExtras.load(std::memory_order_acquire); - AutoMutex lock(e->mLock); + RpcMutexUniqueLock lock(e->mLock); if (mRecordingOn) { Parcel emptyReply; timespec ts; @@ -452,7 +451,7 @@ void* BBinder::attachObject(const void* objectID, void* object, void* cleanupCoo Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.attach(objectID, object, cleanupCookie, func); } @@ -461,7 +460,7 @@ void* BBinder::findObject(const void* objectID) const Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return nullptr; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.find(objectID); } @@ -469,7 +468,7 @@ void* BBinder::detachObject(const void* objectID) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return nullptr; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.detach(objectID); } @@ -477,7 +476,7 @@ void BBinder::withLock(const std::function<void()>& doWithLock) { Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); doWithLock(); } @@ -485,7 +484,7 @@ sp<IBinder> BBinder::lookupOrCreateWeak(const void* objectID, object_make_func m const void* makeArgs) { Extras* e = getOrCreateExtras(); LOG_ALWAYS_FATAL_IF(!e, "no memory"); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); return e->mObjects.lookupOrCreateWeak(objectID, make, makeArgs); } @@ -692,7 +691,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, auto weakThis = wp<BBinder>::fromExisting(this); Extras* e = getOrCreateExtras(); - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); auto rpcServer = RpcServer::make(); LOG_ALWAYS_FATAL_IF(rpcServer == nullptr, "RpcServer::make returns null"); auto link = sp<RpcServerLink>::make(rpcServer, keepAliveBinder, weakThis); @@ -716,7 +715,7 @@ status_t BBinder::setRpcClientDebug(android::base::unique_fd socketFd, void BBinder::removeRpcServerLink(const sp<RpcServerLink>& link) { Extras* e = mExtras.load(std::memory_order_acquire); if (!e) return; - AutoMutex _l(e->mLock); + RpcMutexUniqueLock _l(e->mLock); (void)e->mRpcServerLinks.erase(link); } diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index 3bc4f929e7..49038b1974 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -23,7 +23,6 @@ #include <binder/IResultReceiver.h> #include <binder/RpcSession.h> #include <binder/Stability.h> -#include <utils/Log.h> #include <stdio.h> @@ -38,7 +37,7 @@ namespace android { // --------------------------------------------------------------------------- -Mutex BpBinder::sTrackingLock; +RpcMutex BpBinder::sTrackingLock; std::unordered_map<int32_t, uint32_t> BpBinder::sTrackingMap; std::unordered_map<int32_t, uint32_t> BpBinder::sLastLimitCallbackMap; int BpBinder::sNumTrackedUids = 0; @@ -163,7 +162,7 @@ sp<BpBinder> BpBinder::create(int32_t handle) { int32_t trackedUid = -1; if (sCountByUidEnabled) { trackedUid = IPCThreadState::self()->getCallingUid(); - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[trackedUid]; if (trackedValue & LIMIT_REACHED_MASK) [[unlikely]] { if (sBinderProxyThrottleCreate) { @@ -276,7 +275,7 @@ std::optional<int32_t> BpBinder::getDebugBinderHandle() const { } bool BpBinder::isDescriptorCached() const { - Mutex::Autolock _l(mLock); + RpcMutexUniqueLock _l(mLock); return mDescriptorCache.c_str() != kDescriptorUninit.c_str(); } @@ -292,7 +291,7 @@ const String16& BpBinder::getInterfaceDescriptor() const status_t err = thiz->transact(INTERFACE_TRANSACTION, data, &reply); if (err == NO_ERROR) { String16 res(reply.readString16()); - Mutex::Autolock _l(mLock); + RpcMutexUniqueLock _l(mLock); // mDescriptorCache could have been assigned while the lock was // released. if (mDescriptorCache.c_str() == kDescriptorUninit.c_str()) mDescriptorCache = res; @@ -385,7 +384,7 @@ status_t BpBinder::transact( status = IPCThreadState::self()->transact(binderHandle(), code, data, reply, flags); } if (data.dataSize() > LOG_TRANSACTIONS_OVER_SIZE) { - Mutex::Autolock _l(mLock); + RpcMutexUniqueLock _l(mLock); ALOGW("Large outgoing transaction of %zu bytes, interface descriptor %s, code %d", data.dataSize(), String8(mDescriptorCache).c_str(), code); } @@ -431,7 +430,7 @@ status_t BpBinder::linkToDeath( "linkToDeath(): recipient must be non-NULL"); { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); if (!mObitsSent) { if (!mObituaries) { @@ -467,7 +466,7 @@ status_t BpBinder::unlinkToDeath( return INVALID_OPERATION; } - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); if (mObitsSent) { return DEAD_OBJECT; @@ -555,30 +554,30 @@ void BpBinder::reportOneDeath(const Obituary& obit) void* BpBinder::attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); ALOGV("Attaching object %p to binder %p (manager=%p)", object, this, &mObjects); return mObjects.attach(objectID, object, cleanupCookie, func); } void* BpBinder::findObject(const void* objectID) const { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.find(objectID); } void* BpBinder::detachObject(const void* objectID) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.detach(objectID); } void BpBinder::withLock(const std::function<void()>& doWithLock) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); doWithLock(); } sp<IBinder> BpBinder::lookupOrCreateWeak(const void* objectID, object_make_func make, const void* makeArgs) { - AutoMutex _l(mLock); + RpcMutexUniqueLock _l(mLock); return mObjects.lookupOrCreateWeak(objectID, make, makeArgs); } @@ -602,7 +601,7 @@ BpBinder::~BpBinder() { IPCThreadState* ipc = IPCThreadState::self(); if (mTrackedUid >= 0) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uint32_t trackedValue = sTrackingMap[mTrackedUid]; if ((trackedValue & COUNTING_VALUE_MASK) == 0) [[unlikely]] { ALOGE("Unexpected Binder Proxy tracking decrement in %p handle %d\n", this, @@ -702,7 +701,7 @@ bool BpBinder::onIncStrongAttempted(uint32_t /*flags*/, const void* /*id*/) uint32_t BpBinder::getBinderProxyCount(uint32_t uid) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); auto it = sTrackingMap.find(uid); if (it != sTrackingMap.end()) { return it->second & COUNTING_VALUE_MASK; @@ -717,7 +716,7 @@ uint32_t BpBinder::getBinderProxyCount() void BpBinder::getCountByUid(Vector<uint32_t>& uids, Vector<uint32_t>& counts) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); uids.setCapacity(sTrackingMap.size()); counts.setCapacity(sTrackingMap.size()); for (const auto& it : sTrackingMap) { @@ -731,12 +730,12 @@ void BpBinder::disableCountByUid() { sCountByUidEnabled.store(false); } void BpBinder::setCountByUidEnabled(bool enable) { sCountByUidEnabled.store(enable); } void BpBinder::setLimitCallback(binder_proxy_limit_callback cb) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); sLimitCallback = cb; } void BpBinder::setBinderProxyCountWatermarks(int high, int low) { - AutoMutex _l(sTrackingLock); + RpcMutexUniqueLock _l(sTrackingLock); sBinderProxyCountHighWatermark = high; sBinderProxyCountLowWatermark = low; } diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp index c6e4fb378d..7ae616e2b0 100644 --- a/libs/binder/Debug.cpp +++ b/libs/binder/Debug.cpp @@ -19,8 +19,6 @@ #include <binder/ProcessState.h> -#include <utils/misc.h> - #include <stdio.h> #include <stdlib.h> #include <ctype.h> diff --git a/libs/binder/FdTrigger.cpp b/libs/binder/FdTrigger.cpp index 8ee6cb0615..a1fbbf321c 100644 --- a/libs/binder/FdTrigger.cpp +++ b/libs/binder/FdTrigger.cpp @@ -21,12 +21,15 @@ #include <poll.h> -#include <android-base/macros.h> -#include <android-base/scopeguard.h> +#include <binder/Functional.h> #include "RpcState.h" +#include "Utils.h" + namespace android { +using namespace android::binder::impl; + std::unique_ptr<FdTrigger> FdTrigger::make() { auto ret = std::make_unique<FdTrigger>(); #ifndef BINDER_RPC_SINGLE_THREADED @@ -74,10 +77,9 @@ status_t FdTrigger::triggerablePoll(const android::RpcTransportFd& transportFd, "Only one thread should be polling on Fd!"); transportFd.setPollingState(true); - auto pollingStateGuard = - android::base::make_scope_guard([&]() { transportFd.setPollingState(false); }); + auto pollingStateGuard = make_scope_guard([&]() { transportFd.setPollingState(false); }); - int ret = TEMP_FAILURE_RETRY(poll(pfd, arraysize(pfd), -1)); + int ret = TEMP_FAILURE_RETRY(poll(pfd, countof(pfd), -1)); if (ret < 0) { return -errno; } diff --git a/libs/binder/FdTrigger.h b/libs/binder/FdTrigger.h index 5fbf2908ad..dba1dc9f6d 100644 --- a/libs/binder/FdTrigger.h +++ b/libs/binder/FdTrigger.h @@ -17,7 +17,6 @@ #include <memory> -#include <android-base/result.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp index 2780bd4cd9..dea26038cf 100644 --- a/libs/binder/IInterface.cpp +++ b/libs/binder/IInterface.cpp @@ -15,7 +15,6 @@ */ #define LOG_TAG "IInterface" -#include <utils/Log.h> #include <binder/IInterface.h> namespace android { diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index da58251149..9341eff91e 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -22,7 +22,6 @@ #include <binder/BpBinder.h> #include <binder/TextOutput.h> -#include <android-base/macros.h> #include <cutils/sched_policy.h> #include <utils/CallStack.h> #include <utils/Log.h> @@ -395,7 +394,9 @@ void IPCThreadState::restoreGetCallingSpGuard(const SpGuard* guard) { } void IPCThreadState::checkContextIsBinderForUse(const char* use) const { - if (LIKELY(mServingStackPointerGuard == nullptr)) return; + if (mServingStackPointerGuard == nullptr) [[likely]] { + return; + } if (!mServingStackPointer || mServingStackPointerGuard->address < mServingStackPointer) { LOG_ALWAYS_FATAL("In context %s, %s does not make sense (binder sp: %p, guard: %p).", @@ -832,7 +833,7 @@ status_t IPCThreadState::transact(int32_t handle, } if ((flags & TF_ONE_WAY) == 0) { - if (UNLIKELY(mCallRestriction != ProcessState::CallRestriction::NONE)) { + if (mCallRestriction != ProcessState::CallRestriction::NONE) [[unlikely]] { if (mCallRestriction == ProcessState::CallRestriction::ERROR_IF_NOT_ONEWAY) { ALOGE("Process making non-oneway call (code: %u) but is restricted.", code); CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), @@ -842,13 +843,13 @@ status_t IPCThreadState::transact(int32_t handle, } } - #if 0 +#if 0 if (code == 4) { // relayout ALOGI(">>>>>> CALLING transaction 4"); } else { ALOGI(">>>>>> CALLING transaction %d", code); } - #endif +#endif if (reply) { err = waitForResponse(reply); } else { diff --git a/libs/binder/IResultReceiver.cpp b/libs/binder/IResultReceiver.cpp index cd92217f84..60ece7269c 100644 --- a/libs/binder/IResultReceiver.cpp +++ b/libs/binder/IResultReceiver.cpp @@ -18,7 +18,6 @@ #include <binder/IResultReceiver.h> -#include <utils/Log.h> #include <binder/Parcel.h> #include <utils/String8.h> diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 6034f2b4ca..fe566fccb2 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -200,7 +200,7 @@ bool checkCallingPermission(const String16& permission, int32_t* outPid, int32_t } bool checkPermission(const String16& permission, pid_t pid, uid_t uid, bool logPermissionFailure) { - static Mutex gPermissionControllerLock; + static std::mutex gPermissionControllerLock; static sp<IPermissionController> gPermissionController; sp<IPermissionController> pc; diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index 5b1cb7ea56..95bdbb4b65 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -155,7 +155,7 @@ private: void dump_l(String8& res, const char* what) const; static const int kMemoryAlign; - mutable Mutex mLock; + mutable std::mutex mLock; LinkedList<chunk_t> mList; size_t mHeapSize; }; @@ -305,14 +305,14 @@ size_t SimpleBestFitAllocator::size() const size_t SimpleBestFitAllocator::allocate(size_t size, uint32_t flags) { - Mutex::Autolock _l(mLock); + std::unique_lock<std::mutex> _l(mLock); ssize_t offset = alloc(size, flags); return offset; } status_t SimpleBestFitAllocator::deallocate(size_t offset) { - Mutex::Autolock _l(mLock); + std::unique_lock<std::mutex> _l(mLock); chunk_t const * const freed = dealloc(offset); if (freed) { return NO_ERROR; @@ -420,7 +420,7 @@ SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) void SimpleBestFitAllocator::dump(const char* what) const { - Mutex::Autolock _l(mLock); + std::unique_lock<std::mutex> _l(mLock); dump_l(what); } @@ -434,7 +434,7 @@ void SimpleBestFitAllocator::dump_l(const char* what) const void SimpleBestFitAllocator::dump(String8& result, const char* what) const { - Mutex::Autolock _l(mLock); + std::unique_lock<std::mutex> _l(mLock); dump_l(result, what); } diff --git a/libs/binder/OS.h b/libs/binder/OS.h index 8dc1f6ae70..bb7caa951b 100644 --- a/libs/binder/OS.h +++ b/libs/binder/OS.h @@ -18,14 +18,13 @@ #include <stddef.h> #include <cstdint> -#include <android-base/result.h> #include <android-base/unique_fd.h> #include <binder/RpcTransport.h> #include <utils/Errors.h> namespace android::binder::os { -android::base::Result<void> setNonBlocking(android::base::borrowed_fd fd); +status_t setNonBlocking(android::base::borrowed_fd fd); status_t getRandomBytes(uint8_t* data, size_t size); diff --git a/libs/binder/OS_unix_base.cpp b/libs/binder/OS_unix_base.cpp index 81933d5c6b..a3cf117326 100644 --- a/libs/binder/OS_unix_base.cpp +++ b/libs/binder/OS_unix_base.cpp @@ -15,29 +15,29 @@ */ #include "OS.h" +#include "Utils.h" #include <android-base/file.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> #include <string.h> -using android::base::ErrnoError; -using android::base::Result; - namespace android::binder::os { // Linux kernel supports up to 253 (from SCM_MAX_FD) for unix sockets. constexpr size_t kMaxFdsPerMsg = 253; -Result<void> setNonBlocking(android::base::borrowed_fd fd) { +status_t setNonBlocking(android::base::borrowed_fd fd) { int flags = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_GETFL)); if (flags == -1) { - return ErrnoError() << "Could not get flags for fd"; + PLOGE("Failed setNonBlocking: Could not get flags for fd"); + return -errno; } if (int ret = TEMP_FAILURE_RETRY(fcntl(fd.get(), F_SETFL, flags | O_NONBLOCK)); ret == -1) { - return ErrnoError() << "Could not set non-blocking flag for fd"; + PLOGE("Failed setNonBlocking: Could not set non-blocking flag for fd"); + return -errno; } - return {}; + return OK; } status_t getRandomBytes(uint8_t* data, size_t size) { diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a3ff7d258b..334940298f 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "Parcel" //#define LOG_NDEBUG 0 +#include <endian.h> #include <errno.h> #include <fcntl.h> #include <inttypes.h> @@ -32,6 +33,7 @@ #include <binder/Binder.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/Parcel.h> #include <binder/ProcessState.h> @@ -39,15 +41,11 @@ #include <binder/Status.h> #include <binder/TextOutput.h> -#include <android-base/scopeguard.h> #ifndef BINDER_DISABLE_BLOB #include <cutils/ashmem.h> #endif -#include <utils/Flattenable.h> -#include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> -#include <utils/misc.h> #include "OS.h" #include "RpcState.h" @@ -98,6 +96,8 @@ static size_t pad_size(size_t s) { namespace android { +using namespace android::binder::impl; + // many things compile this into prebuilts on the stack #ifdef __LP64__ static_assert(sizeof(Parcel) == 120); @@ -627,7 +627,7 @@ status_t Parcel::appendFrom(const Parcel* parcel, size_t offset, size_t len) { } const size_t savedDataPos = mDataPos; - base::ScopeGuard scopeGuard = [&]() { mDataPos = savedDataPos; }; + auto scopeGuard = make_scope_guard([&]() { mDataPos = savedDataPos; }); rpcFields->mObjectPositions.reserve(otherRpcFields->mObjectPositions.size()); if (otherRpcFields->mFds != nullptr) { diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 8ec4af9945..0344eb04d6 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -18,10 +18,9 @@ #include <binder/ProcessState.h> -#include <android-base/result.h> -#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/Stability.h> @@ -32,6 +31,7 @@ #include <utils/Thread.h> #include "Static.h" +#include "Utils.h" #include "binder_module.h" #include <errno.h> @@ -60,6 +60,8 @@ const char* kDefaultDriver = "/dev/binder"; namespace android { +using namespace android::binder::impl; + class PoolThread : public Thread { public: @@ -189,7 +191,7 @@ void ProcessState::childPostFork() { void ProcessState::startThreadPool() { - AutoMutex _l(mLock); + std::unique_lock<std::mutex> _l(mLock); if (!mThreadPoolStarted) { if (mMaxThreads == 0) { // see also getThreadPoolMaxTotalThreadCount @@ -203,7 +205,7 @@ void ProcessState::startThreadPool() bool ProcessState::becomeContextManager() { - AutoMutex _l(mLock); + std::unique_lock<std::mutex> _l(mLock); flat_binder_object obj { .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, @@ -310,7 +312,7 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) { sp<IBinder> result; - AutoMutex _l(mLock); + std::unique_lock<std::mutex> _l(mLock); if (handle == 0 && the_context_object != nullptr) return the_context_object; @@ -374,7 +376,7 @@ sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle) void ProcessState::expungeHandle(int32_t handle, IBinder* binder) { - AutoMutex _l(mLock); + std::unique_lock<std::mutex> _l(mLock); handle_entry* e = lookupHandleLocked(handle); @@ -430,7 +432,7 @@ status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { size_t ProcessState::getThreadPoolMaxTotalThreadCount() const { pthread_mutex_lock(&mThreadCountLock); - base::ScopeGuard detachGuard = [&]() { pthread_mutex_unlock(&mThreadCountLock); }; + auto detachGuard = make_scope_guard([&]() { pthread_mutex_unlock(&mThreadCountLock); }); if (mThreadPoolStarted) { LOG_ALWAYS_FATAL_IF(mKernelStartedThreads > mMaxThreads + 1, @@ -512,31 +514,31 @@ String8 ProcessState::getDriverName() { return mDriverName; } -static base::Result<int> open_driver(const char* driver) { - int fd = open(driver, O_RDWR | O_CLOEXEC); - if (fd < 0) { - return base::ErrnoError() << "Opening '" << driver << "' failed"; +static base::unique_fd open_driver(const char* driver) { + auto fd = base::unique_fd(open(driver, O_RDWR | O_CLOEXEC)); + if (!fd.ok()) { + PLOGE("Opening '%s' failed", driver); + return {}; } int vers = 0; - status_t result = ioctl(fd, BINDER_VERSION, &vers); + int result = ioctl(fd.get(), BINDER_VERSION, &vers); if (result == -1) { - close(fd); - return base::ErrnoError() << "Binder ioctl to obtain version failed"; + PLOGE("Binder ioctl to obtain version failed"); + return {}; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { - close(fd); - return base::Error() << "Binder driver protocol(" << vers - << ") does not match user space protocol(" - << BINDER_CURRENT_PROTOCOL_VERSION - << ")! ioctl() return value: " << result; + ALOGE("Binder driver protocol(%d) does not match user space protocol(%d)! " + "ioctl() return value: %d", + vers, BINDER_CURRENT_PROTOCOL_VERSION, result); + return {}; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; - result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); + result = ioctl(fd.get(), BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } uint32_t enable = DEFAULT_ENABLE_ONEWAY_SPAM_DETECTION; - result = ioctl(fd, BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); + result = ioctl(fd.get(), BINDER_ENABLE_ONEWAY_SPAM_DETECTION, &enable); if (result == -1) { ALOGE_IF(ProcessState::isDriverFeatureEnabled( ProcessState::DriverFeature::ONEWAY_SPAM_DETECTION), @@ -561,28 +563,27 @@ ProcessState::ProcessState(const char* driver) mThreadPoolStarted(false), mThreadPoolSeq(1), mCallRestriction(CallRestriction::NONE) { - base::Result<int> opened = open_driver(driver); + base::unique_fd opened = open_driver(driver); if (opened.ok()) { // mmap the binder, providing a chunk of virtual address space to receive transactions. mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, - opened.value(), 0); + opened.get(), 0); if (mVMStart == MAP_FAILED) { - close(opened.value()); // *sigh* - opened = base::Error() - << "Using " << driver << " failed: unable to mmap transaction memory."; + ALOGE("Using %s failed: unable to mmap transaction memory.", driver); + opened.reset(); mDriverName.clear(); } } #ifdef __ANDROID__ - LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating: %s", - driver, opened.error().message().c_str()); + LOG_ALWAYS_FATAL_IF(!opened.ok(), "Binder driver '%s' could not be opened. Terminating.", + driver); #endif if (opened.ok()) { - mDriverFD = opened.value(); + mDriverFD = opened.release(); } } diff --git a/libs/binder/RecordedTransaction.cpp b/libs/binder/RecordedTransaction.cpp index 324670633f..cedd3af289 100644 --- a/libs/binder/RecordedTransaction.cpp +++ b/libs/binder/RecordedTransaction.cpp @@ -16,12 +16,13 @@ #include <android-base/file.h> #include <android-base/logging.h> -#include <android-base/scopeguard.h> #include <android-base/unique_fd.h> +#include <binder/Functional.h> #include <binder/RecordedTransaction.h> #include <sys/mman.h> #include <algorithm> +using namespace android::binder::impl; using android::Parcel; using android::base::borrowed_fd; using android::base::unique_fd; @@ -218,7 +219,7 @@ std::optional<RecordedTransaction> RecordedTransaction::fromFile(const unique_fd size_t memoryMappedSize = chunkPayloadSize + mmapPayloadStartOffset; void* mappedMemory = mmap(NULL, memoryMappedSize, PROT_READ, MAP_SHARED, fd.get(), mmapPageAlignedStart); - auto mmap_guard = android::base::make_scope_guard( + auto mmap_guard = make_scope_guard( [mappedMemory, memoryMappedSize] { munmap(mappedMemory, memoryMappedSize); }); transaction_checksum_t* payloadMap = diff --git a/libs/binder/RpcServer.cpp b/libs/binder/RpcServer.cpp index 07ab093992..1ba20b3103 100644 --- a/libs/binder/RpcServer.cpp +++ b/libs/binder/RpcServer.cpp @@ -25,12 +25,11 @@ #include <thread> #include <vector> -#include <android-base/scopeguard.h> +#include <binder/Functional.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <log/log.h> -#include <utils/Compat.h> #include "BuildFlags.h" #include "FdTrigger.h" @@ -45,7 +44,7 @@ namespace android { constexpr size_t kSessionIdBytes = 32; -using base::ScopeGuard; +using namespace android::binder::impl; using base::unique_fd; RpcServer::RpcServer(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) {} @@ -231,10 +230,7 @@ status_t RpcServer::recvmsgSocketConnection(const RpcServer& server, RpcTranspor } unique_fd fd(std::move(std::get<unique_fd>(fds.back()))); - if (auto res = binder::os::setNonBlocking(fd); !res.ok()) { - ALOGE("Failed setNonBlocking: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; *out = RpcTransportFd(std::move(fd)); return OK; @@ -458,11 +454,12 @@ void RpcServer::establishConnection( LOG_ALWAYS_FATAL_IF(threadId == server->mConnectingThreads.end(), "Must establish connection on owned thread"); thisThread = std::move(threadId->second); - ScopeGuard detachGuard = [&]() { + auto detachGuardLambda = [&]() { thisThread.detach(); _l.unlock(); server->mShutdownCv.notify_all(); }; + auto detachGuard = make_scope_guard(std::ref(detachGuardLambda)); server->mConnectingThreads.erase(threadId); if (status != OK || server->mShutdownTrigger->isTriggered()) { @@ -548,7 +545,7 @@ void RpcServer::establishConnection( return; } - detachGuard.Disable(); + detachGuard.release(); session->preJoinThreadOwnership(std::move(thisThread)); } diff --git a/libs/binder/RpcSession.cpp b/libs/binder/RpcSession.cpp index fa8f2b51ac..c895b21f91 100644 --- a/libs/binder/RpcSession.cpp +++ b/libs/binder/RpcSession.cpp @@ -26,14 +26,12 @@ #include <string_view> -#include <android-base/macros.h> -#include <android-base/scopeguard.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> #include <binder/RpcTransportRaw.h> #include <binder/Stability.h> -#include <utils/Compat.h> #include <utils/String8.h> #include "BuildFlags.h" @@ -52,6 +50,7 @@ extern "C" JavaVM* AndroidRuntimeGetJavaVM(); namespace android { +using namespace android::binder::impl; using base::unique_fd; RpcSession::RpcSession(std::unique_ptr<RpcTransportCtx> ctx) : mCtx(std::move(ctx)) { @@ -194,10 +193,7 @@ status_t RpcSession::setupPreconnectedClient(base::unique_fd fd, fd = request(); if (!fd.ok()) return BAD_VALUE; } - if (auto res = binder::os::setNonBlocking(fd); !res.ok()) { - ALOGE("setupPreconnectedClient: %s", res.error().message().c_str()); - return res.error().code() == 0 ? UNKNOWN_ERROR : -res.error().code(); - } + if (status_t res = binder::os::setNonBlocking(fd); res != OK) return res; RpcTransportFd transportFd(std::move(fd)); status_t status = initAndAddConnection(std::move(transportFd), sessionId, incoming); @@ -412,7 +408,9 @@ public: } private: - DISALLOW_COPY_AND_ASSIGN(JavaThreadAttacher); + JavaThreadAttacher(const JavaThreadAttacher&) = delete; + void operator=(const JavaThreadAttacher&) = delete; + bool mAttached = false; static JavaVM* getJavaVM() { @@ -497,7 +495,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< if (auto status = initShutdownTrigger(); status != OK) return status; auto oldProtocolVersion = mProtocolVersion; - auto cleanup = base::ScopeGuard([&] { + auto cleanup = make_scope_guard([&] { // if any threads are started, shut them down (void)shutdownAndWait(true); @@ -577,7 +575,7 @@ status_t RpcSession::setupClient(const std::function<status_t(const std::vector< if (status_t status = connectAndInit(mId, true /*incoming*/); status != OK) return status; } - cleanup.Disable(); + cleanup.release(); return OK; } diff --git a/libs/binder/RpcState.cpp b/libs/binder/RpcState.cpp index 26a2f4fa39..008e5d21f0 100644 --- a/libs/binder/RpcState.cpp +++ b/libs/binder/RpcState.cpp @@ -18,9 +18,8 @@ #include "RpcState.h" -#include <android-base/macros.h> -#include <android-base/scopeguard.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IPCThreadState.h> #include <binder/RpcServer.h> @@ -39,6 +38,8 @@ namespace android { +using namespace android::binder::impl; + #if RPC_FLAKE_PRONE void rpcMaybeWaitToFlake() { [[clang::no_destroy]] static std::random_device r; @@ -357,7 +358,7 @@ RpcState::CommandData::CommandData(size_t size) : mSize(size) { status_t RpcState::rpcSend( const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { for (int i = 0; i < niovs; i++) { LOG_RPC_DETAIL("Sending %s (part %d of %d) on RpcTransport %p: %s", @@ -412,10 +413,8 @@ bool RpcState::validateProtocolVersion(uint32_t version) { return false; } #else - // TODO(b/305983144) - // don't restrict on other platforms, though experimental should - // only really be used for testing, we don't have a good way to see - // what is shipping outside of Android + ALOGE("Cannot use experimental RPC binder protocol outside of Android."); + return false; #endif } else if (version >= RPC_WIRE_PROTOCOL_VERSION_NEXT) { ALOGE("Cannot use RPC binder protocol version %u which is unknown (current protocol " @@ -602,25 +601,24 @@ status_t RpcState::transactAddress(const sp<RpcSession::RpcConnection>& connecti {const_cast<uint8_t*>(data.data()), data.dataSize()}, objectTableSpan.toIovec(), }; - if (status_t status = rpcSend( - connection, session, "transaction", iovs, arraysize(iovs), - [&] { - if (waitUs > kWaitLogUs) { - ALOGE("Cannot send command, trying to process pending refcounts. Waiting " - "%zuus. Too many oneway calls?", - waitUs); - } + auto altPoll = [&] { + if (waitUs > kWaitLogUs) { + ALOGE("Cannot send command, trying to process pending refcounts. Waiting " + "%zuus. Too many oneway calls?", + waitUs); + } - if (waitUs > 0) { - usleep(waitUs); - waitUs = std::min(kWaitMaxUs, waitUs * 2); - } else { - waitUs = 1; - } + if (waitUs > 0) { + usleep(waitUs); + waitUs = std::min(kWaitMaxUs, waitUs * 2); + } else { + waitUs = 1; + } - return drainCommands(connection, session, CommandType::CONTROL_ONLY); - }, - rpcFields->mFds.get()); + return drainCommands(connection, session, CommandType::CONTROL_ONLY); + }; + if (status_t status = rpcSend(connection, session, "transaction", iovs, countof(iovs), + std::ref(altPoll), rpcFields->mFds.get()); status != OK) { // rpcSend calls shutdownAndWait, so all refcounts should be reset. If we ever tolerate // errors here, then we may need to undo the binder-sent counts for the transaction as @@ -692,7 +690,7 @@ status_t RpcState::waitForReply(const sp<RpcSession::RpcConnection>& connection, {&rpcReply, rpcReplyWireSize}, {data.data(), data.size()}, }; - if (status_t status = rpcRec(connection, session, "reply body", iovs, arraysize(iovs), nullptr); + if (status_t status = rpcRec(connection, session, "reply body", iovs, countof(iovs), nullptr); status != OK) return status; @@ -762,7 +760,7 @@ status_t RpcState::sendDecStrongToTarget(const sp<RpcSession::RpcConnection>& co .bodySize = sizeof(RpcDecStrong), }; iovec iovs[]{{&cmd, sizeof(cmd)}, {&body, sizeof(body)}}; - return rpcSend(connection, session, "dec ref", iovs, arraysize(iovs), std::nullopt); + return rpcSend(connection, session, "dec ref", iovs, countof(iovs), std::nullopt); } status_t RpcState::getAndExecuteCommand(const sp<RpcSession::RpcConnection>& connection, @@ -811,11 +809,11 @@ status_t RpcState::processCommand( origGuard = kernelBinderState->pushGetCallingSpGuard(&spGuard); } - base::ScopeGuard guardUnguard = [&]() { + auto guardUnguard = make_scope_guard([&]() { if (kernelBinderState != nullptr) { kernelBinderState->restoreGetCallingSpGuard(origGuard); } - }; + }); #endif // BINDER_WITH_KERNEL_IPC switch (command.command) { @@ -1145,7 +1143,7 @@ processTransactInternalTailCall: {const_cast<uint8_t*>(reply.data()), reply.dataSize()}, objectTableSpan.toIovec(), }; - return rpcSend(connection, session, "reply", iovs, arraysize(iovs), std::nullopt, + return rpcSend(connection, session, "reply", iovs, countof(iovs), std::nullopt, rpcFields->mFds.get()); } diff --git a/libs/binder/RpcState.h b/libs/binder/RpcState.h index 1fe71a5a78..2a954e632b 100644 --- a/libs/binder/RpcState.h +++ b/libs/binder/RpcState.h @@ -16,6 +16,7 @@ #pragma once #include <android-base/unique_fd.h> +#include <binder/Functional.h> #include <binder/IBinder.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> @@ -190,7 +191,7 @@ private: [[nodiscard]] status_t rpcSend( const sp<RpcSession::RpcConnection>& connection, const sp<RpcSession>& session, const char* what, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds = nullptr); [[nodiscard]] status_t rpcRec( diff --git a/libs/binder/RpcTransportRaw.cpp b/libs/binder/RpcTransportRaw.cpp index c089811561..ffa315191d 100644 --- a/libs/binder/RpcTransportRaw.cpp +++ b/libs/binder/RpcTransportRaw.cpp @@ -29,6 +29,8 @@ namespace android { +using namespace android::binder::impl; + // RpcTransport with TLS disabled. class RpcTransportRaw : public RpcTransport { public: @@ -54,7 +56,7 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { bool sentFds = false; @@ -70,7 +72,7 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { auto recv = [&](iovec* iovs, int niovs) -> ssize_t { return binder::os::receiveMessageFromSocket(mSocket, iovs, niovs, ancillaryFds); diff --git a/libs/binder/RpcTransportTipcAndroid.cpp b/libs/binder/RpcTransportTipcAndroid.cpp index 0c81d83032..188ba3b0cb 100644 --- a/libs/binder/RpcTransportTipcAndroid.cpp +++ b/libs/binder/RpcTransportTipcAndroid.cpp @@ -26,8 +26,7 @@ #include "RpcState.h" #include "RpcTransportUtils.h" -using android::base::Error; -using android::base::Result; +using namespace android::binder::impl; namespace android { @@ -75,7 +74,7 @@ public: status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { auto writeFn = [&](iovec* iovs, size_t niovs) -> ssize_t { @@ -93,7 +92,7 @@ public: status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* /*ancillaryFds*/) override { auto readFn = [&](iovec* iovs, size_t niovs) -> ssize_t { diff --git a/libs/binder/RpcTransportTls.cpp b/libs/binder/RpcTransportTls.cpp index efb09e9004..fef4be4545 100644 --- a/libs/binder/RpcTransportTls.cpp +++ b/libs/binder/RpcTransportTls.cpp @@ -29,6 +29,8 @@ #include "RpcState.h" #include "Utils.h" +#include <sstream> + #define SHOULD_LOG_TLS_DETAIL false #if SHOULD_LOG_TLS_DETAIL @@ -38,6 +40,9 @@ #endif namespace android { + +using namespace android::binder::impl; + namespace { // Implement BIO for socket that ignores SIGPIPE. @@ -181,10 +186,9 @@ public: // |sslError| should be from Ssl::getError(). // If |sslError| is WANT_READ / WANT_WRITE, poll for POLLIN / POLLOUT respectively. Otherwise // return error. Also return error if |fdTrigger| is triggered before or during poll(). - status_t pollForSslError( - const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, - const char* fnString, int additionalEvent, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + status_t pollForSslError(const android::RpcTransportFd& fd, int sslError, FdTrigger* fdTrigger, + const char* fnString, int additionalEvent, + const std::optional<SmallFunction<status_t()>>& altPoll) { switch (sslError) { case SSL_ERROR_WANT_READ: return handlePoll(POLLIN | additionalEvent, fd, fdTrigger, fnString, altPoll); @@ -200,7 +204,7 @@ private: status_t handlePoll(int event, const android::RpcTransportFd& fd, FdTrigger* fdTrigger, const char* fnString, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<SmallFunction<status_t()>>& altPoll) { status_t ret; if (altPoll) { ret = (*altPoll)(); @@ -284,12 +288,12 @@ public: status_t pollRead(void) override; status_t interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; status_t interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override; bool isWaiting() override { return mSocket.isInPollingState(); }; @@ -320,7 +324,7 @@ status_t RpcTransportTls::pollRead(void) { status_t RpcTransportTls::interruptableWriteFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { (void)ancillaryFds; @@ -366,7 +370,7 @@ status_t RpcTransportTls::interruptableWriteFully( status_t RpcTransportTls::interruptableReadFully( FdTrigger* fdTrigger, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& altPoll, + const std::optional<SmallFunction<status_t()>>& altPoll, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) { (void)ancillaryFds; diff --git a/libs/binder/RpcTransportUtils.h b/libs/binder/RpcTransportUtils.h index 32f0db805d..a0e502e92a 100644 --- a/libs/binder/RpcTransportUtils.h +++ b/libs/binder/RpcTransportUtils.h @@ -27,7 +27,7 @@ template <typename SendOrReceive> status_t interruptableReadOrWrite( const android::RpcTransportFd& socket, FdTrigger* fdTrigger, iovec* iovs, int niovs, SendOrReceive sendOrReceiveFun, const char* funName, int16_t event, - const std::optional<android::base::function_ref<status_t()>>& altPoll) { + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll) { MAYBE_WAIT_IN_FLAKE_MODE; if (niovs < 0) { diff --git a/libs/binder/ServiceManagerHost.cpp b/libs/binder/ServiceManagerHost.cpp index 2b67f030e0..9482e3e118 100644 --- a/libs/binder/ServiceManagerHost.cpp +++ b/libs/binder/ServiceManagerHost.cpp @@ -56,16 +56,16 @@ public: [[nodiscard]] const std::optional<unsigned int>& hostPort() const { return mPort; } private: - DISALLOW_COPY_AND_ASSIGN(AdbForwarder); + AdbForwarder(const AdbForwarder&) = delete; + void operator=(const AdbForwarder&) = delete; explicit AdbForwarder(unsigned int port) : mPort(port) {} std::optional<unsigned int> mPort; }; std::optional<AdbForwarder> AdbForwarder::forward(unsigned int devicePort) { auto result = execute({"adb", "forward", "tcp:0", "tcp:" + std::to_string(devicePort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward tcp:0 tcp:%d`: %s", devicePort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward tcp:0 tcp:%d`", devicePort); return std::nullopt; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -94,9 +94,8 @@ AdbForwarder::~AdbForwarder() { if (!mPort.has_value()) return; auto result = execute({"adb", "forward", "--remove", "tcp:" + std::to_string(*mPort)}, nullptr); - if (!result.ok()) { - ALOGE("Unable to run `adb forward --remove tcp:%d`: %s", *mPort, - result.error().message().c_str()); + if (!result.has_value()) { + ALOGE("Unable to run `adb forward --remove tcp:%d`", *mPort); return; } // Must end with exit code 0 (`has_value() && value() == 0`) @@ -130,8 +129,7 @@ sp<IBinder> getDeviceService(std::vector<std::string>&& serviceDispatcherArgs, serviceDispatcherArgs.insert(serviceDispatcherArgs.begin(), prefix.begin(), prefix.end()); auto result = execute(std::move(serviceDispatcherArgs), &CommandResult::stdoutEndsWithNewLine); - if (!result.ok()) { - ALOGE("%s", result.error().message().c_str()); + if (!result.has_value()) { return nullptr; } diff --git a/libs/binder/ServiceManagerHost.h b/libs/binder/ServiceManagerHost.h index c5310dac20..941ba3a757 100644 --- a/libs/binder/ServiceManagerHost.h +++ b/libs/binder/ServiceManagerHost.h @@ -16,7 +16,6 @@ #pragma once -#include <android-base/macros.h> #include <android/os/IServiceManager.h> namespace android { diff --git a/libs/binder/Utils.h b/libs/binder/Utils.h index dd632c0b26..c8431aab43 100644 --- a/libs/binder/Utils.h +++ b/libs/binder/Utils.h @@ -22,6 +22,22 @@ #include <log/log.h> #include <utils/Errors.h> +#define PLOGE_VA_ARGS(...) , ##__VA_ARGS__ +#define PLOGE(fmt, ...) ALOGE(fmt ": %s" PLOGE_VA_ARGS(__VA_ARGS__), strerror(errno)) + +/* TEMP_FAILURE_RETRY is not available on macOS and Trusty. */ +#ifndef TEMP_FAILURE_RETRY +/* Used to retry syscalls that can return EINTR. */ +#define TEMP_FAILURE_RETRY(exp) \ + ({ \ + __typeof__(exp) _rc; \ + do { \ + _rc = (exp); \ + } while (_rc == -1 && errno == EINTR); \ + _rc; \ + }) +#endif + #define TEST_AND_RETURN(value, expr) \ do { \ if (!(expr)) { \ @@ -32,6 +48,17 @@ namespace android { +/** + * Get the size of a statically initialized array. + * + * \param N the array to get the size of. + * \return the size of the array. + */ +template <typename T, size_t N> +constexpr size_t countof(T (&)[N]) { + return N; +} + // avoid optimizations void zeroMemory(uint8_t* data, size_t size); diff --git a/libs/binder/UtilsHost.cpp b/libs/binder/UtilsHost.cpp index 52b8f69d36..3db038f03f 100644 --- a/libs/binder/UtilsHost.cpp +++ b/libs/binder/UtilsHost.cpp @@ -25,6 +25,8 @@ #include <log/log.h> +#include "Utils.h" + namespace android { CommandResult::~CommandResult() { @@ -72,8 +74,8 @@ std::string CommandResult::toString() const { return ss.str(); } -android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec, - const std::function<bool(const CommandResult&)>& end) { +std::optional<CommandResult> execute(std::vector<std::string> argStringVec, + const std::function<bool(const CommandResult&)>& end) { // turn vector<string> into null-terminated char* vector. std::vector<char*> argv; argv.reserve(argStringVec.size() + 1); @@ -82,14 +84,21 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV CommandResult ret; android::base::unique_fd outWrite; - if (!android::base::Pipe(&ret.outPipe, &outWrite)) - return android::base::ErrnoError() << "pipe() for outPipe"; + if (!android::base::Pipe(&ret.outPipe, &outWrite)) { + PLOGE("pipe() for outPipe"); + return {}; + } android::base::unique_fd errWrite; - if (!android::base::Pipe(&ret.errPipe, &errWrite)) - return android::base::ErrnoError() << "pipe() for errPipe"; + if (!android::base::Pipe(&ret.errPipe, &errWrite)) { + PLOGE("pipe() for errPipe"); + return {}; + } int pid = fork(); - if (pid == -1) return android::base::ErrnoError() << "fork()"; + if (pid == -1) { + PLOGE("fork()"); + return {}; + } if (pid == 0) { // child ret.outPipe.reset(); @@ -140,12 +149,19 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV *errPollFd = {.fd = ret.errPipe.get(), .events = POLLIN}; } int pollRet = poll(fds, nfds, 1000 /* ms timeout */); - if (pollRet == -1) return android::base::ErrnoError() << "poll()"; + if (pollRet == -1) { + PLOGE("poll()"); + return {}; + } - if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) - return android::base::ErrnoError() << "read(stdout)"; - if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) - return android::base::ErrnoError() << "read(stderr)"; + if (!handlePoll(&ret.outPipe, outPollFd, &ret.stdoutStr)) { + PLOGE("read(stdout)"); + return {}; + } + if (!handlePoll(&ret.errPipe, errPollFd, &ret.stderrStr)) { + PLOGE("read(stderr)"); + return {}; + } if (end && end(ret)) return ret; } @@ -154,7 +170,10 @@ android::base::Result<CommandResult> execute(std::vector<std::string> argStringV while (ret.pid.has_value()) { int status; auto exitPid = waitpid(pid, &status, 0); - if (exitPid == -1) return android::base::ErrnoError() << "waitpid(" << pid << ")"; + if (exitPid == -1) { + PLOGE("waitpid(%d)", pid); + return {}; + } if (exitPid == pid) { if (WIFEXITED(status)) { ret.pid = std::nullopt; diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h index 98ac4e0c48..5de0980d8e 100644 --- a/libs/binder/UtilsHost.h +++ b/libs/binder/UtilsHost.h @@ -23,8 +23,8 @@ #include <vector> #include <android-base/macros.h> -#include <android-base/result.h> #include <android-base/unique_fd.h> +#include <utils/Errors.h> /** * Log a lot more information about host-device binder communication, when debugging issues. @@ -67,7 +67,8 @@ struct CommandResult { } private: - DISALLOW_COPY_AND_ASSIGN(CommandResult); + CommandResult(const CommandResult&) = delete; + void operator=(const CommandResult&) = delete; }; std::ostream& operator<<(std::ostream& os, const CommandResult& res); @@ -94,6 +95,6 @@ std::ostream& operator<<(std::ostream& os, const CommandResult& res); // // If the parent process has encountered any errors for system calls, return ExecuteError with // the proper errno set. -android::base::Result<CommandResult> execute(std::vector<std::string> argStringVec, - const std::function<bool(const CommandResult&)>& end); +std::optional<CommandResult> execute(std::vector<std::string> argStringVec, + const std::function<bool(const CommandResult&)>& end); } // namespace android diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 28fb9f1bbc..d78ea0d6b1 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -18,7 +18,7 @@ #include <android-base/unique_fd.h> #include <binder/IBinder.h> -#include <utils/Mutex.h> +#include <binder/RpcThreads.h> #include <map> #include <optional> @@ -193,7 +193,7 @@ private: void reportOneDeath(const Obituary& obit); bool isDescriptorCached() const; - mutable Mutex mLock; + mutable RpcMutex mLock; volatile int32_t mAlive; volatile int32_t mObitsSent; Vector<Obituary>* mObituaries; @@ -201,7 +201,7 @@ private: mutable String16 mDescriptorCache; int32_t mTrackedUid; - static Mutex sTrackingLock; + static RpcMutex sTrackingLock; static std::unordered_map<int32_t,uint32_t> sTrackingMap; static int sNumTrackedUids; static std::atomic_bool sCountByUidEnabled; diff --git a/libs/binder/include/binder/Functional.h b/libs/binder/include/binder/Functional.h new file mode 100644 index 0000000000..08e3b214da --- /dev/null +++ b/libs/binder/include/binder/Functional.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2023 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 <functional> +#include <memory> + +namespace android::binder::impl { + +template <typename F> +constexpr void assert_small_callable() { + // While this buffer (std::function::__func::__buf_) is an implementation detail generally not + // accessible to users, it's a good bet to assume its size to be around 3 pointers. + constexpr size_t kFunctionBufferSize = 3 * sizeof(void*); + + static_assert(sizeof(F) <= kFunctionBufferSize, + "Supplied callable is larger than std::function optimization buffer. " + "Try using std::ref, but make sure lambda lives long enough to be called."); +} + +template <typename F> +std::unique_ptr<void, std::function<void(void*)>> make_scope_guard(F&& f) { + assert_small_callable<decltype(std::bind(f))>(); + return {reinterpret_cast<void*>(true), std::bind(f)}; +} + +template <typename T> +class SmallFunction : public std::function<T> { +public: + template <typename F> + SmallFunction(F&& f) : std::function<T>(f) { + assert_small_callable<F>(); + } +}; + +} // namespace android::binder::impl diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index 98d12bb120..6961abc64b 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -33,7 +33,6 @@ #include <utils/RefBase.h> #include <utils/String16.h> #include <utils/Vector.h> -#include <utils/Flattenable.h> #include <binder/IInterface.h> #include <binder/Parcelable.h> diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index 9dc370b412..3672702fe1 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -17,13 +17,13 @@ #pragma once #include <binder/IBinder.h> -#include <utils/KeyedVector.h> -#include <utils/Mutex.h> #include <utils/String16.h> #include <utils/String8.h> #include <pthread.h> +#include <mutex> + // --------------------------------------------------------------------------- namespace android { @@ -178,7 +178,7 @@ private: // Time when thread pool was emptied int64_t mStarvationStartTimeMs; - mutable Mutex mLock; // protects everything below. + mutable std::mutex mLock; // protects everything below. Vector<handle_entry> mHandleToObject; diff --git a/libs/binder/include/binder/RpcSession.h b/libs/binder/include/binder/RpcSession.h index cb6460398d..e3805ac888 100644 --- a/libs/binder/include/binder/RpcSession.h +++ b/libs/binder/include/binder/RpcSession.h @@ -15,7 +15,6 @@ */ #pragma once -#include <android-base/threads.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcThreads.h> diff --git a/libs/binder/include/binder/RpcTransport.h b/libs/binder/include/binder/RpcTransport.h index 6db9ad983c..115a1732d7 100644 --- a/libs/binder/include/binder/RpcTransport.h +++ b/libs/binder/include/binder/RpcTransport.h @@ -25,10 +25,10 @@ #include <variant> #include <vector> -#include <android-base/function_ref.h> #include <android-base/unique_fd.h> #include <utils/Errors.h> +#include <binder/Functional.h> #include <binder/RpcCertificateFormat.h> #include <binder/RpcThreads.h> @@ -85,13 +85,13 @@ public: * error - interrupted (failure or trigger) */ [[nodiscard]] virtual status_t interruptableWriteFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll, - const std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, + const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0; [[nodiscard]] virtual status_t interruptableReadFully( - FdTrigger *fdTrigger, iovec *iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>> &altPoll, - std::vector<std::variant<base::unique_fd, base::borrowed_fd>> *ancillaryFds) = 0; + FdTrigger* fdTrigger, iovec* iovs, int niovs, + const std::optional<binder::impl::SmallFunction<status_t()>>& altPoll, + std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) = 0; /** * Check whether any threads are blocked while polling the transport diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs index a08cb7ab39..78f8877c1d 100644 --- a/libs/binder/rust/src/binder.rs +++ b/libs/binder/rust/src/binder.rs @@ -21,6 +21,7 @@ use crate::parcel::{BorrowedParcel, Parcel}; use crate::proxy::{DeathRecipient, SpIBinder, WpIBinder}; use crate::sys; +use downcast_rs::{impl_downcast, DowncastSync}; use std::borrow::Borrow; use std::cmp::Ordering; use std::convert::TryFrom; @@ -51,7 +52,7 @@ pub type TransactionFlags = u32; /// interfaces) must implement this trait. /// /// This is equivalent `IInterface` in C++. -pub trait Interface: Send + Sync { +pub trait Interface: Send + Sync + DowncastSync { /// Convert this binder object into a generic [`SpIBinder`] reference. fn as_binder(&self) -> SpIBinder { panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") @@ -66,6 +67,8 @@ pub trait Interface: Send + Sync { } } +impl_downcast!(sync Interface); + /// Implemented by sync interfaces to specify what the associated async interface is. /// Generic to handle the fact that async interfaces are generic over a thread pool. /// @@ -143,7 +146,7 @@ impl TryFrom<i32> for Stability { /// When using the AIDL backend, users need only implement the high-level AIDL-defined /// interface. The AIDL compiler then generates a container struct that wraps /// the user-defined service and implements `Remotable`. -pub trait Remotable: Send + Sync { +pub trait Remotable: Send + Sync + 'static { /// The Binder interface descriptor string. /// /// This string is a unique identifier for a Binder interface, and should be @@ -893,6 +896,23 @@ macro_rules! declare_binder_interface { $crate::binder_impl::IBinderInternal::set_requesting_sid(&mut binder, features.set_requesting_sid); $crate::Strong::new(Box::new(binder)) } + + /// Tries to downcast the interface to another type. + /// When receiving this object from a binder call, make sure that the object received is + /// a binder native object and that is of the right type for the Downcast: + /// + /// let binder = received_object.as_binder(); + /// if !binder.is_remote() { + /// let binder_native: Binder<BnFoo> = binder.try_into()?; + /// let original_object = binder_native.downcast_binder::<MyFoo>(); + /// // Check that returned type is not None before using it + /// } + /// + /// Handle the error cases instead of just calling `unwrap` or `expect` to prevent a + /// malicious caller to mount a Denial of Service attack. + pub fn downcast_binder<T: $interface>(&self) -> Option<&T> { + self.0.as_any().downcast_ref::<T>() + } } impl $crate::binder_impl::Remotable for $native { @@ -1004,7 +1024,7 @@ macro_rules! declare_binder_interface { $( // Async interface trait implementations. - impl<P: $crate::BinderAsyncPool> $crate::FromIBinder for dyn $async_interface<P> { + impl<P: $crate::BinderAsyncPool + 'static> $crate::FromIBinder for dyn $async_interface<P> { fn try_from(mut ibinder: $crate::SpIBinder) -> std::result::Result<$crate::Strong<dyn $async_interface<P>>, $crate::StatusCode> { use $crate::binder_impl::AssociateClass; @@ -1030,27 +1050,27 @@ macro_rules! declare_binder_interface { } } - impl<P: $crate::BinderAsyncPool> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ { + impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::Serialize for dyn $async_interface<P> + '_ { fn serialize(&self, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> { let binder = $crate::Interface::as_binder(self); parcel.write(&binder) } } - impl<P: $crate::BinderAsyncPool> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ { + impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::SerializeOption for dyn $async_interface<P> + '_ { fn serialize_option(this: Option<&Self>, parcel: &mut $crate::binder_impl::BorrowedParcel<'_>) -> std::result::Result<(), $crate::StatusCode> { parcel.write(&this.map($crate::Interface::as_binder)) } } - impl<P: $crate::BinderAsyncPool> std::fmt::Debug for dyn $async_interface<P> + '_ { + impl<P: $crate::BinderAsyncPool + 'static> std::fmt::Debug for dyn $async_interface<P> + '_ { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.pad(stringify!($async_interface)) } } /// Convert a &dyn $async_interface to Strong<dyn $async_interface> - impl<P: $crate::BinderAsyncPool> std::borrow::ToOwned for dyn $async_interface<P> { + impl<P: $crate::BinderAsyncPool + 'static> std::borrow::ToOwned for dyn $async_interface<P> { type Owned = $crate::Strong<dyn $async_interface<P>>; fn to_owned(&self) -> Self::Owned { self.as_binder().into_interface() @@ -1058,11 +1078,11 @@ macro_rules! declare_binder_interface { } } - impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface { + impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToAsyncInterface<P> for dyn $interface { type Target = dyn $async_interface<P>; } - impl<P: $crate::BinderAsyncPool> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> { + impl<P: $crate::BinderAsyncPool + 'static> $crate::binder_impl::ToSyncInterface for dyn $async_interface<P> { type Target = dyn $interface; } )? diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp index 6712c9cece..7e0b59463a 100644 --- a/libs/binder/tests/binderAllocationLimits.cpp +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -16,6 +16,7 @@ #include <android-base/logging.h> #include <binder/Binder.h> +#include <binder/Functional.h> #include <binder/IServiceManager.h> #include <binder/Parcel.h> #include <binder/RpcServer.h> @@ -28,6 +29,8 @@ #include <functional> #include <vector> +using namespace android::binder::impl; + static android::String8 gEmpty(""); // make sure first allocation from optimization runs struct DestructionAction { @@ -172,6 +175,18 @@ TEST(BinderAllocation, PingTransaction) { a_binder->pingBinder(); } +TEST(BinderAllocation, MakeScopeGuard) { + const auto m = ScopeDisallowMalloc(); + { + auto guard1 = make_scope_guard([] {}); + guard1.release(); + + auto guard2 = make_scope_guard([&guard1, ptr = imaginary_use] { + if (ptr == nullptr) guard1.release(); + }); + } +} + TEST(BinderAllocation, InterfaceDescriptorTransaction) { sp<IBinder> a_binder = GetRemoteBinder(); diff --git a/libs/binder/tests/binderHostDeviceTest.cpp b/libs/binder/tests/binderHostDeviceTest.cpp index 0075688ed3..0ae536c6bc 100644 --- a/libs/binder/tests/binderHostDeviceTest.cpp +++ b/libs/binder/tests/binderHostDeviceTest.cpp @@ -75,7 +75,7 @@ class HostDeviceTest : public ::testing::Test { public: void SetUp() override { auto debuggableResult = execute(Split("adb shell getprop ro.debuggable", " "), nullptr); - ASSERT_THAT(debuggableResult, Ok()); + ASSERT_TRUE(debuggableResult.has_value()); ASSERT_EQ(0, debuggableResult->exitCode) << *debuggableResult; auto debuggableBool = ParseBool(Trim(debuggableResult->stdoutStr)); ASSERT_NE(ParseBoolResult::kError, debuggableBool) << Trim(debuggableResult->stdoutStr); @@ -84,7 +84,7 @@ public: } auto lsResult = execute(Split("adb shell which servicedispatcher", " "), nullptr); - ASSERT_THAT(lsResult, Ok()); + ASSERT_TRUE(lsResult.has_value()); if (lsResult->exitCode != 0) { GTEST_SKIP() << "b/182914638: until feature is fully enabled, skip test on devices " "without servicedispatcher"; @@ -95,7 +95,7 @@ public: auto service = execute({"adb", "shell", kServiceBinary, kServiceName, kDescriptor}, &CommandResult::stdoutEndsWithNewLine); - ASSERT_THAT(service, Ok()); + ASSERT_TRUE(service.has_value()); ASSERT_EQ(std::nullopt, service->exitCode) << *service; mService = std::move(*service); } diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp index 341e9ce5ec..f3969f1a01 100644 --- a/libs/binder/tests/binderLibTest.cpp +++ b/libs/binder/tests/binderLibTest.cpp @@ -29,17 +29,17 @@ #include <android-base/properties.h> #include <android-base/result-gmock.h> -#include <android-base/result.h> -#include <android-base/scopeguard.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <binder/Binder.h> #include <binder/BpBinder.h> +#include <binder/Functional.h> #include <binder/IBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> #include <binder/RpcServer.h> #include <binder/RpcSession.h> +#include <utils/Flattenable.h> #include <linux/sched.h> #include <sys/epoll.h> @@ -52,6 +52,7 @@ #define ARRAY_SIZE(array) (sizeof array / sizeof array[0]) using namespace android; +using namespace android::binder::impl; using namespace std::string_literals; using namespace std::chrono_literals; using android::base::testing::HasValue; @@ -1325,7 +1326,7 @@ TEST_F(BinderLibTest, TooManyFdsFlattenable) { ASSERT_EQ(0, ret); // Restore the original file limits when the test finishes - base::ScopeGuard guardUnguard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); + auto guardUnguard = make_scope_guard([&]() { setrlimit(RLIMIT_NOFILE, &origNofile); }); rlimit testNofile = {1024, 1024}; ret = setrlimit(RLIMIT_NOFILE, &testNofile); diff --git a/libs/binder/tests/binderRpcTestCommon.h b/libs/binder/tests/binderRpcTestCommon.h index 786fab8133..c3070ddd14 100644 --- a/libs/binder/tests/binderRpcTestCommon.h +++ b/libs/binder/tests/binderRpcTestCommon.h @@ -74,8 +74,7 @@ static inline bool hasExperimentalRpc() { #ifdef __ANDROID__ return base::GetProperty("ro.build.version.codename", "") != "REL"; #else - // TODO(b/305983144): restrict on other platforms - return true; + return false; #endif } diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index 7ec7c99f06..e59dc82b2b 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -15,7 +15,6 @@ */ #include <android-base/logging.h> -#include <android-base/macros.h> #include <android-base/properties.h> #include <android-base/strings.h> #include <binder/Parcel.h> diff --git a/libs/binder/tests/binderSafeInterfaceTest.cpp b/libs/binder/tests/binderSafeInterfaceTest.cpp index 1c13866626..cbbbe74bb5 100644 --- a/libs/binder/tests/binderSafeInterfaceTest.cpp +++ b/libs/binder/tests/binderSafeInterfaceTest.cpp @@ -28,6 +28,7 @@ #include <gtest/gtest.h> #pragma clang diagnostic pop +#include <utils/Flattenable.h> #include <utils/LightRefBase.h> #include <utils/NativeHandle.h> diff --git a/libs/binder/tests/binderUtilsHostTest.cpp b/libs/binder/tests/binderUtilsHostTest.cpp index 25e286ce0c..6301c74842 100644 --- a/libs/binder/tests/binderUtilsHostTest.cpp +++ b/libs/binder/tests/binderUtilsHostTest.cpp @@ -32,7 +32,7 @@ namespace android { TEST(UtilsHost, ExecuteImmediately) { auto result = execute({"echo", "foo"}, nullptr); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_THAT(result->exitCode, Optional(EX_OK)); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -58,7 +58,7 @@ TEST(UtilsHost, ExecuteLongRunning) { EXPECT_GE(elapsedMs, 1000); EXPECT_LT(elapsedMs, 2000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -83,7 +83,7 @@ TEST(UtilsHost, ExecuteLongRunning2) { EXPECT_GE(elapsedMs, 4000); EXPECT_LT(elapsedMs, 6000); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_EQ(result->stdoutStr, "foo\n"); } @@ -104,7 +104,7 @@ TEST(UtilsHost, KillWithSigKill) { return false; }); - ASSERT_THAT(result, Ok()); + ASSERT_TRUE(result.has_value()); EXPECT_EQ(std::nullopt, result->exitCode); EXPECT_THAT(result->signal, Optional(SIGKILL)); } diff --git a/libs/binder/tests/parcel_fuzzer/Android.bp b/libs/binder/tests/parcel_fuzzer/Android.bp index 383795eff5..fe79f8ebe9 100644 --- a/libs/binder/tests/parcel_fuzzer/Android.bp +++ b/libs/binder/tests/parcel_fuzzer/Android.bp @@ -32,7 +32,11 @@ cc_fuzz { host_supported: true, fuzz_config: { - cc: ["smoreland@google.com"], + cc: [ + "smoreland@google.com", + "waghpawan@google.com", + ], + use_for_presubmit: true, }, srcs: [ diff --git a/libs/binder/tests/parcel_fuzzer/binder.cpp b/libs/binder/tests/parcel_fuzzer/binder.cpp index 416ffad466..ffeca2d13e 100644 --- a/libs/binder/tests/parcel_fuzzer/binder.cpp +++ b/libs/binder/tests/parcel_fuzzer/binder.cpp @@ -25,6 +25,7 @@ #include <binder/ParcelableHolder.h> #include <binder/PersistableBundle.h> #include <binder/Status.h> +#include <utils/Flattenable.h> #include "../../Utils.h" diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp index e494366267..070618294f 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFileFuzz.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android-base/macros.h> #include <binder/RecordedTransaction.h> #include <filesystem> @@ -35,8 +34,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (transaction.has_value()) { intermediateFile = std::tmpfile(); - android::base::unique_fd fdForWriting(fileno(intermediateFile)); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + android::base::unique_fd fdForWriting(dup(fileno(intermediateFile))); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp index 33a653eb33..9289f6ac90 100644 --- a/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp +++ b/libs/binder/tests/unit_fuzzers/RecordedTransactionFuzz.cpp @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <android-base/macros.h> #include <binder/RecordedTransaction.h> #include <fuzzbinder/random_parcel.h> #include <filesystem> @@ -55,7 +54,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (transaction.has_value()) { std::FILE* intermediateFile = std::tmpfile(); android::base::unique_fd fdForWriting(dup(fileno(intermediateFile))); - auto writeStatus ATTRIBUTE_UNUSED = transaction.value().dumpToFile(fdForWriting); + auto writeStatus [[maybe_unused]] = transaction.value().dumpToFile(fdForWriting); std::fclose(intermediateFile); } diff --git a/libs/binder/trusty/OS.cpp b/libs/binder/trusty/OS.cpp index 43e06e013d..0d18b0b94d 100644 --- a/libs/binder/trusty/OS.cpp +++ b/libs/binder/trusty/OS.cpp @@ -26,13 +26,11 @@ #include "../OS.h" #include "TrustyStatus.h" -using android::base::Result; - namespace android::binder::os { -Result<void> setNonBlocking(android::base::borrowed_fd /*fd*/) { +status_t setNonBlocking(android::base::borrowed_fd /*fd*/) { // Trusty IPC syscalls are all non-blocking by default. - return {}; + return OK; } status_t getRandomBytes(uint8_t* data, size_t size) { diff --git a/libs/binder/trusty/RpcTransportTipcTrusty.cpp b/libs/binder/trusty/RpcTransportTipcTrusty.cpp index 692f82d6cd..6bb45e2e11 100644 --- a/libs/binder/trusty/RpcTransportTipcTrusty.cpp +++ b/libs/binder/trusty/RpcTransportTipcTrusty.cpp @@ -29,6 +29,8 @@ namespace android { +using namespace android::binder::impl; + // RpcTransport for Trusty. class RpcTransportTipcTrusty : public RpcTransport { public: @@ -45,7 +47,7 @@ public: status_t interruptableWriteFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::optional<SmallFunction<status_t()>>& /*altPoll*/, const std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { if (niovs < 0) { @@ -115,7 +117,7 @@ public: status_t interruptableReadFully( FdTrigger* /*fdTrigger*/, iovec* iovs, int niovs, - const std::optional<android::base::function_ref<status_t()>>& /*altPoll*/, + const std::optional<SmallFunction<status_t()>>& /*altPoll*/, std::vector<std::variant<base::unique_fd, base::borrowed_fd>>* ancillaryFds) override { if (niovs < 0) { return BAD_VALUE; diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h index 8924b3679f..aa476f946a 100644 --- a/libs/binder/trusty/include/binder/RpcServerTrusty.h +++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h @@ -17,7 +17,6 @@ #pragma once #include <android-base/expected.h> -#include <android-base/macros.h> #include <android-base/unique_fd.h> #include <binder/IBinder.h> #include <binder/RpcServer.h> @@ -83,7 +82,8 @@ private: // Both this class and RpcServer have multiple non-copyable fields, // including mPortAcl below which can't be copied because mUuidPtrs // holds pointers into it - DISALLOW_COPY_AND_ASSIGN(RpcServerTrusty); + RpcServerTrusty(const RpcServerTrusty&) = delete; + void operator=(const RpcServerTrusty&) = delete; friend sp<RpcServerTrusty>; explicit RpcServerTrusty(std::unique_ptr<RpcTransportCtx> ctx, std::string&& portName, diff --git a/libs/binder/trusty/logging.cpp b/libs/binder/trusty/logging.cpp index b4243af891..88a1075f60 100644 --- a/libs/binder/trusty/logging.cpp +++ b/libs/binder/trusty/logging.cpp @@ -22,7 +22,6 @@ #include <iostream> #include <string> -#include <android-base/macros.h> #include <android-base/strings.h> namespace android { diff --git a/libs/bufferqueueconverter/Android.bp b/libs/bufferqueueconverter/Android.bp index d4605ea13a..3fe71cefce 100644 --- a/libs/bufferqueueconverter/Android.bp +++ b/libs/bufferqueueconverter/Android.bp @@ -34,5 +34,7 @@ cc_library { "libbase", "liblog", ], + static_libs: ["libguiflags"], export_include_dirs: ["include"], + export_static_lib_headers: ["libguiflags"], } diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index ed5d5c1095..394a0002ed 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -575,17 +575,23 @@ android_namespace_t* GraphicsEnv::getAngleNamespace() { return mAngleNamespace; } - if (mAnglePath.empty() && !mShouldUseSystemAngle) { - ALOGV("mAnglePath is empty and not using system ANGLE, abort creating ANGLE namespace"); + // If ANGLE path is not set, it means ANGLE should not be used for this process; + // or if ANGLE path is set and set to use system ANGLE, then a namespace is not needed + // because: + // 1) if the default OpenGL ES driver is already ANGLE, then the loader will skip; + // 2) if the default OpenGL ES driver is native, then there's no symbol conflict; + // 3) if there's no OpenGL ES driver is preloaded, then there's no symbol conflict. + if (mAnglePath.empty() || mShouldUseSystemAngle) { + ALOGV("mAnglePath is empty or use system ANGLE, abort creating ANGLE namespace"); return nullptr; } // Construct the search paths for system ANGLE. const char* const defaultLibraryPaths = #if defined(__LP64__) - "/vendor/lib64/egl:/system/lib64/egl"; + "/vendor/lib64/egl:/system/lib64"; #else - "/vendor/lib/egl:/system/lib/egl"; + "/vendor/lib/egl:/system/lib"; #endif // If the application process will run on top of system ANGLE, construct the namespace diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h index 47607a0ab9..9ebaf16eb4 100644 --- a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -104,7 +104,7 @@ public: GL_UPDATED = 2, VULKAN = 3, VULKAN_UPDATED = 4, - ANGLE = 5, + ANGLE = 5, // cover both system ANGLE and ANGLE APK }; enum Stats { diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index f17a6547f7..c84ee1f9da 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -20,6 +20,25 @@ package { default_applicable_licenses: ["frameworks_native_license"], } +aconfig_declarations { + name: "libgui_flags", + package: "com.android.graphics.libgui.flags", + srcs: ["libgui_flags.aconfig"], +} + +cc_aconfig_library { + name: "libguiflags", + host_supported: true, + vendor_available: true, + min_sdk_version: "29", + apex_available: [ + "//apex_available:platform", + "com.android.media.swcodec", + "test_com.android.media.swcodec", + ], + aconfig_declarations: "libgui_flags", +} + cc_library_headers { name: "libgui_headers", vendor_available: true, @@ -36,6 +55,8 @@ cc_library_headers { "android.hardware.graphics.bufferqueue@1.0", "android.hardware.graphics.bufferqueue@2.0", ], + static_libs: ["libguiflags"], + export_static_lib_headers: ["libguiflags"], min_sdk_version: "29", // TODO(b/218719284) can media use be constrained to libgui_bufferqueue_static? apex_available: [ @@ -192,19 +213,6 @@ cc_library_static { }, } -aconfig_declarations { - name: "libgui_flags", - package: "com.android.graphics.libgui.flags", - srcs: ["libgui_flags.aconfig"], -} - -cc_aconfig_library { - name: "libguiflags", - host_supported: true, - vendor_available: true, - aconfig_declarations: "libgui_flags", -} - filegroup { name: "libgui-sources", srcs: [ @@ -265,6 +273,9 @@ cc_defaults { "libbinder", "libGLESv2", ], + export_static_lib_headers: [ + "libguiflags", + ], } cc_library_shared { @@ -460,6 +471,7 @@ cc_library_static { static_libs: [ "libgtest", "libgmock", + "libguiflags", ], srcs: [ diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index dd0a028865..8d0331ebb5 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -26,7 +26,7 @@ #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> -#include <gui/Flags.h> + #include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> #include <gui/IProducerListener.h> @@ -41,8 +41,6 @@ #include <android-base/thread_annotations.h> #include <chrono> -#include <com_android_graphics_libgui_flags.h> - using namespace com::android::graphics::libgui; using namespace std::chrono_literals; @@ -144,7 +142,7 @@ void BLASTBufferItemConsumer::onSidebandStreamChanged() { } } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void BLASTBufferItemConsumer::onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) { sp<BLASTBufferQueue> bbq = mBLASTBufferQueue.promote(); diff --git a/libs/gui/BufferQueue.cpp b/libs/gui/BufferQueue.cpp index ab0f6d213f..b0f6e69115 100644 --- a/libs/gui/BufferQueue.cpp +++ b/libs/gui/BufferQueue.cpp @@ -22,7 +22,6 @@ #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> -#include <gui/Flags.h> namespace android { @@ -99,7 +98,7 @@ void BufferQueue::ProxyConsumerListener::addAndGetFrameTimestamps( } } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void BufferQueue::ProxyConsumerListener::onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) { sp<ConsumerListener> listener(mConsumerListener.promote()); diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 67dff6dec6..19693e37cf 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -32,7 +32,7 @@ #include <gui/BufferItem.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> -#include <gui/Flags.h> + #include <gui/FrameRateUtils.h> #include <gui/GLConsumer.h> #include <gui/IConsumerListener.h> @@ -1753,7 +1753,7 @@ status_t BufferQueueProducer::setAutoPrerotation(bool autoPrerotation) { return NO_ERROR; } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) status_t BufferQueueProducer::setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) { ATRACE_CALL(); diff --git a/libs/gui/FrameRateUtils.cpp b/libs/gui/FrameRateUtils.cpp index 6993bfab45..11524e2b51 100644 --- a/libs/gui/FrameRateUtils.cpp +++ b/libs/gui/FrameRateUtils.cpp @@ -14,14 +14,16 @@ * limitations under the License. */ -#include <gui/Flags.h> #include <gui/FrameRateUtils.h> #include <system/window.h> #include <utils/Log.h> #include <cmath> +#include <com_android_graphics_libgui_flags.h> + namespace android { +using namespace com::android::graphics::libgui; // Returns true if the frameRate is valid. // // @param frameRate the frame rate in Hz @@ -53,7 +55,7 @@ bool ValidateFrameRate(float frameRate, int8_t compatibility, int8_t changeFrame changeFrameRateStrategy != ANATIVEWINDOW_CHANGE_FRAME_RATE_ALWAYS) { ALOGE("%s failed - invalid change frame rate strategy value %d", functionName, changeFrameRateStrategy); - if (FLAG_BQ_SET_FRAME_RATE) { + if (flags::bq_setframerate()) { return false; } } diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index d0c09e481d..e81c098b85 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -28,7 +28,7 @@ #include <binder/IInterface.h> #include <gui/BufferQueueDefs.h> -#include <gui/Flags.h> + #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> #include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> @@ -763,7 +763,7 @@ public: } return result; } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) virtual status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override { Parcel data, reply; @@ -973,7 +973,7 @@ status_t IGraphicBufferProducer::setAutoPrerotation(bool autoPrerotation) { return INVALID_OPERATION; } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) status_t IGraphicBufferProducer::setFrameRate(float /*frameRate*/, int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) { // No-op for IGBP other than BufferQueue. @@ -1522,7 +1522,7 @@ status_t BnGraphicBufferProducer::onTransact( reply->writeInt32(result); return NO_ERROR; } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) case SET_FRAME_RATE: { CHECK_INTERFACE(IGraphicBuffer, data, reply); float frameRate = data.readFloat(); diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index a87f05357f..07a0cfed63 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -43,7 +43,7 @@ #include <gui/AidlStatusUtil.h> #include <gui/BufferItem.h> -#include <gui/Flags.h> + #include <gui/IProducerListener.h> #include <gui/ISurfaceComposer.h> @@ -2571,7 +2571,7 @@ void Surface::ProducerListenerProxy::onBuffersDiscarded(const std::vector<int32_ status_t Surface::setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) { -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) if (flags::bq_setframerate()) { status_t err = mGraphicBufferProducer->setFrameRate(frameRate, compatibility, changeFrameRateStrategy); diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h index 02d7c4d2ac..892215ec32 100644 --- a/libs/gui/include/gui/BLASTBufferQueue.h +++ b/libs/gui/include/gui/BLASTBufferQueue.h @@ -19,7 +19,7 @@ #include <gui/BufferItem.h> #include <gui/BufferItemConsumer.h> -#include <gui/Flags.h> + #include <gui/IGraphicBufferProducer.h> #include <gui/SurfaceComposerClient.h> @@ -31,6 +31,8 @@ #include <thread> #include <queue> +#include <com_android_graphics_libgui_flags.h> + namespace android { class BLASTBufferQueue; @@ -59,7 +61,7 @@ public: protected: void onSidebandStreamChanged() override EXCLUDES(mMutex); -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif diff --git a/libs/gui/include/gui/BufferQueue.h b/libs/gui/include/gui/BufferQueue.h index 2756277f2c..0948c4d076 100644 --- a/libs/gui/include/gui/BufferQueue.h +++ b/libs/gui/include/gui/BufferQueue.h @@ -19,11 +19,13 @@ #include <gui/BufferItem.h> #include <gui/BufferQueueDefs.h> -#include <gui/Flags.h> + #include <gui/IConsumerListener.h> #include <gui/IGraphicBufferConsumer.h> #include <gui/IGraphicBufferProducer.h> +#include <com_android_graphics_libgui_flags.h> + namespace android { class BufferQueue { @@ -70,7 +72,7 @@ public: void addAndGetFrameTimestamps( const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override; -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif diff --git a/libs/gui/include/gui/BufferQueueProducer.h b/libs/gui/include/gui/BufferQueueProducer.h index 38805d0221..de47483dca 100644 --- a/libs/gui/include/gui/BufferQueueProducer.h +++ b/libs/gui/include/gui/BufferQueueProducer.h @@ -18,7 +18,7 @@ #define ANDROID_GUI_BUFFERQUEUEPRODUCER_H #include <gui/BufferQueueDefs.h> -#include <gui/Flags.h> + #include <gui/IGraphicBufferProducer.h> namespace android { @@ -202,7 +202,7 @@ public: // See IGraphicBufferProducer::setAutoPrerotation virtual status_t setAutoPrerotation(bool autoPrerotation); -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) // See IGraphicBufferProducer::setFrameRate status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; diff --git a/libs/gui/include/gui/IConsumerListener.h b/libs/gui/include/gui/IConsumerListener.h index e183bf2668..51d3959de7 100644 --- a/libs/gui/include/gui/IConsumerListener.h +++ b/libs/gui/include/gui/IConsumerListener.h @@ -19,13 +19,13 @@ #include <binder/IInterface.h> #include <binder/SafeInterface.h> -#include <gui/Flags.h> - #include <utils/Errors.h> #include <utils/RefBase.h> #include <cstdint> +#include <com_android_graphics_libgui_flags.h> + namespace android { class BufferItem; @@ -93,7 +93,7 @@ public: virtual void addAndGetFrameTimestamps(const NewFrameEventsEntry* /*newTimestamps*/, FrameEventHistoryDelta* /*outDelta*/) {} -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) // Notifies the consumer of a setFrameRate call from the producer side. virtual void onSetFrameRate(float /*frameRate*/, int8_t /*compatibility*/, int8_t /*changeFrameRateStrategy*/) {} diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 3562906870..7639e709ca 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -31,7 +31,6 @@ #include <ui/Rect.h> #include <ui/Region.h> -#include <gui/Flags.h> #include <gui/FrameTimestamps.h> #include <gui/HdrMetadata.h> @@ -42,6 +41,8 @@ #include <optional> #include <vector> +#include <com_android_graphics_libgui_flags.h> + namespace android { // ---------------------------------------------------------------------------- @@ -677,7 +678,7 @@ public: // the width and height used for dequeueBuffer will be additionally swapped. virtual status_t setAutoPrerotation(bool autoPrerotation); -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) // Sets the apps intended frame rate. virtual status_t setFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy); diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index 38c0eed474..6dcd501404 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -21,7 +21,7 @@ cc_test { "-Wall", "-Werror", "-Wno-extra", - "-DFLAG_BQ_SET_FRAME_RATE=true", + "-DCOM_ANDROID_GRAPHICS_LIBGUI_FLAGS_BQ_SETFRAMERATE=true", ], srcs: [ diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 17aa5f1350..1410c7dce0 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -1266,9 +1266,7 @@ TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { } TEST_F(BufferQueueTest, TestBqSetFrameRateFlagBuildTimeIsSet) { - if (flags::bq_setframerate()) { - ASSERT_EQ(true, FLAG_BQ_SET_FRAME_RATE); - } + ASSERT_EQ(flags::bq_setframerate(), COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE)); } struct BufferItemConsumerSetFrameRateListener : public BufferItemConsumer { diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index e7b1232eac..86ced2ccf2 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -1341,7 +1341,7 @@ protected: newFrame->mRefreshes[0].mGpuCompositionDone.mFenceTime : FenceTime::NO_FENCE; // HWC2 releases the previous buffer after a new latch just before - // calling postComposition. + // calling onCompositionPresented. if (oldFrame != nullptr) { mCfeh->addRelease(nOldFrame, oldFrame->kDequeueReadyTime, std::shared_ptr<FenceTime>(oldFrame->mRelease.mFenceTime)); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 69a4f0aed1..c37db1693a 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -37,14 +37,14 @@ filegroup { // flags ///////////////////////////////////////////////// aconfig_declarations { - name: "aconfig_input_flags", + name: "com.android.input.flags-aconfig", package: "com.android.input.flags", srcs: ["input_flags.aconfig"], } cc_aconfig_library { - name: "aconfig_input_flags_c_lib", - aconfig_declarations: "aconfig_input_flags", + name: "com.android.input.flags-aconfig-cc", + aconfig_declarations: "com.android.input.flags-aconfig", host_supported: true, // Use the test version of the aconfig flag library by default to allow tests to set local // overrides for flags, without having to link against a separate version of libinput or of this @@ -242,7 +242,7 @@ cc_library { ], whole_static_libs: [ - "aconfig_input_flags_c_lib", + "com.android.input.flags-aconfig-cc", "libinput_rust_ffi", ], diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 116b778608..613a0df040 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -275,10 +275,10 @@ void VelocityTracker::addMovement(nsecs_t eventTime, int32_t pointerId, int32_t } } -void VelocityTracker::addMovement(const MotionEvent* event) { +void VelocityTracker::addMovement(const MotionEvent& event) { // Stores data about which axes to process based on the incoming motion event. std::set<int32_t> axesToProcess; - int32_t actionMasked = event->getActionMasked(); + int32_t actionMasked = event.getActionMasked(); switch (actionMasked) { case AMOTION_EVENT_ACTION_DOWN: @@ -291,7 +291,7 @@ void VelocityTracker::addMovement(const MotionEvent* event) { // Start a new movement trace for a pointer that just went down. // We do this on down instead of on up because the client may want to query the // final velocity for a pointer that just went up. - clearPointer(event->getPointerId(event->getActionIndex())); + clearPointer(event.getPointerId(event.getActionIndex())); axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; } @@ -300,8 +300,14 @@ void VelocityTracker::addMovement(const MotionEvent* event) { axesToProcess.insert(PLANAR_AXES.begin(), PLANAR_AXES.end()); break; case AMOTION_EVENT_ACTION_POINTER_UP: + if (event.getFlags() & AMOTION_EVENT_FLAG_CANCELED) { + clearPointer(event.getPointerId(event.getActionIndex())); + return; + } + // Continue to ACTION_UP to ensure that the POINTER_STOPPED logic is triggered. + [[fallthrough]]; case AMOTION_EVENT_ACTION_UP: { - std::chrono::nanoseconds delaySinceLastEvent(event->getEventTime() - mLastEventTime); + std::chrono::nanoseconds delaySinceLastEvent(event.getEventTime() - mLastEventTime); if (delaySinceLastEvent > ASSUME_POINTER_STOPPED_TIME) { ALOGD_IF(DEBUG_VELOCITY, "VelocityTracker: stopped for %s, clearing state upon pointer liftoff.", @@ -325,21 +331,26 @@ void VelocityTracker::addMovement(const MotionEvent* event) { case AMOTION_EVENT_ACTION_SCROLL: axesToProcess.insert(AMOTION_EVENT_AXIS_SCROLL); break; + case AMOTION_EVENT_ACTION_CANCEL: { + clear(); + return; + } + default: // Ignore all other actions. return; } - const size_t historySize = event->getHistorySize(); + const size_t historySize = event.getHistorySize(); for (size_t h = 0; h <= historySize; h++) { - const nsecs_t eventTime = event->getHistoricalEventTime(h); - for (size_t i = 0; i < event->getPointerCount(); i++) { - if (event->isResampled(i, h)) { + const nsecs_t eventTime = event.getHistoricalEventTime(h); + for (size_t i = 0; i < event.getPointerCount(); i++) { + if (event.isResampled(i, h)) { continue; // skip resampled samples } - const int32_t pointerId = event->getPointerId(i); + const int32_t pointerId = event.getPointerId(i); for (int32_t axis : axesToProcess) { - const float position = event->getHistoricalAxisValue(axis, i, h); + const float position = event.getHistoricalAxisValue(axis, i, h); addMovement(eventTime, pointerId, axis, position); } } diff --git a/libs/input/input_flags.aconfig b/libs/input/input_flags.aconfig index 978a80f9b3..3672387119 100644 --- a/libs/input/input_flags.aconfig +++ b/libs/input/input_flags.aconfig @@ -48,3 +48,17 @@ flag { description: "Enable additional palm rejection on touchpad while typing" bug: "301055381" } + +flag { + name: "remove_app_switch_drops" + namespace: "input" + description: "Remove the logic of dropping events due to pending app switch" + bug: "284808102" +} + +flag { + name: "disable_reject_touch_on_stylus_hover" + namespace: "input" + description: "Disable touch rejection when the stylus hovers the screen" + bug: "301216095" +} diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs index bbc6d98847..5f05a0f454 100644 --- a/libs/input/rust/input_verifier.rs +++ b/libs/input/rust/input_verifier.rs @@ -118,18 +118,17 @@ impl InputVerifier { match action.into() { MotionAction::Down => { - let it = self - .touching_pointer_ids_by_device - .entry(device_id) - .or_insert_with(HashSet::new); - let pointer_id = pointer_properties[0].id; - if it.contains(&pointer_id) { + if self.touching_pointer_ids_by_device.contains_key(&device_id) { return Err(format!( "{}: Invalid DOWN event - pointers already down for device {:?}: {:?}", - self.name, device_id, it + self.name, device_id, self.touching_pointer_ids_by_device )); } - it.insert(pointer_id); + let it = self + .touching_pointer_ids_by_device + .entry(device_id) + .or_insert_with(HashSet::new); + it.insert(pointer_properties[0].id); } MotionAction::PointerDown { action_index } => { if !self.touching_pointer_ids_by_device.contains_key(&device_id) { @@ -353,6 +352,56 @@ mod tests { } #[test] + fn two_pointer_stream() { + let mut verifier = InputVerifier::new("Test", /*should_log*/ false); + let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Touchscreen, + input_bindgen::AMOTION_EVENT_ACTION_DOWN, + &pointer_properties, + MotionFlags::empty(), + ) + .is_ok()); + // POINTER 1 DOWN + let two_pointer_properties = + Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Touchscreen, + input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN + | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + &two_pointer_properties, + MotionFlags::empty(), + ) + .is_ok()); + // POINTER 0 UP + assert!(verifier + .process_movement( + DeviceId(1), + Source::Touchscreen, + input_bindgen::AMOTION_EVENT_ACTION_POINTER_UP + | (0 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + &two_pointer_properties, + MotionFlags::empty(), + ) + .is_ok()); + // ACTION_UP for pointer id=1 + let pointer_1_properties = Vec::from([RustPointerProperties { id: 1 }]); + assert!(verifier + .process_movement( + DeviceId(1), + Source::Touchscreen, + input_bindgen::AMOTION_EVENT_ACTION_UP, + &pointer_1_properties, + MotionFlags::empty(), + ) + .is_ok()); + } + + #[test] fn multi_device_stream() { let mut verifier = InputVerifier::new("Test", /*should_log*/ false); let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 1c8ec90373..f9ca28083d 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -229,41 +229,23 @@ static std::vector<MotionEvent> createTouchMotionEventStream( return events; } -static std::optional<float> computePlanarVelocity( - const VelocityTracker::Strategy strategy, - const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, - uint32_t pointerId = DEFAULT_POINTER_ID) { +static std::optional<float> computeVelocity(const VelocityTracker::Strategy strategy, + const std::vector<MotionEvent>& events, int32_t axis, + uint32_t pointerId = DEFAULT_POINTER_ID) { VelocityTracker vt(strategy); - std::vector<MotionEvent> events = createTouchMotionEventStream(motions); - for (MotionEvent event : events) { - vt.addMovement(&event); + for (const MotionEvent& event : events) { + vt.addMovement(event); } return vt.getVelocity(axis, pointerId); } -static std::vector<MotionEvent> createMotionEventStream( - int32_t axis, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motion) { - switch (axis) { - case AMOTION_EVENT_AXIS_SCROLL: - return createAxisScrollMotionEventStream(motion); - default: - ADD_FAILURE() << "Axis " << axis << " is not supported"; - return {}; - } -} - -static std::optional<float> computeVelocity( +static std::optional<float> computePlanarVelocity( const VelocityTracker::Strategy strategy, - const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, int32_t axis) { - VelocityTracker vt(strategy); - - for (const MotionEvent& event : createMotionEventStream(axis, motions)) { - vt.addMovement(&event); - } - - return vt.getVelocity(axis, DEFAULT_POINTER_ID); + const std::vector<PlanarMotionEventEntry>& motions, int32_t axis, uint32_t pointerId) { + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); + return computeVelocity(strategy, events, axis, pointerId); } static void computeAndCheckVelocity(const VelocityTracker::Strategy strategy, @@ -277,23 +259,23 @@ static void computeAndCheckAxisScrollVelocity( const VelocityTracker::Strategy strategy, const std::vector<std::pair<std::chrono::nanoseconds, float>>& motions, std::optional<float> targetVelocity) { - checkVelocity(computeVelocity(strategy, motions, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); + std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); + checkVelocity(computeVelocity(strategy, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); // The strategy LSQ2 is not compatible with AXIS_SCROLL. In those situations, we should fall // back to a strategy that supports differential axes. - checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, motions, + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_SCROLL), targetVelocity); } static void computeAndCheckQuadraticVelocity(const std::vector<PlanarMotionEventEntry>& motions, float velocity) { - VelocityTracker vt(VelocityTracker::Strategy::LSQ2); - std::vector<MotionEvent> events = createTouchMotionEventStream(motions); - for (MotionEvent event : events) { - vt.addMovement(&event); - } - std::optional<float> velocityX = vt.getVelocity(AMOTION_EVENT_AXIS_X, 0); - std::optional<float> velocityY = vt.getVelocity(AMOTION_EVENT_AXIS_Y, 0); + std::optional<float> velocityX = + computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + DEFAULT_POINTER_ID); + std::optional<float> velocityY = + computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + DEFAULT_POINTER_ID); ASSERT_TRUE(velocityX); ASSERT_TRUE(velocityY); @@ -330,12 +312,14 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategiesForPlanarAxes) { {30ms, {{6, 20}}}, {40ms, {{10, 30}}}}; - EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X), + EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, + DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, - AMOTION_EVENT_AXIS_X)); - EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y), + AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)); + EXPECT_EQ(computePlanarVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_Y, + DEFAULT_POINTER_ID), computePlanarVelocity(VelocityTracker::Strategy::DEFAULT, motions, - AMOTION_EVENT_AXIS_Y)); + AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)); } TEST_F(VelocityTrackerTest, TestComputedVelocity) { @@ -431,7 +415,7 @@ TEST_F(VelocityTrackerTest, TestGetComputedVelocity) { VelocityTracker vt(VelocityTracker::Strategy::IMPULSE); std::vector<MotionEvent> events = createTouchMotionEventStream(motions); for (const MotionEvent& event : events) { - vt.addMovement(&event); + vt.addMovement(event); } float maxFloat = std::numeric_limits<float>::max(); @@ -509,6 +493,89 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { computeAndCheckVelocity(VelocityTracker::Strategy::LSQ2, motions, AMOTION_EVENT_AXIS_X, 500); } +/** + * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. + */ +TEST_F(VelocityTrackerTest, ActionCancelResultsInZeroVelocity) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{0, 0}}}, // DOWN + {10ms, {{5, 10}}}, // MOVE + {20ms, {{10, 20}}}, // MOVE + {20ms, {{10, 20}}}, // ACTION_UP + }; + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); + // By default, `createTouchMotionEventStream` produces an event stream that terminates with + // ACTION_UP. We need to manually change it to ACTION_CANCEL. + MotionEvent& lastEvent = events.back(); + lastEvent.setAction(AMOTION_EVENT_ACTION_CANCEL); + lastEvent.setFlags(lastEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); + const int32_t pointerId = lastEvent.getPointerId(0); + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, + pointerId), + /*targetVelocity*/ std::nullopt); +} + +/** + * When the stream is terminated with ACTION_CANCEL, the resulting velocity should be 0. + */ +TEST_F(VelocityTrackerTest, ActionPointerCancelResultsInZeroVelocityForThatPointer) { + std::vector<PlanarMotionEventEntry> motions = { + {0ms, {{0, 5}, {NAN, NAN}}}, // DOWN + {0ms, {{0, 5}, {10, 15}}}, // POINTER_DOWN + {10ms, {{5, 10}, {15, 20}}}, // MOVE + {20ms, {{10, 15}, {20, 25}}}, // MOVE + {30ms, {{10, 15}, {20, 25}}}, // POINTER_UP + {30ms, {{10, 15}, {NAN, NAN}}}, // UP + }; + std::vector<MotionEvent> events = createTouchMotionEventStream(motions); + // Cancel the lifting pointer of the ACTION_POINTER_UP event + MotionEvent& pointerUpEvent = events.rbegin()[1]; + pointerUpEvent.setFlags(pointerUpEvent.getFlags() | AMOTION_EVENT_FLAG_CANCELED); + const int32_t pointerId = pointerUpEvent.getPointerId(pointerUpEvent.getActionIndex()); + // Double check the stream + ASSERT_EQ(1, pointerId); + ASSERT_EQ(AMOTION_EVENT_ACTION_POINTER_UP, pointerUpEvent.getActionMasked()); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, events.back().getActionMasked()); + + // Ensure the velocity of the lifting pointer is zero + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, + pointerId), + /*targetVelocity*/ std::nullopt); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, + pointerId), + /*targetVelocity*/ std::nullopt); + + // The remaining pointer should have the correct velocity. + const int32_t remainingPointerId = events.back().getPointerId(0); + ASSERT_EQ(0, remainingPointerId); + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_X, + remainingPointerId), + /*targetVelocity*/ 500); + checkVelocity(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_Y, + remainingPointerId), + /*targetVelocity*/ 500); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_X, + remainingPointerId), + /*targetVelocity*/ 500); + checkVelocity(computeVelocity(VelocityTracker::Strategy::LSQ2, events, AMOTION_EVENT_AXIS_Y, + remainingPointerId), + /*targetVelocity*/ 500); +} /** * ================== VelocityTracker tests generated by recording real events ===================== @@ -1336,9 +1403,10 @@ TEST_F(VelocityTrackerTest, TestDefaultStrategyForAxisScroll) { {40ms, 100}, }; - EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, motions, + std::vector<MotionEvent> events = createAxisScrollMotionEventStream(motions); + EXPECT_EQ(computeVelocity(VelocityTracker::Strategy::IMPULSE, events, AMOTION_EVENT_AXIS_SCROLL), - computeVelocity(VelocityTracker::Strategy::DEFAULT, motions, + computeVelocity(VelocityTracker::Strategy::DEFAULT, events, AMOTION_EVENT_AXIS_SCROLL)); } diff --git a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h index 32fb3508ff..099f47dbe1 100644 --- a/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h +++ b/libs/nativedisplay/include/surfacetexture/SurfaceTexture.h @@ -19,7 +19,7 @@ #include <android/hardware_buffer.h> #include <gui/BufferQueueDefs.h> #include <gui/ConsumerBase.h> -#include <gui/Flags.h> + #include <gui/IGraphicBufferProducer.h> #include <sys/cdefs.h> #include <system/graphics.h> @@ -352,7 +352,7 @@ protected: /** * onSetFrameRate Notifies the consumer of a setFrameRate call from the producer side. */ -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif diff --git a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp index c2535e0bf6..3a09204878 100644 --- a/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp +++ b/libs/nativedisplay/surfacetexture/SurfaceTexture.cpp @@ -515,7 +515,7 @@ void SurfaceTexture::FrameAvailableListenerProxy::onFrameAvailable(const BufferI } } -#if FLAG_BQ_SET_FRAME_RATE +#if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void SurfaceTexture::onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) { SFT_LOGV("onSetFrameRate: %.2f", frameRate); diff --git a/libs/permission/Android.bp b/libs/permission/Android.bp index 0eeca5469e..86dcaefd87 100644 --- a/libs/permission/Android.bp +++ b/libs/permission/Android.bp @@ -20,6 +20,12 @@ aidl_interface { ], } +filegroup { + name: "framework-permission-aidl-filegroup", + srcs: ["aidl/android/**/*.aidl"], + path: "aidl", +} + cc_library { name: "libpermission", host_supported: true, @@ -35,6 +41,7 @@ cc_library { "-Werror", ], srcs: [ + ":framework-permission-aidl-filegroup", "AppOpsManager.cpp", "IAppOpsCallback.cpp", "IAppOpsService.cpp", diff --git a/libs/permission/AppOpsManager.cpp b/libs/permission/AppOpsManager.cpp index 695927418d..b407d02087 100644 --- a/libs/permission/AppOpsManager.cpp +++ b/libs/permission/AppOpsManager.cpp @@ -31,6 +31,9 @@ namespace android { +using ::android::String16; +using ::android::String8; + static const sp<IBinder>& getClientId() { static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER; static sp<IBinder> gClientId; @@ -43,6 +46,11 @@ static const sp<IBinder>& getClientId() { return gClientId; } + +static std::string getString(const String16& stringToConvert) { + return std::string(String8(stringToConvert).c_str()); +} + AppOpsManager::AppOpsManager() { } @@ -78,9 +86,14 @@ sp<IAppOpsService> AppOpsManager::getService() int32_t AppOpsManager::checkOp(int32_t op, int32_t uid, const String16& callingPackage) { sp<IAppOpsService> service = getService(); - return service != nullptr - ? service->checkOperation(op, uid, callingPackage) - : AppOpsManager::MODE_IGNORED; + if (service == nullptr) { + return AppOpsManager::MODE_IGNORED; + } + AttributionSourceState attributionSourceState; + attributionSourceState.uid = uid; + attributionSourceState.packageName = getString(callingPackage); + + return service->checkOperationWithState(op, attributionSourceState); } int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t uid, @@ -99,12 +112,18 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage, const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); - int32_t mode = service != nullptr - ? service->noteOperation(op, uid, callingPackage, attributionTag, - shouldCollectNotes(op), message, uid == AID_SYSTEM) - : AppOpsManager::MODE_IGNORED; + if (service == nullptr) { + return AppOpsManager::MODE_IGNORED; + } + AttributionSourceState attributionSourceState; + attributionSourceState.uid = uid; + attributionSourceState.packageName = getString(callingPackage); + if (attributionTag.has_value()) { + attributionSourceState.attributionTag = getString(attributionTag.value()); + } - return mode; + return service->noteOperationWithState(op, attributionSourceState, + shouldCollectNotes(op), message, uid == AID_SYSTEM); } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, @@ -117,13 +136,18 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& c bool startIfModeDefault, const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); - int32_t mode = service != nullptr - ? service->startOperation(getClientId(), op, uid, callingPackage, - attributionTag, startIfModeDefault, shouldCollectNotes(op), message, - uid == AID_SYSTEM) - : AppOpsManager::MODE_IGNORED; + if (service == nullptr) { + return AppOpsManager::MODE_IGNORED; + } + AttributionSourceState attributionSourceState; + attributionSourceState.uid = uid; + attributionSourceState.packageName = getString(callingPackage); + if (attributionTag.has_value()) { + attributionSourceState.attributionTag = getString(attributionTag.value()); + } - return mode; + return service->startOperationWithState(getClientId(), op, attributionSourceState, + startIfModeDefault,shouldCollectNotes(op), message, uid == AID_SYSTEM); } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { @@ -133,9 +157,16 @@ void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPac void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage, const std::optional<String16>& attributionTag) { sp<IAppOpsService> service = getService(); - if (service != nullptr) { - service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag); + if (service == nullptr) { + return; + } + AttributionSourceState attributionSourceState; + attributionSourceState.uid = uid; + attributionSourceState.packageName = getString(callingPackage); + if (attributionTag.has_value()) { + attributionSourceState.attributionTag = getString(attributionTag.value()); } + service->finishOperationWithState(getClientId(), op, attributionSourceState); } void AppOpsManager::startWatchingMode(int32_t op, const String16& packageName, diff --git a/libs/permission/IAppOpsService.cpp b/libs/permission/IAppOpsService.cpp index 7f235a4541..33dd24d728 100644 --- a/libs/permission/IAppOpsService.cpp +++ b/libs/permission/IAppOpsService.cpp @@ -26,6 +26,8 @@ namespace android { +using android::content::AttributionSourceState; + // ---------------------------------------------------------------------- class BpAppOpsService : public BpInterface<IAppOpsService> @@ -36,31 +38,30 @@ public: { } - virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) { + virtual int32_t checkOperationWithState(int32_t code, + const AttributionSourceState &attributionSourceState) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); - data.writeInt32(uid); - data.writeString16(packageName); - remote()->transact(CHECK_OPERATION_TRANSACTION, data, &reply); + data.writeParcelable(attributionSourceState); + remote()->transact(CHECK_OPERATION_WITH_STATE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; return reply.readInt32(); } - virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, - const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, - const String16& message, bool shouldCollectMessage) { + virtual int32_t noteOperationWithState(int32_t code, + const AttributionSourceState& attributionSourceState, + bool shouldCollectAsyncNotedOp, const String16& message, + bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeInt32(code); - data.writeInt32(uid); - data.writeString16(packageName); - data.writeString16(attributionTag); + data.writeParcelable(attributionSourceState); data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); data.writeBool(shouldCollectMessage); - remote()->transact(NOTE_OPERATION_TRANSACTION, data, &reply); + remote()->transact(NOTE_OPERATION_WITH_STATE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; // TODO b/184855056: extract to class @@ -69,22 +70,20 @@ public: return reply.readInt32(); } - virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, + virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code, + const AttributionSourceState& attributionSourceState, bool startIfModeDefault, + bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); - data.writeInt32(uid); - data.writeString16(packageName); - data.writeString16(attributionTag); + data.writeParcelable(attributionSourceState); data.writeBool(startIfModeDefault); data.writeBool(shouldCollectAsyncNotedOp); data.writeString16(message); data.writeBool(shouldCollectMessage); - remote()->transact(START_OPERATION_TRANSACTION, data, &reply); + remote()->transact(START_OPERATION_WITH_STATE_TRANSACTION, data, &reply); // fail on exception if (reply.readExceptionCode() != 0) return MODE_ERRORED; // TODO b/184855056: extract to class @@ -93,16 +92,14 @@ public: return reply.readInt32(); } - virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::optional<String16>& attributionTag) { + virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code, + const AttributionSourceState& attributionSourceState) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); data.writeInt32(code); - data.writeInt32(uid); - data.writeString16(packageName); - data.writeString16(attributionTag); - remote()->transact(FINISH_OPERATION_TRANSACTION, data, &reply); + data.writeParcelable(attributionSourceState); + remote()->transact(FINISH_OPERATION_WITH_STATE_TRANSACTION, data, &reply); } virtual void startWatchingMode(int32_t op, const String16& packageName, @@ -189,59 +186,65 @@ status_t BnAppOpsService::onTransact( { //printf("AppOpsService received: "); data.print(); switch(code) { - case CHECK_OPERATION_TRANSACTION: { + case CHECK_OPERATION_WITH_STATE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); - int32_t uid = data.readInt32(); - String16 packageName = data.readString16(); - int32_t res = checkOperation(code, uid, packageName); + AttributionSourceState attributionSourceState; + status_t status = data.readParcelable(&attributionSourceState); + if (status != NO_ERROR) { + return status; + } + int32_t res = checkOperationWithState(code, attributionSourceState); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case NOTE_OPERATION_TRANSACTION: { + case NOTE_OPERATION_WITH_STATE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); int32_t code = data.readInt32(); - int32_t uid = data.readInt32(); - String16 packageName = data.readString16(); - std::optional<String16> attributionTag; - data.readString16(&attributionTag); + AttributionSourceState attributionSourceState; + status_t status = data.readParcelable(&attributionSourceState); + if (status != NO_ERROR) { + return status; + } bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); bool shouldCollectMessage = data.readBool(); - int32_t res = noteOperation(code, uid, packageName, attributionTag, + int32_t res = noteOperationWithState(code, attributionSourceState, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case START_OPERATION_TRANSACTION: { + case START_OPERATION_WITH_STATE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); - int32_t uid = data.readInt32(); - String16 packageName = data.readString16(); - std::optional<String16> attributionTag; - data.readString16(&attributionTag); + AttributionSourceState attributionSourceState; + status_t status = data.readParcelable(&attributionSourceState); + if (status != NO_ERROR) { + return status; + } bool startIfModeDefault = data.readBool(); bool shouldCollectAsyncNotedOp = data.readBool(); String16 message = data.readString16(); bool shouldCollectMessage = data.readBool(); - int32_t res = startOperation(token, code, uid, packageName, attributionTag, + int32_t res = startOperationWithState(token, code, attributionSourceState, startIfModeDefault, shouldCollectAsyncNotedOp, message, shouldCollectMessage); reply->writeNoException(); reply->writeInt32(res); return NO_ERROR; } break; - case FINISH_OPERATION_TRANSACTION: { + case FINISH_OPERATION_WITH_STATE_TRANSACTION: { CHECK_INTERFACE(IAppOpsService, data, reply); sp<IBinder> token = data.readStrongBinder(); int32_t code = data.readInt32(); - int32_t uid = data.readInt32(); - String16 packageName = data.readString16(); - std::optional<String16> attributionTag; - data.readString16(&attributionTag); - finishOperation(token, code, uid, packageName, attributionTag); + AttributionSourceState attributionSourceState; + status_t status = data.readParcelable(&attributionSourceState); + if (status != NO_ERROR) { + return status; + } + finishOperationWithState(token, code, attributionSourceState); reply->writeNoException(); return NO_ERROR; } break; diff --git a/libs/permission/include/binder/IAppOpsService.h b/libs/permission/include/binder/IAppOpsService.h index 918fcdbce1..a5fdc54f28 100644 --- a/libs/permission/include/binder/IAppOpsService.h +++ b/libs/permission/include/binder/IAppOpsService.h @@ -16,6 +16,7 @@ #pragma once +#include <android/content/AttributionSourceState.h> #include <binder/IAppOpsCallback.h> #include <binder/IInterface.h> @@ -27,23 +28,24 @@ namespace android { +using android::content::AttributionSourceState; + // ---------------------------------------------------------------------- class IAppOpsService : public IInterface { public: DECLARE_META_INTERFACE(AppOpsService) - - virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; - virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, - const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, + virtual int32_t checkOperationWithState(int32_t code, + const AttributionSourceState& attributionSourceState) = 0; + virtual int32_t noteOperationWithState(int32_t code, + const AttributionSourceState& attributionSourceState, bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0; - virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::optional<String16>& attributionTag, - bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message, - bool shouldCollectMessage) = 0; - virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::optional<String16>& attributionTag) = 0; + virtual int32_t startOperationWithState(const sp<IBinder>& token, int32_t code, + const AttributionSourceState& attributionSourceState, bool startIfModeDefault, + bool shouldCollectAsyncNotedOp, const String16& message, bool shouldCollectMessage) = 0; + virtual void finishOperationWithState(const sp<IBinder>& token, int32_t code, + const AttributionSourceState& attributionSourceState) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; @@ -56,10 +58,10 @@ public: int32_t flags, const sp<IAppOpsCallback>& callback) = 0; enum { - CHECK_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, - NOTE_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+1, - START_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+2, - FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3, + CHECK_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+50, + NOTE_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+52, + START_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+53, + FINISH_OPERATION_WITH_STATE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+54, START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4, STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5, PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6, diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 04e2fffaef..e487cbc54d 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -66,6 +66,8 @@ namespace android { static const char* PERSIST_DRIVER_SUFFIX_PROPERTY = "persist.graphics.egl"; static const char* RO_DRIVER_SUFFIX_PROPERTY = "ro.hardware.egl"; static const char* RO_BOARD_PLATFORM_PROPERTY = "ro.board.platform"; +static const char* ANGLE_SUFFIX_VALUE = "angle"; +static const char* VENDOR_ANGLE_BUILD = "ro.gfx.angle.supported"; static const char* HAL_SUBNAME_KEY_PROPERTIES[3] = { PERSIST_DRIVER_SUFFIX_PROPERTY, @@ -80,6 +82,13 @@ static const char* const VENDOR_LIB_EGL_DIR = "/vendor/lib/egl"; #endif +static const char* const SYSTEM_LIB_DIR = +#if defined(__LP64__) + "/system/lib64"; +#else + "/system/lib"; +#endif + static void* do_dlopen(const char* path, int mode) { ATRACE_CALL(); return dlopen(path, mode); @@ -434,98 +443,110 @@ void Loader::init_api(void* dso, } } -static void* load_system_driver(const char* kind, const char* suffix, const bool exact) { - ATRACE_CALL(); - class MatchFile { - public: - static std::string find(const char* libraryName, const bool exact) { - std::string absolutePath; - if (findLibPath(absolutePath, libraryName, exact)) { - return absolutePath; - } - - // Driver not found. gah. - return std::string(); +static std::string findLibrary(const std::string libraryName, const std::string searchPath, + const bool exact) { + if (exact) { + std::string absolutePath = searchPath + "/" + libraryName + ".so"; + if (!access(absolutePath.c_str(), R_OK)) { + return absolutePath; } - private: - static bool findLibPath(std::string& result, const std::string& pattern, bool exact) { - const std::string vendorLibEglDirString = std::string(VENDOR_LIB_EGL_DIR); - if (exact) { - std::string absolutePath = vendorLibEglDirString + "/" + pattern + ".so"; - if (!access(absolutePath.c_str(), R_OK)) { - result = absolutePath; - return true; - } - return false; - } + return std::string(); + } - DIR* d = opendir(VENDOR_LIB_EGL_DIR); - if (d != nullptr) { - struct dirent* e; - while ((e = readdir(d)) != nullptr) { - if (e->d_type == DT_DIR) { - continue; - } - if (!strcmp(e->d_name, "libGLES_android.so")) { - // always skip the software renderer - continue; - } - if (strstr(e->d_name, pattern.c_str()) == e->d_name) { - if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { - result = vendorLibEglDirString + "/" + e->d_name; - closedir(d); - return true; - } - } + DIR* d = opendir(searchPath.c_str()); + if (d != nullptr) { + struct dirent* e; + while ((e = readdir(d)) != nullptr) { + if (e->d_type == DT_DIR) { + continue; + } + if (!strcmp(e->d_name, "libGLES_android.so")) { + // always skip the software renderer + continue; + } + if (strstr(e->d_name, libraryName.c_str()) == e->d_name) { + if (!strcmp(e->d_name + strlen(e->d_name) - 3, ".so")) { + std::string result = searchPath + "/" + e->d_name; + closedir(d); + return result; } - closedir(d); } - return false; } - }; + closedir(d); + } + // Driver not found. gah. + return std::string(); +} + +static void* load_system_driver(const char* kind, const char* suffix, const bool exact) { + ATRACE_CALL(); std::string libraryName = std::string("lib") + kind; if (suffix) { libraryName += std::string("_") + suffix; } else if (!exact) { - // Deprecated: we look for files that match - // libGLES_*.so, or: + // Deprecated for devices launching in Android 14 + // Look for files that match + // libGLES_*.so, or, // libEGL_*.so, libGLESv1_CM_*.so, libGLESv2_*.so libraryName += std::string("_"); } - std::string absolutePath = MatchFile::find(libraryName.c_str(), exact); + + void* dso = nullptr; + + const bool AngleInVendor = property_get_bool(VENDOR_ANGLE_BUILD, false); + const bool isSuffixAngle = suffix != nullptr && strcmp(suffix, ANGLE_SUFFIX_VALUE) == 0; + // Only use sphal namespace when system ANGLE binaries are not the default drivers. + const bool useSphalNamespace = !isSuffixAngle || AngleInVendor; + + const std::string absolutePath = + findLibrary(libraryName, useSphalNamespace ? VENDOR_LIB_EGL_DIR : SYSTEM_LIB_PATH, + exact); if (absolutePath.empty()) { // this happens often, we don't want to log an error return nullptr; } - const char* const driver_absolute_path = absolutePath.c_str(); + const char* const driverAbsolutePath = absolutePath.c_str(); + + // Currently the default driver is unlikely to be ANGLE on most devices, + // hence put this first. + if (useSphalNamespace) { + // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to + // the original routine when the namespace does not exist. + // See /system/linkerconfig/contents/namespace for the configuration of the + // sphal namespace. + dso = do_android_load_sphal_library(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL); + } else { + // Try to load drivers from the default namespace. + // See /system/linkerconfig/contents/namespace for the configuration of the + // default namespace. + dso = do_dlopen(driverAbsolutePath, RTLD_NOW | RTLD_LOCAL); + } - // Try to load drivers from the 'sphal' namespace, if it exist. Fall back to - // the original routine when the namespace does not exist. - // See /system/core/rootdir/etc/ld.config.txt for the configuration of the - // sphal namespace. - void* dso = do_android_load_sphal_library(driver_absolute_path, - RTLD_NOW | RTLD_LOCAL); if (dso == nullptr) { const char* err = dlerror(); - ALOGE("load_driver(%s): %s", driver_absolute_path, err ? err : "unknown"); + ALOGE("load_driver(%s): %s", driverAbsolutePath, err ? err : "unknown"); return nullptr; } - ALOGD("loaded %s", driver_absolute_path); + ALOGV("loaded %s", driverAbsolutePath); return dso; } static void* load_angle(const char* kind, android_namespace_t* ns) { - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = ns, - }; - std::string name = std::string("lib") + kind + "_angle.so"; + void* so = nullptr; - void* so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + if (android::GraphicsEnv::getInstance().shouldUseSystemAngle()) { + so = do_dlopen(name.c_str(), RTLD_NOW | RTLD_LOCAL); + } else { + const android_dlextinfo dlextinfo = { + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = ns, + }; + so = do_android_dlopen_ext(name.c_str(), RTLD_LOCAL | RTLD_NOW, &dlextinfo); + } if (so) { return so; @@ -563,10 +584,14 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { ATRACE_CALL(); android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); - if (!ns) { + // ANGLE namespace is used for loading ANGLE from apk, and hence if namespace is not + // constructed, it means ANGLE apk is not set to be the OpenGL ES driver. + // Hence skip if ANGLE apk and system ANGLE are not set to be the OpenGL ES driver. + if (!ns && !android::GraphicsEnv::getInstance().shouldUseSystemAngle()) { return nullptr; } + // use ANGLE APK driver android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); driver_t* hnd = nullptr; @@ -588,10 +613,13 @@ Loader::driver_t* Loader::attempt_to_load_angle(egl_connection_t* cnx) { } void Loader::attempt_to_init_angle_backend(void* dso, egl_connection_t* cnx) { - void* pANGLEGetDisplayPlatform = dlsym(dso, "ANGLEGetDisplayPlatform"); - if (pANGLEGetDisplayPlatform) { + cnx->angleGetDisplayPlatformFunc = dlsym(dso, "ANGLEGetDisplayPlatform"); + cnx->angleResetDisplayPlatformFunc = dlsym(dso, "ANGLEResetDisplayPlatform"); + + if (cnx->angleGetDisplayPlatformFunc) { ALOGV("ANGLE GLES library loaded"); cnx->angleLoaded = true; + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); } else { ALOGV("Native GLES library loaded"); cnx->angleLoaded = false; @@ -635,7 +663,13 @@ Loader::driver_t* Loader::attempt_to_load_updated_driver(egl_connection_t* cnx) Loader::driver_t* Loader::attempt_to_load_system_driver(egl_connection_t* cnx, const char* suffix, const bool exact) { ATRACE_CALL(); - android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL); + if (suffix && strcmp(suffix, "angle") == 0) { + // use system ANGLE driver + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::ANGLE); + } else { + android::GraphicsEnv::getInstance().setDriverToLoad(android::GpuStatsInfo::Driver::GL); + } + driver_t* hnd = nullptr; void* dso = load_system_driver("GLES", suffix, exact); if (dso) { diff --git a/opengl/libs/EGL/egl_angle_platform.cpp b/opengl/libs/EGL/egl_angle_platform.cpp index ee605c2011..f0054a7cf7 100644 --- a/opengl/libs/EGL/egl_angle_platform.cpp +++ b/opengl/libs/EGL/egl_angle_platform.cpp @@ -35,12 +35,6 @@ namespace angle { -constexpr char kAngleEs2Lib[] = "libGLESv2_angle.so"; -constexpr int kAngleDlFlags = RTLD_LOCAL | RTLD_NOW; - -static GetDisplayPlatformFunc angleGetDisplayPlatform = nullptr; -static ResetDisplayPlatformFunc angleResetDisplayPlatform = nullptr; - static time_t startTime = time(nullptr); static const unsigned char* getTraceCategoryEnabledFlag(PlatformMethods* /*platform*/, @@ -111,50 +105,19 @@ static void assignAnglePlatformMethods(PlatformMethods* platformMethods) { } // Initialize function ptrs for ANGLE PlatformMethods struct, used for systrace -bool initializeAnglePlatform(EGLDisplay dpy) { - // Since we're inside libEGL, use dlsym to lookup fptr for ANGLEGetDisplayPlatform - android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); - void* so = nullptr; - if (ns) { - const android_dlextinfo dlextinfo = { - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = ns, - }; - so = android_dlopen_ext(kAngleEs2Lib, kAngleDlFlags, &dlextinfo); - if (so) { - ALOGD("dlopen_ext from APK (%s) success at %p", kAngleEs2Lib, so); - } else { - ALOGE("dlopen_ext(\"%s\") failed: %s", kAngleEs2Lib, dlerror()); - return false; - } - } else { - // If we are here, ANGLE is loaded as built-in gl driver in the sphal. - so = android_load_sphal_library(kAngleEs2Lib, kAngleDlFlags); - if (so) { - ALOGD("dlopen (%s) success at %p", kAngleEs2Lib, so); - } else { - ALOGE("%s failed to dlopen %s: %s!", __FUNCTION__, kAngleEs2Lib, dlerror()); - return false; - } - } - - angleGetDisplayPlatform = - reinterpret_cast<GetDisplayPlatformFunc>(dlsym(so, "ANGLEGetDisplayPlatform")); - - if (!angleGetDisplayPlatform) { - ALOGE("dlsym lookup of ANGLEGetDisplayPlatform in libEGL_angle failed!"); - dlclose(so); +bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) { + if (cnx->angleGetDisplayPlatformFunc == nullptr) { + ALOGE("ANGLEGetDisplayPlatform is not initialized!"); return false; } - angleResetDisplayPlatform = - reinterpret_cast<ResetDisplayPlatformFunc>(dlsym(so, "ANGLEResetDisplayPlatform")); + GetDisplayPlatformFunc angleGetDisplayPlatform = + reinterpret_cast<GetDisplayPlatformFunc>(cnx->angleGetDisplayPlatformFunc); PlatformMethods* platformMethods = nullptr; if (!((angleGetDisplayPlatform)(dpy, g_PlatformMethodNames, g_NumPlatformMethods, nullptr, &platformMethods))) { ALOGE("ANGLEGetDisplayPlatform call failed!"); - dlclose(so); return false; } if (platformMethods) { @@ -166,8 +129,10 @@ bool initializeAnglePlatform(EGLDisplay dpy) { return true; } -void resetAnglePlatform(EGLDisplay dpy) { - if (angleResetDisplayPlatform) { +void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx) { + if (cnx->angleResetDisplayPlatformFunc) { + ResetDisplayPlatformFunc angleResetDisplayPlatform = + reinterpret_cast<ResetDisplayPlatformFunc>(cnx->angleResetDisplayPlatformFunc); angleResetDisplayPlatform(dpy); } } diff --git a/opengl/libs/EGL/egl_angle_platform.h b/opengl/libs/EGL/egl_angle_platform.h index 6c24aa5418..63806c2a4c 100644 --- a/opengl/libs/EGL/egl_angle_platform.h +++ b/opengl/libs/EGL/egl_angle_platform.h @@ -25,8 +25,8 @@ namespace angle { -bool initializeAnglePlatform(EGLDisplay dpy); -void resetAnglePlatform(EGLDisplay dpy); +bool initializeAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx); +void resetAnglePlatform(EGLDisplay dpy, android::egl_connection_t* const cnx); }; // namespace angle diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 3317347066..55a2682ac0 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -168,7 +168,7 @@ static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_conn if (dpy == EGL_NO_DISPLAY) { ALOGE("eglGetPlatformDisplay failed!"); } else { - if (!angle::initializeAnglePlatform(dpy)) { + if (!angle::initializeAnglePlatform(dpy, cnx)) { ALOGE("initializeAnglePlatform failed!"); } } @@ -433,7 +433,7 @@ EGLBoolean egl_display_t::terminate() { if (cnx->dso && disp.state == egl_display_t::INITIALIZED) { // If we're using ANGLE reset any custom DisplayPlatform if (cnx->angleLoaded) { - angle::resetAnglePlatform(disp.dpy); + angle::resetAnglePlatform(disp.dpy, cnx); } if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) { ALOGW("eglTerminate(%p) failed (%s)", disp.dpy, diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h index 3bd37cb399..90a3c199a9 100644 --- a/opengl/libs/EGL/egldefs.h +++ b/opengl/libs/EGL/egldefs.h @@ -42,7 +42,9 @@ struct egl_connection_t { libGles1(nullptr), libGles2(nullptr), systemDriverUnloaded(false), - angleLoaded(false) { + angleLoaded(false), + angleGetDisplayPlatformFunc(nullptr), + angleResetDisplayPlatformFunc(nullptr) { const char* const* entries = platform_names; EGLFuncPointer* curr = reinterpret_cast<EGLFuncPointer*>(&platform); while (*entries) { @@ -75,6 +77,9 @@ struct egl_connection_t { bool systemDriverUnloaded; bool angleLoaded; // Was ANGLE successfully loaded + + void* angleGetDisplayPlatformFunc; + void* angleResetDisplayPlatformFunc; }; extern gl_hooks_t gHooks[2]; diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index f06a0457d3..11b636d564 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -163,11 +163,13 @@ void GpuStats::insertDriverStats(const std::string& driverPackageName, addLoadingTime(driver, driverLoadingTime, &appInfo); appInfo.appPackageName = appPackageName; appInfo.driverVersionCode = driverVersionCode; - appInfo.angleInUse = driverPackageName == "angle"; + appInfo.angleInUse = + driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle"; appInfo.lastAccessTime = std::chrono::system_clock::now(); mAppStats.insert({appStatsKey, appInfo}); } else { - mAppStats[appStatsKey].angleInUse = driverPackageName == "angle"; + mAppStats[appStatsKey].angleInUse = + driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle"; addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now(); } diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index e411abb251..1092bdb90e 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -16,17 +16,37 @@ #define LOG_TAG "PointerChoreographer" +#include <android-base/logging.h> +#include <input/PrintTools.h> + #include "PointerChoreographer.h" +#define INDENT " " + namespace android { +namespace { +bool isFromMouse(const NotifyMotionArgs& args) { + return isFromSource(args.source, AINPUT_SOURCE_MOUSE) && + args.pointerProperties[0].toolType == ToolType::MOUSE; +} + +} // namespace + // --- PointerChoreographer --- PointerChoreographer::PointerChoreographer(InputListenerInterface& listener, PointerChoreographerPolicyInterface& policy) - : mNextListener(listener) {} + : mNextListener(listener), + mPolicy(policy), + mDefaultMouseDisplayId(ADISPLAY_ID_DEFAULT), + mNotifiedPointerDisplayId(ADISPLAY_ID_NONE) {} void PointerChoreographer::notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) { + std::scoped_lock _l(mLock); + + mInputDeviceInfos = args.inputDeviceInfos; + updatePointerControllersLocked(); mNextListener.notify(args); } @@ -39,7 +59,67 @@ void PointerChoreographer::notifyKey(const NotifyKeyArgs& args) { } void PointerChoreographer::notifyMotion(const NotifyMotionArgs& args) { - mNextListener.notify(args); + NotifyMotionArgs newArgs = processMotion(args); + + mNextListener.notify(newArgs); +} + +NotifyMotionArgs PointerChoreographer::processMotion(const NotifyMotionArgs& args) { + std::scoped_lock _l(mLock); + + if (isFromMouse(args)) { + return processMouseEventLocked(args); + } else if (isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN)) { + return processTouchscreenEventLocked(args); + } + return args; +} + +NotifyMotionArgs PointerChoreographer::processMouseEventLocked(const NotifyMotionArgs& args) { + if (args.getPointerCount() != 1) { + LOG(FATAL) << "Only mouse events with a single pointer are currently supported: " + << args.dump(); + } + + const int32_t displayId = getTargetMouseDisplayLocked(args.displayId); + + // Get the mouse pointer controller for the display, or create one if it doesn't exist. + auto [it, emplaced] = + mMousePointersByDisplay.try_emplace(displayId, + getMouseControllerConstructor(displayId)); + if (emplaced) { + notifyPointerDisplayIdChangedLocked(); + } + + PointerControllerInterface& pc = *it->second; + + const float deltaX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float deltaY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + pc.move(deltaX, deltaY); + pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); + + const auto [x, y] = pc.getPosition(); + NotifyMotionArgs newArgs(args); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x); + newArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + newArgs.xCursorPosition = x; + newArgs.yCursorPosition = y; + newArgs.displayId = displayId; + return newArgs; +} + +/** + * When screen is touched, fade the mouse pointer on that display. We only call fade for + * ACTION_DOWN events.This would allow both mouse and touch to be used at the same time if the + * mouse device keeps moving and unfades the cursor. + * For touch events, we do not need to populate the cursor position. + */ +NotifyMotionArgs PointerChoreographer::processTouchscreenEventLocked(const NotifyMotionArgs& args) { + if (const auto it = mMousePointersByDisplay.find(args.displayId); + it != mMousePointersByDisplay.end() && args.action == AMOTION_EVENT_ACTION_DOWN) { + it->second->fade(PointerControllerInterface::Transition::GRADUAL); + } + return args; } void PointerChoreographer::notifySwitch(const NotifySwitchArgs& args) { @@ -60,11 +140,141 @@ void PointerChoreographer::notifyDeviceReset(const NotifyDeviceResetArgs& args) void PointerChoreographer::notifyPointerCaptureChanged( const NotifyPointerCaptureChangedArgs& args) { + if (args.request.enable) { + std::scoped_lock _l(mLock); + for (const auto& [_, mousePointerController] : mMousePointersByDisplay) { + mousePointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); + } + } mNextListener.notify(args); } void PointerChoreographer::dump(std::string& dump) { + std::scoped_lock _l(mLock); + dump += "PointerChoreographer:\n"; + + dump += INDENT "MousePointerControllers:\n"; + for (const auto& [displayId, mousePointerController] : mMousePointersByDisplay) { + std::string pointerControllerDump = addLinePrefix(mousePointerController->dump(), INDENT); + dump += INDENT + std::to_string(displayId) + " : " + pointerControllerDump; + } + dump += "\n"; +} + +const DisplayViewport* PointerChoreographer::findViewportByIdLocked(int32_t displayId) const { + for (auto& viewport : mViewports) { + if (viewport.displayId == displayId) { + return &viewport; + } + } + return nullptr; +} + +int32_t PointerChoreographer::getTargetMouseDisplayLocked(int32_t associatedDisplayId) const { + return associatedDisplayId == ADISPLAY_ID_NONE ? mDefaultMouseDisplayId : associatedDisplayId; +} + +void PointerChoreographer::updatePointerControllersLocked() { + std::set<int32_t /*displayId*/> mouseDisplaysToKeep; + + // Mark the displayIds or deviceIds of PointerControllers currently needed. + for (const auto& info : mInputDeviceInfos) { + const uint32_t sources = info.getSources(); + if (isFromSource(sources, AINPUT_SOURCE_MOUSE) || + isFromSource(sources, AINPUT_SOURCE_MOUSE_RELATIVE)) { + const int32_t resolvedDisplayId = + getTargetMouseDisplayLocked(info.getAssociatedDisplayId()); + mouseDisplaysToKeep.insert(resolvedDisplayId); + } + } + + // Remove PointerControllers no longer needed. + // This has the side-effect of fading pointers or clearing spots before removal. + std::erase_if(mMousePointersByDisplay, [&mouseDisplaysToKeep](const auto& pair) { + auto& [displayId, controller] = pair; + if (mouseDisplaysToKeep.find(displayId) == mouseDisplaysToKeep.end()) { + controller->fade(PointerControllerInterface::Transition::IMMEDIATE); + return true; + } + return false; + }); + + // Notify the policy if there's a change on the pointer display ID. + notifyPointerDisplayIdChangedLocked(); +} + +void PointerChoreographer::notifyPointerDisplayIdChangedLocked() { + int32_t displayIdToNotify = ADISPLAY_ID_NONE; + FloatPoint cursorPosition = {0, 0}; + if (const auto it = mMousePointersByDisplay.find(mDefaultMouseDisplayId); + it != mMousePointersByDisplay.end()) { + const auto& pointerController = it->second; + // Use the displayId from the pointerController, because it accurately reflects whether + // the viewport has been added for that display. Otherwise, we would have to check if + // the viewport exists separately. + displayIdToNotify = pointerController->getDisplayId(); + cursorPosition = pointerController->getPosition(); + } + + if (mNotifiedPointerDisplayId == displayIdToNotify) { + return; + } + mPolicy.notifyPointerDisplayIdChanged(displayIdToNotify, cursorPosition); + mNotifiedPointerDisplayId = displayIdToNotify; +} + +void PointerChoreographer::setDefaultMouseDisplayId(int32_t displayId) { + std::scoped_lock _l(mLock); + + mDefaultMouseDisplayId = displayId; + updatePointerControllersLocked(); +} + +void PointerChoreographer::setDisplayViewports(const std::vector<DisplayViewport>& viewports) { + std::scoped_lock _l(mLock); + for (const auto& viewport : viewports) { + if (const auto it = mMousePointersByDisplay.find(viewport.displayId); + it != mMousePointersByDisplay.end()) { + it->second->setDisplayViewport(viewport); + } + } + mViewports = viewports; + notifyPointerDisplayIdChangedLocked(); +} + +std::optional<DisplayViewport> PointerChoreographer::getViewportForPointerDevice( + int32_t associatedDisplayId) { + std::scoped_lock _l(mLock); + const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(associatedDisplayId); + if (const auto viewport = findViewportByIdLocked(resolvedDisplayId); viewport) { + return *viewport; + } + return std::nullopt; +} + +FloatPoint PointerChoreographer::getMouseCursorPosition(int32_t displayId) { + std::scoped_lock _l(mLock); + const int32_t resolvedDisplayId = getTargetMouseDisplayLocked(displayId); + if (auto it = mMousePointersByDisplay.find(resolvedDisplayId); + it != mMousePointersByDisplay.end()) { + return it->second->getPosition(); + } + return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION}; +} + +PointerChoreographer::ControllerConstructor PointerChoreographer::getMouseControllerConstructor( + int32_t displayId) { + std::function<std::shared_ptr<PointerControllerInterface>()> ctor = + [this, displayId]() REQUIRES(mLock) { + auto pc = mPolicy.createPointerController( + PointerControllerInterface::ControllerType::MOUSE); + if (const auto viewport = findViewportByIdLocked(displayId); viewport) { + pc->setDisplayViewport(*viewport); + } + return pc; + }; + return ConstructorDelegate(std::move(ctor)); } } // namespace android diff --git a/services/inputflinger/PointerChoreographer.h b/services/inputflinger/PointerChoreographer.h index 5e5f78257b..c1b900f8a5 100644 --- a/services/inputflinger/PointerChoreographer.h +++ b/services/inputflinger/PointerChoreographer.h @@ -20,9 +20,26 @@ #include "NotifyArgs.h" #include "PointerChoreographerPolicyInterface.h" +#include <android-base/thread_annotations.h> +#include <type_traits> + namespace android { /** + * A helper class that wraps a factory method that acts as a constructor for the type returned + * by the factory method. + */ +template <typename Factory> +struct ConstructorDelegate { + constexpr ConstructorDelegate(Factory&& factory) : mFactory(std::move(factory)) {} + + using ConstructedType = std::invoke_result_t<const Factory&>; + constexpr operator ConstructedType() const { return mFactory(); } + + Factory mFactory; +}; + +/** * PointerChoreographer manages the icons shown by the system for input interactions. * This includes showing the mouse cursor, stylus hover icons, and touch spots. * It is responsible for accumulating the location of the mouse cursor, and populating @@ -31,6 +48,15 @@ namespace android { class PointerChoreographerInterface : public InputListenerInterface { public: /** + * Set the display that pointers, like the mouse cursor and drawing tablets, + * should be drawn on. + */ + virtual void setDefaultMouseDisplayId(int32_t displayId) = 0; + virtual void setDisplayViewports(const std::vector<DisplayViewport>& viewports) = 0; + virtual std::optional<DisplayViewport> getViewportForPointerDevice( + int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0; + virtual FloatPoint getMouseCursorPosition(int32_t displayId) = 0; + /** * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; @@ -42,6 +68,12 @@ public: PointerChoreographerPolicyInterface&); ~PointerChoreographer() override = default; + void setDefaultMouseDisplayId(int32_t displayId) override; + void setDisplayViewports(const std::vector<DisplayViewport>& viewports) override; + std::optional<DisplayViewport> getViewportForPointerDevice( + int32_t associatedDisplayId) override; + FloatPoint getMouseCursorPosition(int32_t displayId) override; + void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; void notifyKey(const NotifyKeyArgs& args) override; @@ -55,7 +87,31 @@ public: void dump(std::string& dump) override; private: + void updatePointerControllersLocked() REQUIRES(mLock); + void notifyPointerDisplayIdChangedLocked() REQUIRES(mLock); + const DisplayViewport* findViewportByIdLocked(int32_t displayId) const REQUIRES(mLock); + int32_t getTargetMouseDisplayLocked(int32_t associatedDisplayId) const REQUIRES(mLock); + + NotifyMotionArgs processMotion(const NotifyMotionArgs& args); + NotifyMotionArgs processMouseEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + NotifyMotionArgs processTouchscreenEventLocked(const NotifyMotionArgs& args) REQUIRES(mLock); + + using ControllerConstructor = + ConstructorDelegate<std::function<std::shared_ptr<PointerControllerInterface>()>>; + ControllerConstructor getMouseControllerConstructor(int32_t displayId) REQUIRES(mLock); + + std::mutex mLock; + InputListenerInterface& mNextListener; + PointerChoreographerPolicyInterface& mPolicy; + + std::map<int32_t, std::shared_ptr<PointerControllerInterface>> mMousePointersByDisplay + GUARDED_BY(mLock); + + int32_t mDefaultMouseDisplayId GUARDED_BY(mLock); + int32_t mNotifiedPointerDisplayId GUARDED_BY(mLock); + std::vector<InputDeviceInfo> mInputDeviceInfos GUARDED_BY(mLock); + std::vector<DisplayViewport> mViewports GUARDED_BY(mLock); }; } // namespace android diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp index ee0ab33559..d9d0450148 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp +++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp @@ -15,10 +15,15 @@ */ #include "PreferStylusOverTouchBlocker.h" +#include <com_android_input_flags.h> #include <input/PrintTools.h> +namespace input_flags = com::android::input::flags; + namespace android { +const bool BLOCK_TOUCH_WHEN_STYLUS_HOVER = !input_flags::disable_reject_touch_on_stylus_hover(); + static std::pair<bool, bool> checkToolType(const NotifyMotionArgs& args) { bool hasStylus = false; bool hasTouch = false; @@ -96,8 +101,11 @@ static void intersectInPlace(std::map<K, V>& map, const std::set<K>& set2) { std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( const NotifyMotionArgs& args) { const auto [hasTouch, hasStylus] = checkToolType(args); - const bool isUpOrCancel = - args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL; + const bool isDisengageOrCancel = BLOCK_TOUCH_WHEN_STYLUS_HOVER + ? (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL) + : (args.action == AMOTION_EVENT_ACTION_UP || + args.action == AMOTION_EVENT_ACTION_CANCEL); if (hasTouch && hasStylus) { mDevicesWithMixedToolType.insert(args.deviceId); @@ -109,7 +117,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) { // If we started to cancel events from this device, continue to do so to keep // the stream consistent. It should happen at most once per "mixed" device. - if (isUpOrCancel) { + if (isDisengageOrCancel) { mCanceledDevices.erase(args.deviceId); mLastTouchEvents.erase(args.deviceId); } @@ -119,10 +127,13 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( } const bool isStylusEvent = hasStylus; - const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN; + const bool isEngage = BLOCK_TOUCH_WHEN_STYLUS_HOVER + ? (args.action == AMOTION_EVENT_ACTION_DOWN || + args.action == AMOTION_EVENT_ACTION_HOVER_ENTER) + : (args.action == AMOTION_EVENT_ACTION_DOWN); if (isStylusEvent) { - if (isDown) { + if (isEngage) { // Reject all touch while stylus is down mActiveStyli.insert(args.deviceId); @@ -143,7 +154,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( result.push_back(args); return result; } - if (isUpOrCancel) { + if (isDisengageOrCancel) { mActiveStyli.erase(args.deviceId); } // Never drop stylus events @@ -158,7 +169,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( } const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end(); - if (isUpOrCancel) { + if (isDisengageOrCancel) { mCanceledDevices.erase(args.deviceId); mLastTouchEvents.erase(args.deviceId); } @@ -169,7 +180,7 @@ std::vector<NotifyMotionArgs> PreferStylusOverTouchBlocker::processMotion( return {}; } - if (!isUpOrCancel) { + if (!isDisengageOrCancel) { mLastTouchEvents[args.deviceId] = args; } return {args}; diff --git a/services/inputflinger/TEST_MAPPING b/services/inputflinger/TEST_MAPPING index 6f092a6098..513cbfa7cc 100644 --- a/services/inputflinger/TEST_MAPPING +++ b/services/inputflinger/TEST_MAPPING @@ -148,7 +148,7 @@ ] } ], - "hwasan-postsubmit": [ + "postsubmit": [ { "name": "CtsWindowManagerDeviceWindow", "options": [ @@ -281,6 +281,9 @@ "include-filter": "android.security.cts.Poc19_03#testPocBug_115739809" } ] + }, + { + "name": "CtsInputHostTestCases" } ] } diff --git a/services/inputflinger/dispatcher/DebugConfig.h b/services/inputflinger/dispatcher/DebugConfig.h index c7d98ab31a..c889b9b136 100644 --- a/services/inputflinger/dispatcher/DebugConfig.h +++ b/services/inputflinger/dispatcher/DebugConfig.h @@ -69,6 +69,16 @@ const bool DEBUG_INJECTION = android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "Injection"); /** + * Generally, we always log whenever events are dropped. However, to reduce logspam, some messages + * are suppressed. + * Log additional debug messages about dropped input events with this flag. + * Enable this via "adb shell setprop log.tag.InputDispatcherDroppedEventsVerbose DEBUG". + * Requires system_server restart via `adb shell stop && adb shell start`. + */ +const bool DEBUG_DROPPED_EVENTS_VERBOSE = + android::base::ShouldLog(android::base::LogSeverity::DEBUG, LOG_TAG "DroppedEventsVerbose"); + +/** * Log debug messages about input focus tracking. * Enable this via "adb shell setprop log.tag.InputDispatcherFocus DEBUG" (requires restart) */ diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 30e68024f9..2d1a22b3e6 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -67,24 +67,11 @@ EventEntry::EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policy injectionState(nullptr), dispatchInProgress(false) {} -EventEntry::~EventEntry() { - releaseInjectionState(); -} - -void EventEntry::releaseInjectionState() { - if (injectionState) { - injectionState->release(); - injectionState = nullptr; - } -} - // --- ConfigurationChangedEntry --- ConfigurationChangedEntry::ConfigurationChangedEntry(int32_t id, nsecs_t eventTime) : EventEntry(id, Type::CONFIGURATION_CHANGED, eventTime, 0) {} -ConfigurationChangedEntry::~ConfigurationChangedEntry() {} - std::string ConfigurationChangedEntry::getDescription() const { return StringPrintf("ConfigurationChangedEvent(), policyFlags=0x%08x", policyFlags); } @@ -94,8 +81,6 @@ std::string ConfigurationChangedEntry::getDescription() const { DeviceResetEntry::DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId) : EventEntry(id, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {} -DeviceResetEntry::~DeviceResetEntry() {} - std::string DeviceResetEntry::getDescription() const { return StringPrintf("DeviceResetEvent(deviceId=%d), policyFlags=0x%08x", deviceId, policyFlags); } @@ -110,8 +95,6 @@ FocusEntry::FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToke hasFocus(hasFocus), reason(reason) {} -FocusEntry::~FocusEntry() {} - std::string FocusEntry::getDescription() const { return StringPrintf("FocusEvent(hasFocus=%s)", hasFocus ? "true" : "false"); } @@ -125,8 +108,6 @@ PointerCaptureChangedEntry::PointerCaptureChangedEntry(int32_t id, nsecs_t event : EventEntry(id, Type::POINTER_CAPTURE_CHANGED, eventTime, POLICY_FLAG_PASS_TO_USER), pointerCaptureRequest(request) {} -PointerCaptureChangedEntry::~PointerCaptureChangedEntry() {} - std::string PointerCaptureChangedEntry::getDescription() const { return StringPrintf("PointerCaptureChangedEvent(pointerCaptureEnabled=%s)", pointerCaptureRequest.enable ? "true" : "false"); @@ -143,18 +124,16 @@ DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, x(x), y(y) {} -DragEntry::~DragEntry() {} - std::string DragEntry::getDescription() const { return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y); } // --- KeyEntry --- -KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags, - int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount, - nsecs_t downTime) +KeyEntry::KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, + int32_t metaState, int32_t repeatCount, nsecs_t downTime) : EventEntry(id, Type::KEY, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -168,9 +147,9 @@ KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t sou downTime(downTime), syntheticRepeat(false), interceptKeyResult(KeyEntry::InterceptKeyResult::UNKNOWN), - interceptKeyWakeupTime(0) {} - -KeyEntry::~KeyEntry() {} + interceptKeyWakeupTime(0) { + EventEntry::injectionState = std::move(injectionState); +} std::string KeyEntry::getDescription() const { if (!IS_DEBUGGABLE_BUILD) { @@ -185,15 +164,6 @@ std::string KeyEntry::getDescription() const { keyCode, scanCode, metaState, repeatCount, policyFlags); } -void KeyEntry::recycle() { - releaseInjectionState(); - - dispatchInProgress = false; - syntheticRepeat = false; - interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; - interceptKeyWakeupTime = 0; -} - // --- TouchModeEntry --- TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId) @@ -201,21 +171,19 @@ TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, inTouchMode(inTouchMode), displayId(displayId) {} -TouchModeEntry::~TouchModeEntry() {} - std::string TouchModeEntry::getDescription() const { return StringPrintf("TouchModeEvent(inTouchMode=%s)", inTouchMode ? "true" : "false"); } // --- MotionEntry --- -MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, - int32_t displayId, uint32_t policyFlags, int32_t action, - int32_t actionButton, int32_t flags, int32_t metaState, - int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, - float xCursorPosition, float yCursorPosition, nsecs_t downTime, - const std::vector<PointerProperties>& pointerProperties, +MotionEntry::MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, + nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, + uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, + int32_t metaState, int32_t buttonState, + MotionClassification classification, int32_t edgeFlags, float xPrecision, + float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties, const std::vector<PointerCoords>& pointerCoords) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), @@ -234,9 +202,9 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 yCursorPosition(yCursorPosition), downTime(downTime), pointerProperties(pointerProperties), - pointerCoords(pointerCoords) {} - -MotionEntry::~MotionEntry() {} + pointerCoords(pointerCoords) { + EventEntry::injectionState = std::move(injectionState); +} std::string MotionEntry::getDescription() const { if (!IS_DEBUGGABLE_BUILD) { @@ -285,8 +253,6 @@ SensorEntry::SensorEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 hwTimestamp(hwTimestamp), values(std::move(values)) {} -SensorEntry::~SensorEntry() {} - std::string SensorEntry::getDescription() const { std::string msg; msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, " diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index b341784eda..d44a211535 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -48,7 +48,7 @@ struct EventEntry { Type type; nsecs_t eventTime; uint32_t policyFlags; - InjectionState* injectionState; + std::shared_ptr<InjectionState> injectionState; bool dispatchInProgress; // initially false, set to true while dispatching @@ -72,17 +72,12 @@ struct EventEntry { virtual std::string getDescription() const = 0; EventEntry(int32_t id, Type type, nsecs_t eventTime, uint32_t policyFlags); - virtual ~EventEntry(); - -protected: - void releaseInjectionState(); + virtual ~EventEntry() = default; }; struct ConfigurationChangedEntry : EventEntry { explicit ConfigurationChangedEntry(int32_t id, nsecs_t eventTime); std::string getDescription() const override; - - ~ConfigurationChangedEntry() override; }; struct DeviceResetEntry : EventEntry { @@ -90,8 +85,6 @@ struct DeviceResetEntry : EventEntry { DeviceResetEntry(int32_t id, nsecs_t eventTime, int32_t deviceId); std::string getDescription() const override; - - ~DeviceResetEntry() override; }; struct FocusEntry : EventEntry { @@ -102,8 +95,6 @@ struct FocusEntry : EventEntry { FocusEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool hasFocus, const std::string& reason); std::string getDescription() const override; - - ~FocusEntry() override; }; struct PointerCaptureChangedEntry : EventEntry { @@ -111,8 +102,6 @@ struct PointerCaptureChangedEntry : EventEntry { PointerCaptureChangedEntry(int32_t id, nsecs_t eventTime, const PointerCaptureRequest&); std::string getDescription() const override; - - ~PointerCaptureChangedEntry() override; }; struct DragEntry : EventEntry { @@ -123,8 +112,6 @@ struct DragEntry : EventEntry { DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x, float y); std::string getDescription() const override; - - ~DragEntry() override; }; struct KeyEntry : EventEntry { @@ -150,13 +137,11 @@ struct KeyEntry : EventEntry { InterceptKeyResult interceptKeyResult; // set based on the interception result nsecs_t interceptKeyWakeupTime; // used with INTERCEPT_KEY_RESULT_TRY_AGAIN_LATER - KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, - int32_t metaState, int32_t repeatCount, nsecs_t downTime); + KeyEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, + int32_t repeatCount, nsecs_t downTime); std::string getDescription() const override; - void recycle(); - - ~KeyEntry() override; }; struct MotionEntry : EventEntry { @@ -180,16 +165,14 @@ struct MotionEntry : EventEntry { size_t getPointerCount() const { return pointerProperties.size(); } - MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source, int32_t displayId, - uint32_t policyFlags, int32_t action, int32_t actionButton, int32_t flags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, nsecs_t downTime, - const std::vector<PointerProperties>& pointerProperties, + MotionEntry(int32_t id, std::shared_ptr<InjectionState> injectionState, nsecs_t eventTime, + int32_t deviceId, uint32_t source, int32_t displayId, uint32_t policyFlags, + int32_t action, int32_t actionButton, int32_t flags, int32_t metaState, + int32_t buttonState, MotionClassification classification, int32_t edgeFlags, + float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, + nsecs_t downTime, const std::vector<PointerProperties>& pointerProperties, const std::vector<PointerCoords>& pointerCoords); std::string getDescription() const override; - - ~MotionEntry() override; }; std::ostream& operator<<(std::ostream& out, const MotionEntry& motionEntry); @@ -209,8 +192,6 @@ struct SensorEntry : EventEntry { InputDeviceSensorAccuracy accuracy, bool accuracyChanged, std::vector<float> values); std::string getDescription() const override; - - ~SensorEntry() override; }; struct TouchModeEntry : EventEntry { @@ -219,8 +200,6 @@ struct TouchModeEntry : EventEntry { TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int32_t displayId); std::string getDescription() const override; - - ~TouchModeEntry() override; }; // Tracks the progress of dispatching a particular event to a particular connection. diff --git a/services/inputflinger/dispatcher/InjectionState.cpp b/services/inputflinger/dispatcher/InjectionState.cpp index 053594b253..f693dcf8e4 100644 --- a/services/inputflinger/dispatcher/InjectionState.cpp +++ b/services/inputflinger/dispatcher/InjectionState.cpp @@ -20,22 +20,10 @@ namespace android::inputdispatcher { -InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid) - : refCount(1), - targetUid(targetUid), +InjectionState::InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync) + : targetUid(targetUid), + injectionIsAsync(isAsync), injectionResult(android::os::InputEventInjectionResult::PENDING), - injectionIsAsync(false), pendingForegroundDispatches(0) {} -InjectionState::~InjectionState() {} - -void InjectionState::release() { - refCount -= 1; - if (refCount == 0) { - delete this; - } else { - ALOG_ASSERT(refCount > 0); - } -} - } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InjectionState.h b/services/inputflinger/dispatcher/InjectionState.h index 3a3f5aefba..8225dec87a 100644 --- a/services/inputflinger/dispatcher/InjectionState.h +++ b/services/inputflinger/dispatcher/InjectionState.h @@ -24,18 +24,12 @@ namespace android { namespace inputdispatcher { struct InjectionState { - mutable int32_t refCount; - - std::optional<gui::Uid> targetUid; + const std::optional<gui::Uid> targetUid; + const bool injectionIsAsync; // set to true if injection is not waiting for the result android::os::InputEventInjectionResult injectionResult; // initially PENDING - bool injectionIsAsync; // set to true if injection is not waiting for the result int32_t pendingForegroundDispatches; // the number of foreground dispatches in progress - explicit InjectionState(const std::optional<gui::Uid>& targetUid); - void release(); - -private: - ~InjectionState(); + explicit InjectionState(const std::optional<gui::Uid>& targetUid, bool isAsync); }; } // namespace inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 53e0855f30..c8528e1a03 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -72,6 +72,8 @@ using android::os::InputEventInjectionResult; using android::os::InputEventInjectionSync; namespace input_flags = com::android::input::flags; +static const bool REMOVE_APP_SWITCH_DROPS = input_flags::remove_app_switch_drops(); + namespace android::inputdispatcher { namespace { @@ -390,21 +392,17 @@ std::unique_ptr<DispatchEntry> createDispatchEntry( } std::unique_ptr<MotionEntry> combinedMotionEntry = - std::make_unique<MotionEntry>(motionEntry.id, motionEntry.eventTime, - motionEntry.deviceId, motionEntry.source, - motionEntry.displayId, motionEntry.policyFlags, - motionEntry.action, motionEntry.actionButton, - motionEntry.flags, motionEntry.metaState, - motionEntry.buttonState, motionEntry.classification, - motionEntry.edgeFlags, motionEntry.xPrecision, - motionEntry.yPrecision, motionEntry.xCursorPosition, - motionEntry.yCursorPosition, motionEntry.downTime, - motionEntry.pointerProperties, pointerCoords); - - if (motionEntry.injectionState) { - combinedMotionEntry->injectionState = motionEntry.injectionState; - combinedMotionEntry->injectionState->refCount += 1; - } + std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState, + motionEntry.eventTime, motionEntry.deviceId, + motionEntry.source, motionEntry.displayId, + motionEntry.policyFlags, motionEntry.action, + motionEntry.actionButton, motionEntry.flags, + motionEntry.metaState, motionEntry.buttonState, + motionEntry.classification, motionEntry.edgeFlags, + motionEntry.xPrecision, motionEntry.yPrecision, + motionEntry.xCursorPosition, motionEntry.yCursorPosition, + motionEntry.downTime, motionEntry.pointerProperties, + pointerCoords); std::unique_ptr<DispatchEntry> dispatchEntry = std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags, @@ -756,10 +754,6 @@ bool shouldSplitTouch(const TouchState& touchState, const MotionEntry& entry) { // --- InputDispatcher --- InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) - : InputDispatcher(policy, STALE_EVENT_TIMEOUT) {} - -InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, - std::chrono::nanoseconds staleEventTimeout) : mPolicy(policy), mPendingEvent(nullptr), mLastDropReason(DropReason::NOT_DROPPED), @@ -774,7 +768,6 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy, mMaximumObscuringOpacityForTouch(1.0f), mFocusedDisplayId(ADISPLAY_ID_DEFAULT), mWindowTokenWithPointerCapture(nullptr), - mStaleEventTimeout(staleEventTimeout), mLatencyAggregator(), mLatencyTracker(&mLatencyAggregator) { mLooper = sp<Looper>::make(false); @@ -956,20 +949,25 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { // Optimize latency of app switches. // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has // been pressed. When it expires, we preempt dispatch and drop all other pending events. - bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; + bool isAppSwitchDue; + if (!REMOVE_APP_SWITCH_DROPS) { + isAppSwitchDue = mAppSwitchDueTime <= currentTime; + if (mAppSwitchDueTime < *nextWakeupTime) { + *nextWakeupTime = mAppSwitchDueTime; + } } // Ready to start a new event. // If we don't already have a pending event, go grab one. if (!mPendingEvent) { if (mInboundQueue.empty()) { - if (isAppSwitchDue) { - // The inbound queue is empty so the app switch key we were waiting - // for will never arrive. Stop waiting for it. - resetPendingAppSwitchLocked(false); - isAppSwitchDue = false; + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchDue) { + // The inbound queue is empty so the app switch key we were waiting + // for will never arrive. Stop waiting for it. + resetPendingAppSwitchLocked(false); + isAppSwitchDue = false; + } } // Synthesize a key repeat if appropriate. @@ -1067,12 +1065,14 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::KEY: { std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent); - if (isAppSwitchDue) { - if (isAppSwitchKeyEvent(*keyEntry)) { - resetPendingAppSwitchLocked(true); - isAppSwitchDue = false; - } else if (dropReason == DropReason::NOT_DROPPED) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchDue) { + if (isAppSwitchKeyEvent(*keyEntry)) { + resetPendingAppSwitchLocked(true); + isAppSwitchDue = false; + } else if (dropReason == DropReason::NOT_DROPPED) { + dropReason = DropReason::APP_SWITCH; + } } } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *keyEntry)) { @@ -1088,8 +1088,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::MOTION: { std::shared_ptr<MotionEntry> motionEntry = std::static_pointer_cast<MotionEntry>(mPendingEvent); - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; + } } if (dropReason == DropReason::NOT_DROPPED && isStaleEvent(currentTime, *motionEntry)) { dropReason = DropReason::STALE; @@ -1104,8 +1106,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::Type::SENSOR: { std::shared_ptr<SensorEntry> sensorEntry = std::static_pointer_cast<SensorEntry>(mPendingEvent); - if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { - dropReason = DropReason::APP_SWITCH; + if (!REMOVE_APP_SWITCH_DROPS) { + if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) { + dropReason = DropReason::APP_SWITCH; + } } // Sensor timestamps use SYSTEM_TIME_BOOTTIME time base, so we can't use // 'currentTime' here, get SYSTEM_TIME_BOOTTIME instead. @@ -1131,7 +1135,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } bool InputDispatcher::isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { - return std::chrono::nanoseconds(currentTime - entry.eventTime) >= mStaleEventTimeout; + return mPolicy.isStaleEvent(currentTime, entry.eventTime); } /** @@ -1207,21 +1211,23 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE // If the application takes too long to catch up then we drop all events preceding // the app switch key. const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry); - if (isAppSwitchKeyEvent(keyEntry)) { - if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { - mAppSwitchSawKeyDown = true; - } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { - if (mAppSwitchSawKeyDown) { - if (DEBUG_APP_SWITCH) { - ALOGD("App switch is pending!"); + + if (!REMOVE_APP_SWITCH_DROPS) { + if (isAppSwitchKeyEvent(keyEntry)) { + if (keyEntry.action == AKEY_EVENT_ACTION_DOWN) { + mAppSwitchSawKeyDown = true; + } else if (keyEntry.action == AKEY_EVENT_ACTION_UP) { + if (mAppSwitchSawKeyDown) { + if (DEBUG_APP_SWITCH) { + ALOGD("App switch is pending!"); + } + mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; + mAppSwitchSawKeyDown = false; + needWake = true; } - mAppSwitchDueTime = keyEntry.eventTime + APP_SWITCH_TIMEOUT; - mAppSwitchSawKeyDown = false; - needWake = true; } } } - // If a new up event comes in, and the pending event with same key code has been asked // to try again later because of the policy. We have to reset the intercept key wake up // time for it may have been handled in the policy and could be dropped. @@ -1482,7 +1488,7 @@ void InputDispatcher::releasePendingEventLocked() { } void InputDispatcher::releaseInboundEventLocked(std::shared_ptr<EventEntry> entry) { - InjectionState* injectionState = entry->injectionState; + const std::shared_ptr<InjectionState>& injectionState = entry->injectionState; if (injectionState && injectionState->injectionResult == InputEventInjectionResult::PENDING) { if (DEBUG_DISPATCH_CYCLE) { ALOGD("Injected inbound event was dropped."); @@ -1508,10 +1514,11 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur (POLICY_FLAG_RAW_MASK | POLICY_FLAG_PASS_TO_USER | POLICY_FLAG_TRUSTED); std::shared_ptr<KeyEntry> newEntry = - std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, entry->deviceId, - entry->source, entry->displayId, policyFlags, entry->action, - entry->flags, entry->keyCode, entry->scanCode, - entry->metaState, entry->repeatCount + 1, entry->downTime); + std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + currentTime, entry->deviceId, entry->source, + entry->displayId, policyFlags, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, + entry->repeatCount + 1, entry->downTime); newEntry->syntheticRepeat = true; mKeyRepeatState.lastKeyEntry = newEntry; @@ -2032,10 +2039,10 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, if (connection != nullptr) { prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget); } else { - if (DEBUG_FOCUS) { - ALOGD("Dropping event delivery to target with channel '%s' because it " - "is no longer registered with the input dispatcher.", - inputTarget.inputChannel->getName().c_str()); + if (DEBUG_DROPPED_EVENTS_VERBOSE) { + LOG(INFO) << "Dropping event delivery to target with channel " + << inputTarget.inputChannel->getName() + << " because it is no longer registered with the input dispatcher."; } } } @@ -2453,10 +2460,11 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked( // If the pointer is not currently down, then ignore the event. if (!tempTouchState.isDown(entry.deviceId) && maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT) { - LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId - << " is not down or we previously " - "dropped the pointer down event in display " - << displayId << ": " << entry.getDescription(); + if (DEBUG_DROPPED_EVENTS_VERBOSE) { + LOG(INFO) << "Dropping event because the pointer for device " << entry.deviceId + << " is not down or we previously dropped the pointer down event in " + << "display " << displayId << ": " << entry.getDescription(); + } outInjectionResult = InputEventInjectionResult::FAILED; return {}; } @@ -2864,8 +2872,13 @@ void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHa it = inputTargets.end() - 1; } - LOG_ALWAYS_FATAL_IF(it->flags != targetFlags); - LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); + if (it->flags != targetFlags) { + LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it; + } + if (it->globalScaleFactor != windowInfo->globalScaleFactor) { + LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor + << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor; + } } void InputDispatcher::addPointerWindowTargetLocked( @@ -2911,10 +2924,12 @@ void InputDispatcher::addPointerWindowTargetLocked( } if (it->flags != targetFlags) { - logDispatchStateLocked(); - LOG(FATAL) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it; + LOG(ERROR) << "Flags don't match! targetFlags=" << targetFlags.string() << ", it=" << *it; + } + if (it->globalScaleFactor != windowInfo->globalScaleFactor) { + LOG(ERROR) << "Mismatch! it->globalScaleFactor=" << it->globalScaleFactor + << ", windowInfo->globalScaleFactor=" << windowInfo->globalScaleFactor; } - LOG_ALWAYS_FATAL_IF(it->globalScaleFactor != windowInfo->globalScaleFactor); it->addPointers(pointerIds, windowInfo->transform); } @@ -4229,7 +4244,8 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( ").", originalMotionEntry.id, newId)); std::unique_ptr<MotionEntry> splitMotionEntry = - std::make_unique<MotionEntry>(newId, originalMotionEntry.eventTime, + std::make_unique<MotionEntry>(newId, originalMotionEntry.injectionState, + originalMotionEntry.eventTime, originalMotionEntry.deviceId, originalMotionEntry.source, originalMotionEntry.displayId, originalMotionEntry.policyFlags, action, @@ -4244,11 +4260,6 @@ std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( originalMotionEntry.yCursorPosition, splitDownTime, splitPointerProperties, splitPointerCoords); - if (originalMotionEntry.injectionState) { - splitMotionEntry->injectionState = originalMotionEntry.injectionState; - splitMotionEntry->injectionState->refCount += 1; - } - return splitMotionEntry; } @@ -4336,9 +4347,10 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { } std::unique_ptr<KeyEntry> newEntry = - std::make_unique<KeyEntry>(args.id, args.eventTime, args.deviceId, args.source, - args.displayId, policyFlags, args.action, flags, keyCode, - args.scanCode, metaState, repeatCount, args.downTime); + std::make_unique<KeyEntry>(args.id, /*injectionState=*/nullptr, args.eventTime, + args.deviceId, args.source, args.displayId, policyFlags, + args.action, flags, keyCode, args.scanCode, metaState, + repeatCount, args.downTime); needWake = enqueueInboundEventLocked(std::move(newEntry)); mLock.unlock(); @@ -4422,7 +4434,8 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { const auto touchStateIt = mTouchStatesByDisplay.find(args.displayId); if (touchStateIt != mTouchStatesByDisplay.end()) { const TouchState& touchState = touchStateIt->second; - if (touchState.hasTouchingPointers(args.deviceId)) { + if (touchState.hasTouchingPointers(args.deviceId) || + touchState.hasHoveringPointers(args.deviceId)) { policyFlags |= POLICY_FLAG_PASS_TO_USER; } } @@ -4455,14 +4468,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { // Just enqueue a new motion event. std::unique_ptr<MotionEntry> newEntry = - std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source, - args.displayId, policyFlags, args.action, - args.actionButton, args.flags, args.metaState, - args.buttonState, args.classification, args.edgeFlags, - args.xPrecision, args.yPrecision, - args.xCursorPosition, args.yCursorPosition, - args.downTime, args.pointerProperties, - args.pointerCoords); + std::make_unique<MotionEntry>(args.id, /*injectionState=*/nullptr, args.eventTime, + args.deviceId, args.source, args.displayId, + policyFlags, args.action, args.actionButton, + args.flags, args.metaState, args.buttonState, + args.classification, args.edgeFlags, args.xPrecision, + args.yPrecision, args.xCursorPosition, + args.yCursorPosition, args.downTime, + args.pointerProperties, args.pointerCoords); if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER && @@ -4534,6 +4547,7 @@ void InputDispatcher::notifySwitch(const NotifySwitchArgs& args) { } void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs& args) { + // TODO(b/308677868) Remove device reset from the InputListener interface if (debugInboundEventDetails()) { ALOGD("notifyDeviceReset - eventTime=%" PRId64 ", deviceId=%d", args.eventTime, args.deviceId); @@ -4608,6 +4622,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev resolvedDeviceId = event->getDeviceId(); } + const bool isAsync = syncMode == InputEventInjectionSync::NONE; + auto injectionState = std::make_shared<InjectionState>(targetUid, isAsync); + std::queue<std::unique_ptr<EventEntry>> injectedEntries; switch (event->getType()) { case InputEventType::KEY: { @@ -4640,10 +4657,11 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev mLock.lock(); std::unique_ptr<KeyEntry> injectedEntry = - std::make_unique<KeyEntry>(incomingKey.getId(), incomingKey.getEventTime(), - resolvedDeviceId, incomingKey.getSource(), - incomingKey.getDisplayId(), policyFlags, action, - flags, keyCode, incomingKey.getScanCode(), metaState, + std::make_unique<KeyEntry>(incomingKey.getId(), injectionState, + incomingKey.getEventTime(), resolvedDeviceId, + incomingKey.getSource(), incomingKey.getDisplayId(), + policyFlags, action, flags, keyCode, + incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime()); injectedEntries.push(std::move(injectedEntry)); @@ -4675,6 +4693,30 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev } mLock.lock(); + + if (policyFlags & POLICY_FLAG_FILTERED) { + // The events from InputFilter impersonate real hardware devices. Check these + // events for consistency and print an error. An inconsistent event sent from + // InputFilter could cause a crash in the later stages of dispatching pipeline. + auto [it, _] = + mInputFilterVerifiersByDisplay + .try_emplace(displayId, + StringPrintf("Injection on %" PRId32, displayId)); + InputVerifier& verifier = it->second; + + Result<void> result = + verifier.processMovement(resolvedDeviceId, motionEvent.getSource(), + motionEvent.getAction(), + motionEvent.getPointerCount(), + motionEvent.getPointerProperties(), + motionEvent.getSamplePointerCoords(), flags); + if (!result.ok()) { + logDispatchStateLocked(); + LOG(ERROR) << "Inconsistent event: " << motionEvent + << ", reason: " << result.error(); + } + } + const nsecs_t* sampleEventTimes = motionEvent.getSampleEventTimes(); const size_t pointerCount = motionEvent.getPointerCount(); const std::vector<PointerProperties> @@ -4683,9 +4725,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev const PointerCoords* samplePointerCoords = motionEvent.getSamplePointerCoords(); std::unique_ptr<MotionEntry> injectedEntry = - std::make_unique<MotionEntry>(motionEvent.getId(), *sampleEventTimes, - resolvedDeviceId, motionEvent.getSource(), - displayId, policyFlags, motionEvent.getAction(), + std::make_unique<MotionEntry>(motionEvent.getId(), injectionState, + *sampleEventTimes, resolvedDeviceId, + motionEvent.getSource(), displayId, policyFlags, + motionEvent.getAction(), motionEvent.getActionButton(), flags, motionEvent.getMetaState(), motionEvent.getButtonState(), @@ -4705,9 +4748,10 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev sampleEventTimes += 1; samplePointerCoords += motionEvent.getPointerCount(); std::unique_ptr<MotionEntry> nextInjectedEntry = std::make_unique< - MotionEntry>(motionEvent.getId(), *sampleEventTimes, resolvedDeviceId, - motionEvent.getSource(), displayId, policyFlags, - motionEvent.getAction(), motionEvent.getActionButton(), flags, + MotionEntry>(motionEvent.getId(), injectionState, *sampleEventTimes, + resolvedDeviceId, motionEvent.getSource(), displayId, + policyFlags, motionEvent.getAction(), + motionEvent.getActionButton(), flags, motionEvent.getMetaState(), motionEvent.getButtonState(), motionEvent.getClassification(), motionEvent.getEdgeFlags(), motionEvent.getXPrecision(), motionEvent.getYPrecision(), @@ -4729,14 +4773,6 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev return InputEventInjectionResult::FAILED; } - InjectionState* injectionState = new InjectionState(targetUid); - if (syncMode == InputEventInjectionSync::NONE) { - injectionState->injectionIsAsync = true; - } - - injectionState->refCount += 1; - injectedEntries.back()->injectionState = injectionState; - bool needWake = false; while (!injectedEntries.empty()) { if (DEBUG_INJECTION) { @@ -4799,8 +4835,6 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev } } } - - injectionState->release(); } // release lock if (DEBUG_INJECTION) { @@ -4846,37 +4880,40 @@ std::unique_ptr<VerifiedInputEvent> InputDispatcher::verifyInputEvent(const Inpu void InputDispatcher::setInjectionResult(EventEntry& entry, InputEventInjectionResult injectionResult) { - InjectionState* injectionState = entry.injectionState; - if (injectionState) { - if (DEBUG_INJECTION) { - LOG(INFO) << "Setting input event injection result to " - << ftl::enum_string(injectionResult); - } + if (!entry.injectionState) { + // Not an injected event. + return; + } - if (injectionState->injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { - // Log the outcome since the injector did not wait for the injection result. - switch (injectionResult) { - case InputEventInjectionResult::SUCCEEDED: - ALOGV("Asynchronous input event injection succeeded."); - break; - case InputEventInjectionResult::TARGET_MISMATCH: - ALOGV("Asynchronous input event injection target mismatch."); - break; - case InputEventInjectionResult::FAILED: - ALOGW("Asynchronous input event injection failed."); - break; - case InputEventInjectionResult::TIMED_OUT: - ALOGW("Asynchronous input event injection timed out."); - break; - case InputEventInjectionResult::PENDING: - ALOGE("Setting result to 'PENDING' for asynchronous injection"); - break; - } - } + InjectionState& injectionState = *entry.injectionState; + if (DEBUG_INJECTION) { + LOG(INFO) << "Setting input event injection result to " + << ftl::enum_string(injectionResult); + } - injectionState->injectionResult = injectionResult; - mInjectionResultAvailable.notify_all(); + if (injectionState.injectionIsAsync && !(entry.policyFlags & POLICY_FLAG_FILTERED)) { + // Log the outcome since the injector did not wait for the injection result. + switch (injectionResult) { + case InputEventInjectionResult::SUCCEEDED: + ALOGV("Asynchronous input event injection succeeded."); + break; + case InputEventInjectionResult::TARGET_MISMATCH: + ALOGV("Asynchronous input event injection target mismatch."); + break; + case InputEventInjectionResult::FAILED: + ALOGW("Asynchronous input event injection failed."); + break; + case InputEventInjectionResult::TIMED_OUT: + ALOGW("Asynchronous input event injection timed out."); + break; + case InputEventInjectionResult::PENDING: + ALOGE("Setting result to 'PENDING' for asynchronous injection"); + break; + } } + + injectionState.injectionResult = injectionResult; + mInjectionResultAvailable.notify_all(); } void InputDispatcher::transformMotionEntryForInjectionLocked( @@ -4903,18 +4940,16 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( } void InputDispatcher::incrementPendingForegroundDispatches(EventEntry& entry) { - InjectionState* injectionState = entry.injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches += 1; + if (entry.injectionState) { + entry.injectionState->pendingForegroundDispatches += 1; } } void InputDispatcher::decrementPendingForegroundDispatches(EventEntry& entry) { - InjectionState* injectionState = entry.injectionState; - if (injectionState) { - injectionState->pendingForegroundDispatches -= 1; + if (entry.injectionState) { + entry.injectionState->pendingForegroundDispatches -= 1; - if (injectionState->pendingForegroundDispatches == 0) { + if (entry.injectionState->pendingForegroundDispatches == 0) { mInjectionSyncFinished.notify_all(); } } @@ -5800,6 +5835,9 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { dump += INDENT "Connections: <none>\n"; } + dump += "input_flags::remove_app_switch_drops() = "; + dump += toString(REMOVE_APP_SWITCH_DROPS); + dump += "\n"; if (isAppSwitchPendingLocked()) { dump += StringPrintf(INDENT "AppSwitch: pending, due in %" PRId64 "ms\n", ns2ms(mAppSwitchDueTime - now())); @@ -6131,7 +6169,7 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, uint32_t seq, bool handled, nsecs_t consumeTime) { // Handle post-event policy actions. - bool restartEvent; + std::unique_ptr<KeyEntry> fallbackKeyEntry; { // Start critical section auto dispatchEntryIt = @@ -6155,15 +6193,9 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } if (dispatchEntry.eventEntry->type == EventEntry::Type::KEY) { - KeyEntry& keyEntry = static_cast<KeyEntry&>(*(dispatchEntry.eventEntry)); - restartEvent = + const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*(dispatchEntry.eventEntry)); + fallbackKeyEntry = afterKeyEventLockedInterruptable(connection, dispatchEntry, keyEntry, handled); - } else if (dispatchEntry.eventEntry->type == EventEntry::Type::MOTION) { - MotionEntry& motionEntry = static_cast<MotionEntry&>(*(dispatchEntry.eventEntry)); - restartEvent = afterMotionEventLockedInterruptable(connection, dispatchEntry, - motionEntry, handled); - } else { - restartEvent = false; } } // End critical section: The -LockedInterruptable methods may have released the lock. @@ -6187,12 +6219,13 @@ void InputDispatcher::doDispatchCycleFinishedCommand(nsecs_t finishTime, } } traceWaitQueueLength(*connection); - if (restartEvent && connection->status == Connection::Status::NORMAL) { - connection->outboundQueue.emplace_front(std::move(dispatchEntry)); - traceOutboundQueueLength(*connection); - } else { - releaseDispatchEntry(std::move(dispatchEntry)); + if (fallbackKeyEntry && connection->status == Connection::Status::NORMAL) { + const InputTarget target{.inputChannel = connection->inputChannel, + .flags = dispatchEntry->targetFlags}; + enqueueDispatchEntryLocked(connection, std::move(fallbackKeyEntry), target, + InputTarget::Flags::DISPATCH_AS_IS); } + releaseDispatchEntry(std::move(dispatchEntry)); } // Start the next dispatch cycle for this connection. @@ -6377,15 +6410,15 @@ void InputDispatcher::processConnectionResponsiveLocked(const Connection& connec sendWindowResponsiveCommandLocked(connectionToken, pid); } -bool InputDispatcher::afterKeyEventLockedInterruptable( +std::unique_ptr<KeyEntry> InputDispatcher::afterKeyEventLockedInterruptable( const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry, - KeyEntry& keyEntry, bool handled) { + const KeyEntry& keyEntry, bool handled) { if (keyEntry.flags & AKEY_EVENT_FLAG_FALLBACK) { if (!handled) { // Report the key as unhandled, since the fallback was not handled. mReporter->reportUnhandledKey(keyEntry.id); } - return false; + return {}; } // Get the fallback key state. @@ -6445,7 +6478,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( "keyCode=%d, action=%d, repeatCount=%d, policyFlags=0x%08x", originalKeyCode, keyEntry.action, keyEntry.repeatCount, keyEntry.policyFlags); } - return false; + return {}; } // Dispatch the unhandled key to the policy. @@ -6470,7 +6503,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( if (connection->status != Connection::Status::NORMAL) { connection->inputState.removeFallbackKey(originalKeyCode); - return false; + return {}; } // Latch the fallback keycode for this key on an initial down. @@ -6531,25 +6564,22 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( } if (fallback) { - // Restart the dispatch cycle using the fallback key. - keyEntry.eventTime = event.getEventTime(); - keyEntry.deviceId = event.getDeviceId(); - keyEntry.source = event.getSource(); - keyEntry.displayId = event.getDisplayId(); - keyEntry.flags = event.getFlags() | AKEY_EVENT_FLAG_FALLBACK; - keyEntry.keyCode = *fallbackKeyCode; - keyEntry.scanCode = event.getScanCode(); - keyEntry.metaState = event.getMetaState(); - keyEntry.repeatCount = event.getRepeatCount(); - keyEntry.downTime = event.getDownTime(); - keyEntry.syntheticRepeat = false; - + // Return the fallback key that we want dispatched to the channel. + std::unique_ptr<KeyEntry> newEntry = + std::make_unique<KeyEntry>(mIdGenerator.nextId(), keyEntry.injectionState, + event.getEventTime(), event.getDeviceId(), + event.getSource(), event.getDisplayId(), + keyEntry.policyFlags, keyEntry.action, + event.getFlags() | AKEY_EVENT_FLAG_FALLBACK, + *fallbackKeyCode, event.getScanCode(), + event.getMetaState(), event.getRepeatCount(), + event.getDownTime()); if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("Unhandled key event: Dispatching fallback key. " "originalKeyCode=%d, fallbackKeyCode=%d, fallbackMetaState=%08x", originalKeyCode, *fallbackKeyCode, keyEntry.metaState); } - return true; // restart the event + return newEntry; } else { if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("Unhandled key event: No fallback key."); @@ -6559,13 +6589,7 @@ bool InputDispatcher::afterKeyEventLockedInterruptable( mReporter->reportUnhandledKey(keyEntry.id); } } - return false; -} - -bool InputDispatcher::afterMotionEventLockedInterruptable( - const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry, - MotionEntry& motionEntry, bool handled) { - return false; + return {}; } void InputDispatcher::traceInboundQueueLengthLocked() { @@ -6736,6 +6760,7 @@ void InputDispatcher::displayRemoved(int32_t displayId) { // Remove the associated touch mode state. mTouchModePerDisplay.erase(displayId); mVerifiersByDisplay.erase(displayId); + mInputFilterVerifiersByDisplay.erase(displayId); } // release lock // Wake up poll loop since it may need to make new input dispatching choices. diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index a1127a0f45..f0f67721e7 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -82,8 +82,6 @@ public: static constexpr bool kDefaultInTouchMode = true; explicit InputDispatcher(InputDispatcherPolicyInterface& policy); - explicit InputDispatcher(InputDispatcherPolicyInterface& policy, - std::chrono::nanoseconds staleEventTimeout); ~InputDispatcher() override; void dump(std::string& dump) const override; @@ -288,7 +286,8 @@ private: void transformMotionEntryForInjectionLocked(MotionEntry&, const ui::Transform& injectedTransform) const REQUIRES(mLock); - + // Per-display correction of injected events + std::map</*displayId*/ int32_t, InputVerifier> mInputFilterVerifiersByDisplay GUARDED_BY(mLock); std::condition_variable mInjectionSyncFinished; void incrementPendingForegroundDispatches(EventEntry& entry); void decrementPendingForegroundDispatches(EventEntry& entry); @@ -461,9 +460,6 @@ private: */ std::optional<nsecs_t> mNoFocusedWindowTimeoutTime GUARDED_BY(mLock); - // Amount of time to allow for an event to be dispatched (measured since its eventTime) - // before considering it stale and dropping it. - const std::chrono::nanoseconds mStaleEventTimeout; bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry); bool shouldPruneInboundQueueLocked(const MotionEntry& motionEntry) REQUIRES(mLock); @@ -664,12 +660,10 @@ private: void updateLastAnrStateLocked(const std::string& windowLabel, const std::string& reason) REQUIRES(mLock); std::map<int32_t /*displayId*/, InputVerifier> mVerifiersByDisplay; - bool afterKeyEventLockedInterruptable(const std::shared_ptr<Connection>& connection, - DispatchEntry& dispatchEntry, KeyEntry& keyEntry, - bool handled) REQUIRES(mLock); - bool afterMotionEventLockedInterruptable(const std::shared_ptr<Connection>& connection, - DispatchEntry& dispatchEntry, MotionEntry& motionEntry, - bool handled) REQUIRES(mLock); + // Returns a fallback KeyEntry that should be sent to the connection, if required. + std::unique_ptr<KeyEntry> afterKeyEventLockedInterruptable( + const std::shared_ptr<Connection>& connection, DispatchEntry& dispatchEntry, + const KeyEntry& keyEntry, bool handled) REQUIRES(mLock); // Find touched state and touched window by token. std::tuple<TouchState*, TouchedWindow*, int32_t /*displayId*/> diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 09b5186a69..16cc266938 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -421,9 +421,10 @@ std::unique_ptr<MotionEntry> InputState::createCancelEntryForMemento(const Motio if (action == AMOTION_EVENT_ACTION_CANCEL) { flags |= AMOTION_EVENT_FLAG_CANCELED; } - return std::make_unique<MotionEntry>(mIdGenerator.nextId(), eventTime, memento.deviceId, - memento.source, memento.displayId, memento.policyFlags, - action, /*actionButton=*/0, flags, AMETA_NONE, + return std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + eventTime, memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, action, + /*actionButton=*/0, flags, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, @@ -437,9 +438,10 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizeCancelationEvents for (KeyMemento& memento : mKeyMementos) { if (shouldCancelKey(memento, options)) { events.push_back( - std::make_unique<KeyEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId, - memento.source, memento.displayId, - memento.policyFlags, AKEY_EVENT_ACTION_UP, + std::make_unique<KeyEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + currentTime, memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, + AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED, memento.keyCode, memento.scanCode, memento.metaState, /*repeatCount=*/0, memento.downTime)); @@ -498,8 +500,8 @@ std::vector<std::unique_ptr<EventEntry>> InputState::synthesizePointerDownEvents | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); events.push_back( - std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, - memento.deviceId, memento.source, + std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, /*actionButton=*/0, memento.flags, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, @@ -539,11 +541,11 @@ std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEvent flags |= AMOTION_EVENT_FLAG_CANCELED; } events.push_back( - std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, memento.deviceId, - memento.source, memento.displayId, - memento.policyFlags, action, /*actionButton=*/0, - flags, AMETA_NONE, /*buttonState=*/0, - MotionClassification::NONE, + std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + currentTime, memento.deviceId, memento.source, + memento.displayId, memento.policyFlags, action, + /*actionButton=*/0, flags, AMETA_NONE, + /*buttonState=*/0, MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, @@ -564,8 +566,8 @@ std::vector<std::unique_ptr<MotionEntry>> InputState::synthesizeCancelationEvent : AMOTION_EVENT_ACTION_POINTER_UP | (pointerIdx << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); events.push_back( - std::make_unique<MotionEntry>(mIdGenerator.nextId(), currentTime, - memento.deviceId, memento.source, + std::make_unique<MotionEntry>(mIdGenerator.nextId(), /*injectionState=*/nullptr, + currentTime, memento.deviceId, memento.source, memento.displayId, memento.policyFlags, action, /*actionButton=*/0, memento.flags | AMOTION_EVENT_FLAG_CANCELED, diff --git a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h index af28e48121..bf4880480c 100644 --- a/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h +++ b/services/inputflinger/dispatcher/include/InputDispatcherPolicyInterface.h @@ -18,6 +18,7 @@ #include "InputDispatcherConfiguration.h" +#include <android-base/properties.h> #include <binder/IBinder.h> #include <gui/InputApplication.h> #include <input/Input.h> @@ -118,6 +119,16 @@ public: /* Poke user activity for an event dispatched to a window. */ virtual void pokeUserActivity(nsecs_t eventTime, int32_t eventType, int32_t displayId) = 0; + /* + * Return true if the provided event is stale, and false otherwise. Used for determining + * whether the dispatcher should drop the event. + */ + virtual bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) { + static const std::chrono::duration STALE_EVENT_TIMEOUT = + std::chrono::seconds(10) * android::base::HwTimeoutMultiplier(); + return std::chrono::nanoseconds(currentTime - eventTime) >= STALE_EVENT_TIMEOUT; + } + /* Notifies the policy that a pointer down event has occurred outside the current focused * window. * diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index 25e1d21850..efc8b260f3 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -451,6 +451,15 @@ public: /* Returns true if any InputConnection is currently active. */ virtual bool isInputMethodConnectionActive() = 0; + + /* Gets the viewport of a particular display that the pointer device is associated with. If + * the pointer device is not associated with any display, it should ADISPLAY_IS_NONE to get + * the viewport that should be used. The device should get a new viewport using this method + * every time there is a display configuration change. The logical bounds of the viewport should + * be used as the range of possible values for pointing devices, like mice and touchpads. + */ + virtual std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId = ADISPLAY_ID_NONE) = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerChoreographerPolicyInterface.h b/services/inputflinger/include/PointerChoreographerPolicyInterface.h index 9e020c7c7f..8b47b555e5 100644 --- a/services/inputflinger/include/PointerChoreographerPolicyInterface.h +++ b/services/inputflinger/include/PointerChoreographerPolicyInterface.h @@ -38,7 +38,16 @@ public: * library, libinputservice, that has the additional dependencies. The PointerController * will be mocked when testing PointerChoreographer. */ - virtual std::shared_ptr<PointerControllerInterface> createPointerController() = 0; + virtual std::shared_ptr<PointerControllerInterface> createPointerController( + PointerControllerInterface::ControllerType type) = 0; + + /** + * Notifies the policy that the default pointer displayId has changed. PointerChoreographer is + * the single source of truth for all pointers on screen. + * @param displayId The updated display on which the mouse cursor is shown + * @param position The new position of the mouse cursor on the logical display + */ + virtual void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) = 0; }; } // namespace android diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 95f819a8da..8837b25378 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -52,6 +52,22 @@ protected: virtual ~PointerControllerInterface() { } public: + /** + * Enum used to differentiate various types of PointerControllers for the transition to + * using PointerChoreographer. + * + * TODO(b/293587049): Refactor the PointerController class into different controller types. + */ + enum class ControllerType { + // The PointerController that is responsible for drawing all icons. + LEGACY, + // Represents a single mouse pointer. + MOUSE, + }; + + /* Dumps the state of the pointer controller. */ + virtual std::string dump() = 0; + /* Gets the bounds of the region that the pointer can traverse. * Returns true if the bounds are available. */ virtual std::optional<FloatRect> getBounds() const = 0; diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 5766b14231..0582649d38 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -1046,6 +1046,14 @@ bool InputReader::ContextImpl::isPreventingTouchpadTaps() { return mReader->mPreventingTouchpadTaps; } +void InputReader::ContextImpl::setLastKeyDownTimestamp(nsecs_t when) { + mReader->mLastKeyDownTimestamp = when; +} + +nsecs_t InputReader::ContextImpl::getLastKeyDownTimestamp() { + return mReader->mLastKeyDownTimestamp; +} + void InputReader::ContextImpl::disableVirtualKeysUntil(nsecs_t time) { // lock is already held by the input loop mReader->disableVirtualKeysUntilLocked(time); diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 9a297c9ace..4c78db38cc 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -158,6 +158,9 @@ protected: void setPreventingTouchpadTaps(bool prevent) REQUIRES(mReader->mLock) REQUIRES(mLock) override; bool isPreventingTouchpadTaps() REQUIRES(mReader->mLock) REQUIRES(mLock) override; + void setLastKeyDownTimestamp(nsecs_t when) REQUIRES(mReader->mLock) + REQUIRES(mLock) override; + nsecs_t getLastKeyDownTimestamp() REQUIRES(mReader->mLock) REQUIRES(mLock) override; } mContext; friend class ContextImpl; @@ -198,6 +201,9 @@ private: // true if tap-to-click on touchpad currently disabled bool mPreventingTouchpadTaps GUARDED_BY(mLock){false}; + // records timestamp of the last key press on the physical keyboard + nsecs_t mLastKeyDownTimestamp GUARDED_BY(mLock){0}; + // low-level input event decoding and device management [[nodiscard]] std::list<NotifyArgs> processEventsLocked(const RawEvent* rawEvents, size_t count) REQUIRES(mLock); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index aed75636f7..69b2315a6c 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -65,6 +65,9 @@ public: virtual void setPreventingTouchpadTaps(bool prevent) = 0; virtual bool isPreventingTouchpadTaps() = 0; + + virtual void setLastKeyDownTimestamp(nsecs_t when) = 0; + virtual nsecs_t getLastKeyDownTimestamp() = 0; }; } // namespace android diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 79f07a5f7c..7aeb215174 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -20,6 +20,7 @@ #include "CursorInputMapper.h" +#include <com_android_input_flags.h> #include <optional> #include "CursorButtonAccumulator.h" @@ -29,6 +30,8 @@ #include "input/PrintTools.h" +namespace input_flags = com::android::input::flags; + namespace android { // The default velocity control parameters that has no effect. @@ -71,7 +74,8 @@ void CursorMotionAccumulator::finishSync() { CursorInputMapper::CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig) : InputMapper(deviceContext, readerConfig), - mLastEventTime(std::numeric_limits<nsecs_t>::min()) {} + mLastEventTime(std::numeric_limits<nsecs_t>::min()), + mEnablePointerChoreographer(input_flags::enable_pointer_choreographer()) {} CursorInputMapper::~CursorInputMapper() { if (mPointerController != nullptr) { @@ -87,11 +91,11 @@ void CursorInputMapper::populateDeviceInfo(InputDeviceInfo& info) { InputMapper::populateDeviceInfo(info); if (mParameters.mode == Parameters::Mode::POINTER) { - if (const auto bounds = mPointerController->getBounds(); bounds) { - info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, bounds->left, bounds->right, 0.0f, - 0.0f, 0.0f); - info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, bounds->top, bounds->bottom, 0.0f, - 0.0f, 0.0f); + if (!mBoundsInLogicalDisplay.isEmpty()) { + info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, mBoundsInLogicalDisplay.left, + mBoundsInLogicalDisplay.right, 0.0f, 0.0f, 0.0f); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, mSource, mBoundsInLogicalDisplay.top, + mBoundsInLogicalDisplay.bottom, 0.0f, 0.0f, 0.0f); } } else { info.addMotionRange(AMOTION_EVENT_AXIS_X, mSource, -1.0f, 1.0f, 0.0f, mXScale, 0.0f); @@ -283,19 +287,22 @@ std::list<NotifyArgs> CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { - if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); - - if (moved) { - mPointerController->move(deltaX, deltaY); + if (!mEnablePointerChoreographer) { + if (moved || scrolled || buttonsChanged) { + mPointerController->setPresentation( + PointerControllerInterface::Presentation::POINTER); + + if (moved) { + mPointerController->move(deltaX, deltaY); + } + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } - mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); - } - std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition(); + std::tie(xCursorPosition, yCursorPosition) = mPointerController->getPosition(); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + } pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); } else { @@ -499,31 +506,55 @@ void CursorInputMapper::configureOnChangeDisplayInfo(const InputReaderConfigurat const bool isPointer = mParameters.mode == Parameters::Mode::POINTER; mDisplayId = ADISPLAY_ID_NONE; - if (auto viewport = mDeviceContext.getAssociatedViewport(); viewport) { + std::optional<DisplayViewport> resolvedViewport; + bool isBoundsSet = false; + if (auto assocViewport = mDeviceContext.getAssociatedViewport(); assocViewport) { // 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); + mDisplayId = assocViewport->displayId; + resolvedViewport = *assocViewport; + if (!mEnablePointerChoreographer) { + const bool mismatchedPointerDisplay = + isPointer && (assocViewport->displayId != mPointerController->getDisplayId()); + if (mismatchedPointerDisplay) { + // This device's associated display doesn't match PointerController's current + // display. Do not associate it with any display. + mDisplayId.reset(); + } + } } else if (isPointer) { // The InputDevice is not associated with a viewport, but it controls the mouse pointer. - mDisplayId = mPointerController->getDisplayId(); + if (mEnablePointerChoreographer) { + // Always use DISPLAY_ID_NONE for mouse events. + // PointerChoreographer will make it target the correct the displayId later. + const auto pointerViewport = + getContext()->getPolicy()->getPointerViewportForAssociatedDisplay(); + mDisplayId = pointerViewport ? std::make_optional(ADISPLAY_ID_NONE) : std::nullopt; + resolvedViewport = pointerViewport; + } else { + mDisplayId = mPointerController->getDisplayId(); + if (auto v = config.getDisplayViewportById(*mDisplayId); v) { + resolvedViewport = *v; + } + if (auto bounds = mPointerController->getBounds(); bounds) { + mBoundsInLogicalDisplay = *bounds; + isBoundsSet = true; + } + } } - mOrientation = ui::ROTATION_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. 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); - } + mOrientation = (mParameters.orientationAware && mParameters.hasAssociatedDisplay) || + mParameters.mode == Parameters::Mode::POINTER_RELATIVE || !resolvedViewport + ? ui::ROTATION_0 + : getInverseRotation(resolvedViewport->orientation); + + if (!isBoundsSet) { + mBoundsInLogicalDisplay = resolvedViewport + ? FloatRect{static_cast<float>(resolvedViewport->logicalLeft), + static_cast<float>(resolvedViewport->logicalTop), + static_cast<float>(resolvedViewport->logicalRight - 1), + static_cast<float>(resolvedViewport->logicalBottom - 1)} + : FloatRect{0, 0, 0, 0}; } bumpGeneration(); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index b879bfdd08..308adaa463 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -119,7 +119,8 @@ private: // 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; - ui::Rotation mOrientation; + ui::Rotation mOrientation{ui::ROTATION_0}; + FloatRect mBoundsInLogicalDisplay{}; std::shared_ptr<PointerControllerInterface> mPointerController; @@ -127,6 +128,8 @@ private: nsecs_t mDownTime; nsecs_t mLastEventTime; + const bool mEnablePointerChoreographer; + explicit CursorInputMapper(InputDeviceContext& deviceContext, const InputReaderConfiguration& readerConfig); void dumpParameters(std::string& dump); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp index 531fc6766d..f068cc8aa4 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp @@ -270,7 +270,7 @@ std::list<NotifyArgs> KeyboardInputMapper::processKey(nsecs_t when, nsecs_t read keyDown.flags = flags; mKeyDowns.push_back(keyDown); } - onKeyDownProcessed(); + onKeyDownProcessed(downTime); } else { // Remove key down. if (keyDownIndex) { @@ -448,8 +448,9 @@ std::list<NotifyArgs> KeyboardInputMapper::cancelAllDownKeys(nsecs_t when) { return out; } -void KeyboardInputMapper::onKeyDownProcessed() { +void KeyboardInputMapper::onKeyDownProcessed(nsecs_t downTime) { InputReaderContext& context = *getContext(); + context.setLastKeyDownTimestamp(downTime); if (context.isPreventingTouchpadTaps()) { // avoid pinging java service unnecessarily, just fade pointer again if it became visible context.fadePointer(); diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h index 09808df18c..500256b21f 100644 --- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h +++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h @@ -107,7 +107,7 @@ private: void updateLedStateForModifier(LedState& ledState, int32_t led, int32_t modifier, bool reset); std::optional<DisplayViewport> findViewport(const InputReaderConfiguration& readerConfig); [[nodiscard]] std::list<NotifyArgs> cancelAllDownKeys(nsecs_t when); - void onKeyDownProcessed(); + void onKeyDownProcessed(nsecs_t downTime); }; } // namespace android diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp index f70be72741..b0fc9035f2 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp @@ -152,6 +152,14 @@ void MultiTouchMotionAccumulator::warnIfNotInUse(const RawEvent& event, const Sl } } +size_t MultiTouchMotionAccumulator::getActiveSlotsCount() const { + if (!mUsingSlotsProtocol) { + return mCurrentSlot < 0 ? 0 : mCurrentSlot; + } + return std::count_if(mSlots.begin(), mSlots.end(), + [](const Slot& slot) { return slot.mInUse; }); +} + // --- MultiTouchMotionAccumulator::Slot --- ToolType MultiTouchMotionAccumulator::Slot::getToolType() const { diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h index 943dde5ca2..0e3e2bb365 100644 --- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h +++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h @@ -77,6 +77,7 @@ public: void process(const RawEvent* rawEvent); void finishSync(); + size_t getActiveSlotsCount() const; inline size_t getSlotCount() const { return mSlots.size(); } inline const Slot& getSlot(size_t index) const { LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index); diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index 64e8825f34..db31dede2f 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -58,6 +58,7 @@ cc_test { "InputReader_test.cpp", "InstrumentedInputReader.cpp", "LatencyTracker_test.cpp", + "MultiTouchMotionAccumulator_test.cpp", "NotifyArgs_test.cpp", "PointerChoreographer_test.cpp", "PreferStylusOverTouch_test.cpp", diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.cpp b/services/inputflinger/tests/FakeInputReaderPolicy.cpp index 41c98ef9e9..88f514f1d4 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.cpp +++ b/services/inputflinger/tests/FakeInputReaderPolicy.cpp @@ -261,4 +261,17 @@ void FakeInputReaderPolicy::notifyStylusGestureStarted(int32_t deviceId, nsecs_t mStylusGestureNotified = deviceId; } +std::optional<DisplayViewport> FakeInputReaderPolicy::getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) { + if (associatedDisplayId == ADISPLAY_ID_NONE) { + associatedDisplayId = mConfig.defaultPointerDisplayId; + } + for (auto& viewport : mViewports) { + if (viewport.displayId == associatedDisplayId) { + return std::make_optional(viewport); + } + } + return std::nullopt; +} + } // namespace android diff --git a/services/inputflinger/tests/FakeInputReaderPolicy.h b/services/inputflinger/tests/FakeInputReaderPolicy.h index 48912a6a28..4ef9c3ee4a 100644 --- a/services/inputflinger/tests/FakeInputReaderPolicy.h +++ b/services/inputflinger/tests/FakeInputReaderPolicy.h @@ -79,6 +79,8 @@ public: void setStylusPointerIconEnabled(bool enabled); void setIsInputMethodConnectionActive(bool active); bool isInputMethodConnectionActive() override; + std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) override; private: void getReaderConfiguration(InputReaderConfiguration* outConfig) override; diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h index c374267ff9..c75f6ed7e5 100644 --- a/services/inputflinger/tests/FakePointerController.h +++ b/services/inputflinger/tests/FakePointerController.h @@ -40,6 +40,7 @@ public: bool isPointerShown(); private: + std::string dump() override { return ""; } std::optional<FloatRect> getBounds() const override; void move(float deltaX, float deltaY) override; void fade(Transition) override; @@ -52,7 +53,7 @@ private: bool mHaveBounds{false}; float mMinX{0}, mMinY{0}, mMaxX{0}, mMaxY{0}; float mX{0}, mY{0}; - int32_t mDisplayId{ADISPLAY_ID_DEFAULT}; + int32_t mDisplayId{ADISPLAY_ID_NONE}; bool mIsPointerShown{false}; std::map<int32_t, std::vector<int32_t>> mSpotsByDisplay; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 3c87f7194d..3c2b31d770 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -112,8 +112,6 @@ static constexpr gui::Uid SECONDARY_WINDOW_UID{1012}; // An arbitrary pid of the gesture monitor window static constexpr gui::Pid MONITOR_PID{2001}; -static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms; - /** * If we expect to receive the event, the timeout can be made very long. When the test are running * correctly, we will actually never wait until the end of the timeout because the wait will end @@ -337,7 +335,7 @@ public: std::optional<sp<IBinder>> receivedToken = getItemFromStorageLockedInterruptible(100ms, mBrokenInputChannels, lock, mNotifyInputChannelBroken); - ASSERT_TRUE(receivedToken.has_value()); + ASSERT_TRUE(receivedToken.has_value()) << "Did not receive the broken channel token"; ASSERT_EQ(token, *receivedToken); } @@ -348,6 +346,8 @@ public: mInterceptKeyTimeout = timeout; } + void setStaleEventTimeout(std::chrono::nanoseconds timeout) { mStaleEventTimeout = timeout; } + void assertUserActivityPoked() { std::scoped_lock lock(mLock); ASSERT_TRUE(mPokedUserActivity) << "Expected user activity to have been poked"; @@ -366,6 +366,30 @@ public: ASSERT_FALSE(mNotifiedInteractions.popWithTimeout(10ms)); } + void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler) { + std::scoped_lock lock(mLock); + mUnhandledKeyHandler = handler; + } + + void assertUnhandledKeyReported(int32_t keycode) { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<int32_t> unhandledKeycode = + getItemFromStorageLockedInterruptible(100ms, mReportedUnhandledKeycodes, lock, + mNotifyUnhandledKey); + ASSERT_TRUE(unhandledKeycode) << "Expected unhandled key to be reported"; + ASSERT_EQ(unhandledKeycode, keycode); + } + + void assertUnhandledKeyNotReported() { + std::unique_lock lock(mLock); + base::ScopedLockAssertion assumeLocked(mLock); + std::optional<int32_t> unhandledKeycode = + getItemFromStorageLockedInterruptible(10ms, mReportedUnhandledKeycodes, lock, + mNotifyUnhandledKey); + ASSERT_FALSE(unhandledKeycode) << "Expected unhandled key NOT to be reported"; + } + private: std::mutex mLock; std::unique_ptr<InputEvent> mFilteredEvent GUARDED_BY(mLock); @@ -391,8 +415,14 @@ private: std::chrono::milliseconds mInterceptKeyTimeout = 0ms; + std::chrono::nanoseconds mStaleEventTimeout = 1000ms; + BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions; + std::condition_variable mNotifyUnhandledKey; + std::queue<int32_t> mReportedUnhandledKeycodes GUARDED_BY(mLock); + std::function<std::optional<KeyEvent>(const KeyEvent&)> mUnhandledKeyHandler GUARDED_BY(mLock); + // All three ANR-related callbacks behave the same way, so we use this generic function to wait // for a specific container to become non-empty. When the container is non-empty, return the // first entry from the container and erase it. @@ -435,7 +465,6 @@ private: condition.wait_for(lock, timeout, [&storage]() REQUIRES(mLock) { return !storage.empty(); }); if (storage.empty()) { - ADD_FAILURE() << "Did not receive the expected callback"; return std::nullopt; } T item = storage.front(); @@ -526,9 +555,12 @@ private: return delay; } - std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&, + std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent& event, uint32_t) override { - return {}; + std::scoped_lock lock(mLock); + mReportedUnhandledKeycodes.emplace(event.getKeyCode()); + mNotifyUnhandledKey.notify_all(); + return mUnhandledKeyHandler != nullptr ? mUnhandledKeyHandler(event) : std::nullopt; } void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask, @@ -545,6 +577,10 @@ private: mPokedUserActivity = true; } + bool isStaleEvent(nsecs_t currentTime, nsecs_t eventTime) override { + return std::chrono::nanoseconds(currentTime - eventTime) >= mStaleEventTimeout; + } + void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override { std::scoped_lock lock(mLock); mOnPointerDownToken = newToken; @@ -586,7 +622,8 @@ protected: void SetUp() override { mFakePolicy = std::make_unique<FakeInputDispatcherPolicy>(); - mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy, STALE_EVENT_TIMEOUT); + mDispatcher = std::make_unique<InputDispatcher>(*mFakePolicy); + mDispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread ASSERT_EQ(OK, mDispatcher->start()); @@ -838,13 +875,13 @@ public: mConsumer = std::make_unique<InputConsumer>(std::move(clientChannel)); } - InputEvent* consume(std::chrono::milliseconds timeout) { + InputEvent* consume(std::chrono::milliseconds timeout, bool handled = false) { InputEvent* event; std::optional<uint32_t> consumeSeq = receiveEvent(timeout, &event); if (!consumeSeq) { return nullptr; } - finishEvent(*consumeSeq); + finishEvent(*consumeSeq, handled); return event; } @@ -890,8 +927,8 @@ public: /** * To be used together with "receiveEvent" to complete the consumption of an event. */ - void finishEvent(uint32_t consumeSeq) { - const status_t status = mConsumer->sendFinishedSignal(consumeSeq, true); + void finishEvent(uint32_t consumeSeq, bool handled = true) { + const status_t status = mConsumer->sendFinishedSignal(consumeSeq, handled); ASSERT_EQ(OK, status) << mName.c_str() << ": consumer sendFinishedSignal should return OK."; } @@ -1202,8 +1239,8 @@ public: void setWindowOffset(float offsetX, float offsetY) { mInfo.transform.set(offsetX, offsetY); } - KeyEvent* consumeKey() { - InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED); + KeyEvent* consumeKey(bool handled = true) { + InputEvent* event = consume(CONSUME_TIMEOUT_EVENT_EXPECTED, handled); if (event == nullptr) { ADD_FAILURE() << "Consume failed : no event"; return nullptr; @@ -1342,11 +1379,11 @@ public: mInputReceiver->sendTimeline(inputEventId, timeline); } - InputEvent* consume(std::chrono::milliseconds timeout) { + InputEvent* consume(std::chrono::milliseconds timeout, bool handled = true) { if (mInputReceiver == nullptr) { return nullptr; } - return mInputReceiver->consume(timeout); + return mInputReceiver->consume(timeout, handled); } MotionEvent* consumeMotion() { @@ -2160,6 +2197,69 @@ TEST_F(InputDispatcherTest, TwoPointerCancelInconsistentPolicy) { } /** + * Same as the above 'TwoPointerCancelInconsistentPolicy' test, but for hovers. + * The policy typically sets POLICY_FLAG_PASS_TO_USER to the events. But when the display is not + * interactive, it might stop sending this flag. + * We've already ensured the consistency of the touch event in this case, and we should also ensure + * the consistency of the hover event in this case. + * + * Test procedure: + * HOVER_ENTER -> HOVER_MOVE -> (stop sending POLICY_FLAG_PASS_TO_USER) -> HOVER_EXIT + * HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT + * + * We expect to receive two full streams of hover events. + */ +TEST_F(InputDispatcherTest, HoverEventInconsistentPolicy) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + sp<FakeWindowHandle> window = + sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + window->setFrame(Rect(0, 0, 300, 300)); + + mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(100).y(101)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE)); + + // Send hover exit without the default policy flags. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .policyFlags(0) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(101).y(102)) + .build()); + + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); + + // Send a simple hover event stream, ensure dispatcher not crashed and window can receive + // right event. + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_ENTER, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(200).y(201)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_ENTER)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_MOVE, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_MOVE)); + + mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .policyFlags(DEFAULT_POLICY_FLAGS) + .pointer(PointerBuilder(0, ToolType::STYLUS).x(201).y(202)) + .build()); + window->consumeMotionEvent(WithMotionAction(ACTION_HOVER_EXIT)); +} + +/** * Two windows: a window on the left and a window on the right. * Mouse is hovered from the right window into the left window. * Next, we tap on the left window, where the cursor was last seen. @@ -6464,6 +6564,213 @@ TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithKeys) { ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertNotifyDeviceInteractionWasNotCalled()); } +class InputDispatcherFallbackKeyTest : public InputDispatcherTest { +protected: + std::shared_ptr<FakeApplicationHandle> mApp; + sp<FakeWindowHandle> mWindow; + + virtual void SetUp() override { + InputDispatcherTest::SetUp(); + + mApp = std::make_shared<FakeApplicationHandle>(); + + mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "Window", ADISPLAY_ID_DEFAULT); + mWindow->setFrame(Rect(0, 0, 100, 100)); + + mDispatcher->onWindowInfosChanged({{*mWindow->getInfo()}, {}, 0, 0}); + setFocusedWindow(mWindow); + ASSERT_NO_FATAL_FAILURE(mWindow->consumeFocusEvent(/*hasFocus=*/true)); + } + + void setFallback(int32_t keycode) { + mFakePolicy->setUnhandledKeyHandler([keycode](const KeyEvent& event) { + return KeyEventBuilder(event).keyCode(keycode).build(); + }); + } + + void consumeKey(bool handled, const ::testing::Matcher<KeyEvent>& matcher) { + KeyEvent* event = mWindow->consumeKey(handled); + ASSERT_NE(event, nullptr) << "Did not receive key event"; + ASSERT_THAT(*event, matcher); + } +}; + +TEST_F(InputDispatcherFallbackKeyTest, PolicyNotNotifiedForHandledKey) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/true, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); +} + +TEST_F(InputDispatcherFallbackKeyTest, PolicyNotifiedForUnhandledKey) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); +} + +TEST_F(InputDispatcherFallbackKeyTest, NoFallbackRequestedByPolicy) { + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + + // Since the policy did not request any fallback to be generated, ensure there are no events. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); +} + +TEST_F(InputDispatcherFallbackKeyTest, FallbackDispatchForUnhandledKey) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + + // Since the key was not handled, ensure the fallback event was dispatched instead. + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, AppHandlesPreviouslyUnhandledKey) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, but handle the fallback. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + // But this time, the app handles the original key. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key is canceled. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, AppDoesNotHandleFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // App does not handle the fallback either, so ensure another fallback is not generated. + setFallback(AKEYCODE_C); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, and ensure the fallback key is also released. + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, InconsistentPolicyCancelsFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, so fallback is generated. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // Release the original key, but assume the policy is misbehaving and it + // generates an inconsistent fallback to the one from the DOWN event. + setFallback(AKEYCODE_C); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key reported before as DOWN is canceled due to the inconsistency. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + +TEST_F(InputDispatcherFallbackKeyTest, CanceledKeyCancelsFallback) { + setFallback(AKEYCODE_B); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); + + // Do not handle this key event, so fallback is generated. + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK))); + + // The original key is canceled. + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_A) + .addFlag(AKEY_EVENT_FLAG_CANCELED) + .build()); + consumeKey(/*handled=*/false, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A)); + // Ensure the fallback key is also canceled due to the original key being canceled. + consumeKey(/*handled=*/true, + AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_B), + WithFlags(AKEY_EVENT_FLAG_FALLBACK | AKEY_EVENT_FLAG_CANCELED))); + + ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyNotReported()); + mWindow->assertNoEvents(); +} + class InputDispatcherKeyRepeatTest : public InputDispatcherTest { protected: static constexpr nsecs_t KEY_REPEAT_TIMEOUT = 40 * 1000000; // 40 ms @@ -7529,6 +7836,8 @@ TEST_F(InputDispatcherSingleWindowAnr, StaleKeyEventDoesNotAnr) { mWindow->consumeFocusEvent(false); KeyEvent event; + static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms; + mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT); const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC) - std::chrono::nanoseconds(STALE_EVENT_TIMEOUT).count(); diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index dac4ea02c4..787444c9e6 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -80,9 +80,15 @@ void InputMapperUnitTest::setKeyCodeState(KeyState state, std::set<int> keyCodes } std::list<NotifyArgs> InputMapperUnitTest::process(int32_t type, int32_t code, int32_t value) { + nsecs_t when = systemTime(SYSTEM_TIME_MONOTONIC); + return process(when, type, code, value); +} + +std::list<NotifyArgs> InputMapperUnitTest::process(nsecs_t when, int32_t type, int32_t code, + int32_t value) { RawEvent event; - event.when = systemTime(SYSTEM_TIME_MONOTONIC); - event.readTime = event.when; + event.when = when; + event.readTime = when; event.deviceId = mMapper->getDeviceContext().getEventHubId(); event.type = type; event.code = code; diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h index c2ac258765..3f9061fc6a 100644 --- a/services/inputflinger/tests/InputMapperTest.h +++ b/services/inputflinger/tests/InputMapperTest.h @@ -52,6 +52,7 @@ protected: void setKeyCodeState(KeyState state, std::set<int> keyCodes); std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value); + std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value); MockEventHubInterface mMockEventHub; std::shared_ptr<FakePointerController> mFakePointerController; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 64ae9e8b14..5044b3e777 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -37,6 +37,7 @@ #include <UinputDevice.h> #include <VibratorInputMapper.h> #include <android-base/thread_annotations.h> +#include <com_android_input_flags.h> #include <ftl/enum.h> #include <gtest/gtest.h> #include <gui/constants.h> @@ -99,6 +100,8 @@ static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4); // Maximum smoothing time delta so that we don't generate events too far into the future. constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32); +namespace input_flags = com::android::input::flags; + template<typename T> static inline T min(T a, T b) { return a < b ? a : b; @@ -4097,9 +4100,9 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -// --- CursorInputMapperTest --- +// --- CursorInputMapperTestBase --- -class CursorInputMapperTest : public InputMapperTest { +class CursorInputMapperTestBase : public InputMapperTest { protected: static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; @@ -4133,11 +4136,11 @@ protected: } }; -const int32_t CursorInputMapperTest::TRACKBALL_MOVEMENT_THRESHOLD = 6; +const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6; -void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, - int32_t originalY, int32_t rotatedX, - int32_t rotatedY) { +void CursorInputMapperTestBase::testMotionRotation(CursorInputMapper& mapper, int32_t originalX, + int32_t originalY, int32_t rotatedX, + int32_t rotatedY) { NotifyMotionArgs args; process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, originalX); @@ -4151,6 +4154,16 @@ void CursorInputMapperTest::testMotionRotation(CursorInputMapper& mapper, int32_ float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f)); } +// --- CursorInputMapperTest --- + +class CursorInputMapperTest : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(false); + CursorInputMapperTestBase::SetUp(); + } +}; + TEST_F(CursorInputMapperTest, WhenModeIsPointer_GetSources_ReturnsMouse) { addConfigurationProperty("cursor.mode", "pointer"); CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); @@ -4180,6 +4193,7 @@ TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeF // When the bounds are set, then there should be a valid motion range. mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); InputDeviceInfo info2; mapper.populateDeviceInfo(info2); @@ -4906,11 +4920,459 @@ TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPoint ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); } +// --- CursorInputMapperTestWithChoreographer --- + +class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + CursorInputMapperTestBase::SetUp(); + } +}; + +TEST_F(CursorInputMapperTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + InputDeviceInfo info; + mapper.populateDeviceInfo(info); + + // Initially there may not be a valid motion range. + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE)); + ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); + + // When the viewport and the default pointer display ID is set, then there should be a valid + // motion range. + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, + /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + InputDeviceInfo info2; + mapper.populateDeviceInfo(info2); + + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_WIDTH - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0, + DISPLAY_HEIGHT - 1, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE, + AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + NotifyMotionArgs motionArgs; + NotifyKeyArgs keyArgs; + + // press BTN_LEFT, release BTN_LEFT + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, + motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY, + motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + // press BTN_BACK, release BTN_BACK + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + // press BTN_SIDE, release BTN_SIDE + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode); + + // press BTN_FORWARD, release BTN_FORWARD + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + // press BTN_EXTRA, release BTN_EXTRA + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action); + ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action); + ASSERT_EQ(0, motionArgs.buttonState); + ASSERT_NO_FATAL_FAILURE( + assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f)); + + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs)); + ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action); + ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1); + mFakePointerController->setPosition(100, 200); + + NotifyMotionArgs args; + + 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], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); +} + +TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) { + addConfigurationProperty("cursor.mode", "pointer"); + const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f, + /*highThreshold=*/100.f, /*acceleration=*/10.f); + mFakePolicy->setVelocityControlParams(testParams); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + prepareDisplay(ui::ROTATION_0); + + NotifyDeviceResetArgs resetArgs; + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); + ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); + ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); + + NotifyMotionArgs args; + + // Move and verify scale is 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, args.source); + ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); + const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_GT(relX, 10); + ASSERT_GT(relY, 20); + + // 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 scale 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); + const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X); + const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y); + ASSERT_EQ(10, relX2); + ASSERT_EQ(20, relY2); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_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); + + // Ensure input events are generated without display ID and coords, + // because they will be decided later by PointerChoreographer. + 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(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE), + WithCoords(0.0f, 0.0f)))); +} + +TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_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); + + // Ensure input events are generated with associated display ID but not with coords, + // because the coords will be decided later by PointerChoreographer. + 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(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(0.0f, 0.0f)))); +} + +TEST_F(CursorInputMapperTestWithChoreographer, + ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) { + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display as the display on which the pointer should be shown. + prepareDisplay(ui::ROTATION_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); + + // With PointerChoreographer enabled, there could be a PointerController for the associated + // display even if it is different from the pointer display. So the mapper should generate an + // event. + 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(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), + WithCoords(0.0f, 0.0f)))); +} + // --- BluetoothCursorInputMapperTest --- -class BluetoothCursorInputMapperTest : public CursorInputMapperTest { +class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase { protected: void SetUp() override { + input_flags::enable_pointer_choreographer(false); InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); mFakePointerController = std::make_shared<FakePointerController>(); @@ -5006,6 +5468,122 @@ TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) { WithEventTime(expectedEventTime)))); } +// --- BluetoothCursorInputMapperTestWithChoreographer --- + +class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { +protected: + void SetUp() override { + input_flags::enable_pointer_choreographer(true); + InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH); + + mFakePointerController = std::make_shared<FakePointerController>(); + mFakePolicy->setPointerController(mFakePointerController); + } +}; + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events that come in quick succession, according to their timestamps. + for (int i = 0; i < 3; i++) { + constexpr static nsecs_t delta = ms2ns(1); + static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA); + kernelEventTime += delta; + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // Process several events with the same timestamp from the kernel. + // Ensure that we do not generate events too far into the future. + constexpr static int32_t numEvents = + MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA; + for (int i = 0; i < numEvents; i++) { + expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA; + + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + } + + // By processing more events with the same timestamp, we should not generate events with a + // timestamp that is more than the specified max time delta from the timestamp at its injection. + const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA; + for (int i = 0; i < 3; i++) { + process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1); + process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(cappedEventTime)))); + } +} + +TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) { + addConfigurationProperty("cursor.mode", "pointer"); + CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); + + // Set up the default display. + prepareDisplay(ui::ROTATION_0); + mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); + configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO); + + nsecs_t kernelEventTime = ARBITRARY_TIME; + nsecs_t expectedEventTime = ARBITRARY_TIME; + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); + + // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp + // smoothening is not needed, its timestamp is not affected. + kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1); + expectedEventTime = kernelEventTime; + + process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1); + process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithEventTime(expectedEventTime)))); +} + // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/InstrumentedInputReader.h b/services/inputflinger/tests/InstrumentedInputReader.h index ca85558a0c..e9c7bb44e8 100644 --- a/services/inputflinger/tests/InstrumentedInputReader.h +++ b/services/inputflinger/tests/InstrumentedInputReader.h @@ -106,6 +106,9 @@ protected: void setPreventingTouchpadTaps(bool prevent) override { mPreventingTouchpadTaps = prevent; } bool isPreventingTouchpadTaps() override { return mPreventingTouchpadTaps; } + void setLastKeyDownTimestamp(nsecs_t when) override { mLastKeyDownTimestamp = when; }; + nsecs_t getLastKeyDownTimestamp() override { return mLastKeyDownTimestamp; }; + private: int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; @@ -113,6 +116,7 @@ protected: std::optional<nsecs_t> mRequestedTimeout; std::vector<InputDeviceInfo> mExternalStylusDevices; bool mPreventingTouchpadTaps{false}; + nsecs_t mLastKeyDownTimestamp; } mFakeContext; friend class InputReaderTest; diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h index 05823cd9de..73949136f4 100644 --- a/services/inputflinger/tests/InterfaceMocks.h +++ b/services/inputflinger/tests/InterfaceMocks.h @@ -77,6 +77,9 @@ public: MOCK_METHOD(void, setPreventingTouchpadTaps, (bool prevent), (override)); MOCK_METHOD(bool, isPreventingTouchpadTaps, (), (override)); + MOCK_METHOD(void, setLastKeyDownTimestamp, (nsecs_t when)); + MOCK_METHOD(nsecs_t, getLastKeyDownTimestamp, ()); + private: int32_t mGeneration = 0; }; diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp index 48f5673c51..2ef79999a2 100644 --- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp +++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp @@ -26,6 +26,7 @@ namespace android { using testing::_; +using testing::Args; using testing::DoAll; using testing::Return; using testing::SetArgPointee; @@ -158,4 +159,18 @@ TEST_F(KeyboardInputMapperUnitTest, MetaKeystrokesWithIMeConnectionDontDisableTo testTouchpadTapStateForKeys(metaKeys, /* expectPrevent= */ false); } +TEST_F(KeyboardInputMapperUnitTest, KeyPressTimestampRecorded) { + nsecs_t when = ARBITRARY_TIME; + std::vector<int32_t> keyCodes{KEY_0, KEY_A, KEY_LEFTCTRL, KEY_RIGHTALT, KEY_LEFTSHIFT}; + EXPECT_CALL(mMockInputReaderContext, setLastKeyDownTimestamp) + .With(Args<0>(when)) + .Times(keyCodes.size()); + for (int32_t keyCode : keyCodes) { + process(when, EV_KEY, keyCode, 1); + process(when, EV_SYN, SYN_REPORT, 0); + process(when, EV_KEY, keyCode, 0); + process(when, EV_SYN, SYN_REPORT, 0); + } +} + } // namespace android diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp new file mode 100644 index 0000000000..9fa6cdd298 --- /dev/null +++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp @@ -0,0 +1,87 @@ +/* + * Copyright 2023 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 "MultiTouchMotionAccumulator.h" +#include "InputMapperTest.h" + +namespace android { + +class MultiTouchMotionAccumulatorTest : public InputMapperUnitTest { +protected: + static constexpr size_t SLOT_COUNT = 8; + + MultiTouchMotionAccumulator mMotionAccumulator; + + void processMotionEvent(int32_t type, int32_t code, int32_t value) { + RawEvent event; + event.when = ARBITRARY_TIME; + event.readTime = READ_TIME; + event.deviceId = EVENTHUB_ID; + event.type = type; + event.code = code; + event.value = value; + mMotionAccumulator.process(&event); + } +}; + +TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountUsingSlotsProtocol) { + mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/true); + // We expect active slot count to match the touches being tracked + // first touch + processMotionEvent(EV_ABS, ABS_MT_SLOT, 0); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 123); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // second touch + processMotionEvent(EV_ABS, ABS_MT_SLOT, 1); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, 456); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount()); + + // second lifted + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // first lifted + processMotionEvent(EV_ABS, ABS_MT_SLOT, 0); + processMotionEvent(EV_ABS, ABS_MT_TRACKING_ID, -1); + processMotionEvent(EV_SYN, SYN_REPORT, 0); + ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount()); +} + +TEST_F(MultiTouchMotionAccumulatorTest, ActiveSlotCountNotUsingSlotsProtocol) { + mMotionAccumulator.configure(*mDeviceContext, SLOT_COUNT, /*usingSlotsProtocol=*/false); + + // first touch + processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 0); + processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 0); + processMotionEvent(EV_SYN, SYN_MT_REPORT, 0); + ASSERT_EQ(1u, mMotionAccumulator.getActiveSlotsCount()); + + // second touch + processMotionEvent(EV_ABS, ABS_MT_POSITION_X, 50); + processMotionEvent(EV_ABS, ABS_MT_POSITION_Y, 50); + processMotionEvent(EV_SYN, SYN_MT_REPORT, 0); + ASSERT_EQ(2u, mMotionAccumulator.getActiveSlotsCount()); + + // reset + mMotionAccumulator.finishSync(); + ASSERT_EQ(0u, mMotionAccumulator.getActiveSlotsCount()); +} + +} // namespace android diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 7237424e2a..da2e205ca5 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -19,16 +19,54 @@ #include <gtest/gtest.h> #include <vector> +#include "FakePointerController.h" +#include "NotifyArgsBuilders.h" +#include "TestEventMatchers.h" #include "TestInputListener.h" namespace android { +using ControllerType = PointerControllerInterface::ControllerType; + +namespace { + // Helpers to std::visit with lambdas. template <typename... V> struct Visitor : V... {}; template <typename... V> Visitor(V...) -> Visitor<V...>; +constexpr int32_t DEVICE_ID = 3; +constexpr int32_t DISPLAY_ID = 5; +constexpr int32_t ANOTHER_DISPLAY_ID = 10; + +const auto MOUSE_POINTER = PointerBuilder(/*id=*/0, ToolType::MOUSE) + .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 10) + .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 20); + +static InputDeviceInfo generateTestDeviceInfo(int32_t deviceId, uint32_t source, + int32_t associatedDisplayId) { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(deviceId, /*generation=*/1, /*controllerNumber=*/1, identifier, "alias", + /*isExternal=*/false, /*hasMic=*/false, associatedDisplayId); + info.addSource(source); + return info; +} + +static std::vector<DisplayViewport> createViewports(std::vector<int32_t> displayIds) { + std::vector<DisplayViewport> viewports; + for (auto displayId : displayIds) { + DisplayViewport viewport; + viewport.displayId = displayId; + viewports.push_back(viewport); + } + return viewports; +} + +} // namespace + // --- PointerChoreographerTest --- class PointerChoreographerTest : public testing::Test, public PointerChoreographerPolicyInterface { @@ -36,7 +74,54 @@ protected: TestInputListener mTestListener; PointerChoreographer mChoreographer{mTestListener, *this}; - std::shared_ptr<PointerControllerInterface> createPointerController() { return {}; } + std::shared_ptr<FakePointerController> assertPointerControllerCreated( + ControllerType expectedType) { + EXPECT_TRUE(mLastCreatedController) << "No PointerController was created"; + auto [type, controller] = std::move(*mLastCreatedController); + EXPECT_EQ(expectedType, type); + mLastCreatedController.reset(); + return controller; + } + + void assertPointerControllerNotCreated() { ASSERT_EQ(std::nullopt, mLastCreatedController); } + + void assertPointerControllerRemoved(const std::shared_ptr<FakePointerController>& pc) { + // Ensure that the code under test is not holding onto this PointerController. + // While the policy initially creates the PointerControllers, the PointerChoreographer is + // expected to manage their lifecycles. Although we may not want to strictly enforce how + // the object is managed, in this case, we need to have a way of ensuring that the + // corresponding graphical resources have been released by the PointerController, and the + // simplest way of checking for that is to just make sure that the PointerControllers + // themselves are released by Choreographer when no longer in use. This check is ensuring + // that the reference retained by the test is the last one. + ASSERT_EQ(1, pc.use_count()) << "Expected PointerChoreographer to release all references " + "to this PointerController"; + } + + void assertPointerDisplayIdNotified(int32_t displayId) { + ASSERT_EQ(displayId, mPointerDisplayIdNotified); + mPointerDisplayIdNotified.reset(); + } + + void assertPointerDisplayIdNotNotified() { ASSERT_EQ(std::nullopt, mPointerDisplayIdNotified); } + +private: + std::optional<std::pair<ControllerType, std::shared_ptr<FakePointerController>>> + mLastCreatedController; + std::optional<int32_t> mPointerDisplayIdNotified; + + std::shared_ptr<PointerControllerInterface> createPointerController( + ControllerType type) override { + EXPECT_FALSE(mLastCreatedController.has_value()) + << "More than one PointerController created at a time"; + std::shared_ptr<FakePointerController> pc = std::make_shared<FakePointerController>(); + mLastCreatedController = {type, pc}; + return pc; + } + + void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override { + mPointerDisplayIdNotified = displayId; + } }; TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { @@ -86,4 +171,218 @@ TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { } } +TEST_F(PointerChoreographerTest, WhenMouseIsJustAddedDoesNotCreatePointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + assertPointerControllerNotCreated(); +} + +TEST_F(PointerChoreographerTest, WhenMouseEventOccursCreatesPointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); +} + +TEST_F(PointerChoreographerTest, WhenMouseIsRemovedRemovesPointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + + // Remove the mouse. + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, WhenKeyboardIsAddedDoesNotCreatePointerController) { + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, + {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_NONE)}}); + assertPointerControllerNotCreated(); +} + +TEST_F(PointerChoreographerTest, SetsViewportForAssociatedMouse) { + // Just adding a viewport or device should not create a PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + assertPointerControllerNotCreated(); + + // After the mouse emits event, PointerController will be created and viewport will be set. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, WhenViewportSetLaterSetsViewportForAssociatedMouse) { + // Without viewport information, PointerController will be created by a mouse event + // but viewport won't be set. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, DISPLAY_ID)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ADISPLAY_ID_NONE, pc->getDisplayId()); + + // After Choreographer gets viewport, PointerController should also have viewport. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, SetsDefaultMouseViewportForPointerController) { + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + + // For a mouse event without a target display, default viewport should be set for + // the PointerController. + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, pc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, + WhenDefaultMouseDisplayChangesSetsDefaultMouseViewportForPointerController) { + // Set one display as a default mouse display and emit mouse event to create PointerController. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(DISPLAY_ID, firstDisplayPc->getDisplayId()); + + // Change default mouse display. Existing PointerController should be removed. + mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + assertPointerControllerRemoved(firstDisplayPc); + assertPointerControllerNotCreated(); + + // New PointerController for the new default display will be created by the motion event. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto secondDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + ASSERT_EQ(ANOTHER_DISPLAY_ID, secondDisplayPc->getDisplayId()); +} + +TEST_F(PointerChoreographerTest, CallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenViewportIsSetLaterCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotNotified(); + + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + assertPointerDisplayIdNotified(DISPLAY_ID); +} + +TEST_F(PointerChoreographerTest, WhenMouseIsRemovedCallsNotifyPointerDisplayIdChanged) { + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID})); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto pc = assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(DISPLAY_ID); + + mChoreographer.notifyInputDevicesChanged({/*id=*/1, {}}); + assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); + assertPointerControllerRemoved(pc); +} + +TEST_F(PointerChoreographerTest, WhenDefaultMouseDisplayChangesCallsNotifyPointerDisplayIdChanged) { + // Add two viewports. + mChoreographer.setDisplayViewports(createViewports({DISPLAY_ID, ANOTHER_DISPLAY_ID})); + + // Set one viewport as a default mouse display ID. + mChoreographer.setDefaultMouseDisplayId(DISPLAY_ID); + mChoreographer.notifyInputDevicesChanged( + {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_NONE)}}); + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + auto firstDisplayPc = assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(DISPLAY_ID); + + // Set another viewport as a default mouse display ID. ADISPLAY_ID_NONE will be notified + // before a mouse event. + mChoreographer.setDefaultMouseDisplayId(ANOTHER_DISPLAY_ID); + assertPointerDisplayIdNotified(ADISPLAY_ID_NONE); + assertPointerControllerRemoved(firstDisplayPc); + + // After a mouse event, pointer display ID will be notified with new default mouse display. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE, AINPUT_SOURCE_MOUSE) + .pointer(MOUSE_POINTER) + .deviceId(DEVICE_ID) + .displayId(ADISPLAY_ID_NONE) + .build()); + assertPointerControllerCreated(ControllerType::MOUSE); + assertPointerDisplayIdNotified(ANOTHER_DISPLAY_ID); +} + } // namespace android diff --git a/services/inputflinger/tests/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h index ee6ff53d8c..a0a0f5a3de 100644 --- a/services/inputflinger/tests/TestEventMatchers.h +++ b/services/inputflinger/tests/TestEventMatchers.h @@ -464,9 +464,32 @@ inline WithPointersMatcher WithPointers( return WithPointersMatcher(pointers); } -MATCHER_P(WithKeyCode, keyCode, "KeyEvent with specified key code") { - *result_listener << "expected key code " << keyCode << ", but got " << arg.keyCode; - return arg.keyCode == keyCode; +/// Key code +class WithKeyCodeMatcher { +public: + using is_gtest_matcher = void; + explicit WithKeyCodeMatcher(int32_t keyCode) : mKeyCode(keyCode) {} + + bool MatchAndExplain(const NotifyKeyArgs& args, std::ostream*) const { + return mKeyCode == args.keyCode; + } + + bool MatchAndExplain(const KeyEvent& event, std::ostream*) const { + return mKeyCode == event.getKeyCode(); + } + + void DescribeTo(std::ostream* os) const { + *os << "with key code " << KeyEvent::getLabel(mKeyCode); + } + + void DescribeNegationTo(std::ostream* os) const { *os << "wrong key code"; } + +private: + const int32_t mKeyCode; +}; + +inline WithKeyCodeMatcher WithKeyCode(int32_t keyCode) { + return WithKeyCodeMatcher(keyCode); } MATCHER_P(WithRepeatCount, repeatCount, "KeyEvent with specified repeat count") { diff --git a/services/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h index bdedfdfa0c..2f844977ec 100644 --- a/services/inputflinger/tests/fuzzers/MapperHelpers.h +++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h @@ -275,6 +275,7 @@ public: void clearSpots() override {} int32_t getDisplayId() const override { return mFdp->ConsumeIntegral<int32_t>(); } void setDisplayViewport(const DisplayViewport& displayViewport) override {} + std::string dump() override { return ""; } }; class FuzzInputReaderPolicy : public InputReaderPolicyInterface { @@ -309,6 +310,10 @@ public: void setTouchAffineTransformation(const TouchAffineTransformation t) { mTransform = t; } void notifyStylusGestureStarted(int32_t, nsecs_t) {} bool isInputMethodConnectionActive() override { return mFdp->ConsumeBool(); } + std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay( + int32_t associatedDisplayId) override { + return {}; + } }; class FuzzInputListener : public virtual InputListenerInterface { @@ -360,6 +365,12 @@ public: void setPreventingTouchpadTaps(bool prevent) {} bool isPreventingTouchpadTaps() { return mFdp->ConsumeBool(); }; + + void setLastKeyDownTimestamp(nsecs_t when) { mLastKeyDownTimestamp = when; }; + nsecs_t getLastKeyDownTimestamp() { return mLastKeyDownTimestamp; }; + +private: + nsecs_t mLastKeyDownTimestamp; }; template <class Fdp> diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 6807c8e5d7..a44e4be1ce 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -148,7 +148,7 @@ public: getOverlaySupport, (), (const, override)); MOCK_METHOD(status_t, setRefreshRateChangedCallbackDebugEnabled, (PhysicalDisplayId, bool)); MOCK_METHOD(status_t, notifyExpectedPresentIfRequired, - (PhysicalDisplayId, nsecs_t, int32_t, int32_t)); + (PhysicalDisplayId, Period, TimePoint, Fps, std::optional<Period>)); }; } // namespace mock diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index fb6089dfbe..1d9f9ce73a 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -78,6 +78,59 @@ using aidl::android::hardware::graphics::composer3::Capability; using aidl::android::hardware::graphics::composer3::DisplayCapability; namespace hal = android::hardware::graphics::composer::hal; +namespace { +bool isFrameIntervalOnCadence(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + android::Fps lastFrameInterval, android::Period timeout, + android::Duration threshold) { + if (lastFrameInterval.getPeriodNsecs() == 0) { + return false; + } + + const auto expectedPresentTimeDeltaNs = + expectedPresentTime.ns() - lastExpectedPresentTimestamp.ns(); + + if (expectedPresentTimeDeltaNs > timeout.ns()) { + return false; + } + + const auto expectedPresentPeriods = static_cast<nsecs_t>( + std::round(static_cast<float>(expectedPresentTimeDeltaNs) / + static_cast<float>(lastFrameInterval.getPeriodNsecs()))); + const auto calculatedPeriodsOutNs = lastFrameInterval.getPeriodNsecs() * expectedPresentPeriods; + const auto calculatedExpectedPresentTimeNs = + lastExpectedPresentTimestamp.ns() + calculatedPeriodsOutNs; + const auto presentTimeDelta = + std::abs(expectedPresentTime.ns() - calculatedExpectedPresentTimeNs); + return presentTimeDelta < threshold.ns(); +} + +bool isExpectedPresentWithinTimeout(android::TimePoint expectedPresentTime, + android::TimePoint lastExpectedPresentTimestamp, + std::optional<android::Period> timeoutOpt, + android::Duration threshold) { + if (!timeoutOpt) { + // Always within timeout if timeoutOpt is absent and don't send hint + // for the timeout + return true; + } + + if (timeoutOpt->ns() == 0) { + // Always outside timeout if timeoutOpt is 0 and always send + // the hint for the timeout. + return false; + } + + if (expectedPresentTime.ns() < lastExpectedPresentTimestamp.ns() + timeoutOpt->ns()) { + return true; + } + + // Check if within the threshold as it can be just outside the timeout + return std::abs(expectedPresentTime.ns() - + (lastExpectedPresentTimestamp.ns() + timeoutOpt->ns())) < threshold.ns(); +} +} // namespace + namespace android { HWComposer::~HWComposer() = default; @@ -485,7 +538,12 @@ status_t HWComposer::getDeviceCompositionChanges( }(); displayData.validateWasSkipped = false; - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + { + std::scoped_lock lock{displayData.expectedPresentLock}; + displayData.lastExpectedPresentTimestamp = TimePoint::fromNs(expectedPresentTime); + // TODO(b/296636176) Update displayData.lastFrameInterval for present display commands + } + if (canSkipValidate) { sp<Fence> outPresentFence; uint32_t state = UINT32_MAX; @@ -879,21 +937,44 @@ status_t HWComposer::setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId } status_t HWComposer::notifyExpectedPresentIfRequired(PhysicalDisplayId displayId, - nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) { + Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) { RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); - auto& displayData = mDisplayData[displayId]; - if (expectedPresentTime >= displayData.lastExpectedPresentTimestamp && - expectedPresentTime < displayData.lastExpectedPresentTimestamp + timeoutNs) { - return NO_ERROR; - } + { + std::scoped_lock lock{displayData.expectedPresentLock}; + const auto lastExpectedPresentTimestamp = displayData.lastExpectedPresentTimestamp; + const auto lastFrameInterval = displayData.lastFrameInterval; + displayData.lastFrameInterval = frameInterval; + const auto threshold = Duration::fromNs(vsyncPeriod.ns() / 2); + + const constexpr nsecs_t kOneSecondNs = + std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count(); + const bool frameIntervalIsOnCadence = + isFrameIntervalOnCadence(expectedPresentTime, lastExpectedPresentTimestamp, + lastFrameInterval, + Period::fromNs(timeoutOpt && timeoutOpt->ns() > 0 + ? timeoutOpt->ns() + : kOneSecondNs), + threshold); + + const bool expectedPresentWithinTimeout = + isExpectedPresentWithinTimeout(expectedPresentTime, lastExpectedPresentTimestamp, + timeoutOpt, threshold); + + if (expectedPresentWithinTimeout && frameIntervalIsOnCadence) { + return NO_ERROR; + } - displayData.lastExpectedPresentTimestamp = expectedPresentTime; + displayData.lastExpectedPresentTimestamp = expectedPresentTime; + } ATRACE_FORMAT("%s ExpectedPresentTime %" PRId64 " frameIntervalNs %d", __func__, - expectedPresentTime, frameIntervalNs); + expectedPresentTime, frameInterval.getPeriodNsecs()); const auto error = mComposer->notifyExpectedPresent(displayData.hwcDisplay->getId(), - expectedPresentTime, frameIntervalNs); + expectedPresentTime.ns(), + frameInterval.getPeriodNsecs()); if (error != hal::Error::NONE) { ALOGE("Error in notifyExpectedPresent call %s", to_string(error).c_str()); diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 726a8eafbf..51e93197a8 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -301,9 +301,10 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) = 0; virtual status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) = 0; - virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, - int32_t timeoutNs) = 0; + virtual status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, + Fps frameInterval, + std::optional<Period> timeoutOpt) = 0; }; static inline bool operator==(const android::HWComposer::DeviceRequestedChanges& lhs, @@ -462,8 +463,9 @@ public: aidl::android::hardware::graphics::common::HdrConversionStrategy, aidl::android::hardware::graphics::common::Hdr*) override; status_t setRefreshRateChangedCallbackDebugEnabled(PhysicalDisplayId, bool enabled) override; - status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, nsecs_t expectedPresentTime, - int32_t frameIntervalNs, int32_t timeoutNs) override; + status_t notifyExpectedPresentIfRequired(PhysicalDisplayId, Period vsyncPeriod, + TimePoint expectedPresentTime, Fps frameInterval, + std::optional<Period> timeoutOpt) override; // for debugging ---------------------------------------------------------- void dump(std::string& out) const override; @@ -497,7 +499,10 @@ private: sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires nsecs_t lastPresentTimestamp = 0; - nsecs_t lastExpectedPresentTimestamp = 0; + std::mutex expectedPresentLock; + TimePoint lastExpectedPresentTimestamp GUARDED_BY(expectedPresentLock) = + TimePoint::fromNs(0); + Fps lastFrameInterval GUARDED_BY(expectedPresentLock) = Fps::fromValue(0); std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; diff --git a/services/surfaceflinger/FlagManager.cpp b/services/surfaceflinger/FlagManager.cpp index a8f05bbd56..6d0dbc7978 100644 --- a/services/surfaceflinger/FlagManager.cpp +++ b/services/surfaceflinger/FlagManager.cpp @@ -60,10 +60,6 @@ bool getFlagValue(std::function<bool()> getter, std::optional<bool> overrideValu return getter(); } -void dumpFlag(std::string& result, const char* name, std::function<bool()> getter) { - base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false"); -} - } // namespace const FlagManager& FlagManager::getInstance() { @@ -90,20 +86,43 @@ void FlagManager::setUnitTestMode() { mBootCompleted = true; } +void FlagManager::dumpFlag(std::string& result, bool readonly, const char* name, + std::function<bool()> getter) const { + if (readonly || mBootCompleted) { + base::StringAppendF(&result, "%s: %s\n", name, getter() ? "true" : "false"); + } else { + base::StringAppendF(&result, "%s: in progress (still booting)\n", name); + } +} + void FlagManager::dump(std::string& result) const { -#define DUMP_FLAG(name) dumpFlag(result, #name, std::bind(&FlagManager::name, this)) +#define DUMP_FLAG_INTERVAL(name, readonly) \ + dumpFlag(result, (readonly), #name, std::bind(&FlagManager::name, this)) +#define DUMP_SERVER_FLAG(name) DUMP_FLAG_INTERVAL(name, false) +#define DUMP_READ_ONLY_FLAG(name) DUMP_FLAG_INTERVAL(name, true) base::StringAppendF(&result, "FlagManager values: \n"); - DUMP_FLAG(use_adpf_cpu_hint); - DUMP_FLAG(use_skia_tracing); - DUMP_FLAG(connected_display); - DUMP_FLAG(dont_skip_on_early); - DUMP_FLAG(enable_small_area_detection); - DUMP_FLAG(misc1); - DUMP_FLAG(late_boot_misc2); - DUMP_FLAG(vrr_config); - -#undef DUMP_FLAG + + /// Legacy server flags /// + DUMP_SERVER_FLAG(use_adpf_cpu_hint); + DUMP_SERVER_FLAG(use_skia_tracing); + + /// Trunk stable server flags /// + DUMP_SERVER_FLAG(late_boot_misc2); + DUMP_SERVER_FLAG(dont_skip_on_early); + + /// Trunk stable readonly flags /// + DUMP_READ_ONLY_FLAG(connected_display); + DUMP_READ_ONLY_FLAG(enable_small_area_detection); + DUMP_READ_ONLY_FLAG(misc1); + DUMP_READ_ONLY_FLAG(vrr_config); + DUMP_READ_ONLY_FLAG(hotplug2); + DUMP_READ_ONLY_FLAG(hdcp_level_hal); + DUMP_READ_ONLY_FLAG(multithreaded_present); + +#undef DUMP_READ_ONLY_FLAG +#undef DUMP_SERVER_FLAG +#undef DUMP_FLAG_INTERVAL } std::optional<bool> FlagManager::getBoolProperty(const char* property) const { @@ -165,6 +184,9 @@ FLAG_MANAGER_READ_ONLY_FLAG(connected_display, "") FLAG_MANAGER_READ_ONLY_FLAG(enable_small_area_detection, "") FLAG_MANAGER_READ_ONLY_FLAG(misc1, "") FLAG_MANAGER_READ_ONLY_FLAG(vrr_config, "debug.sf.enable_vrr_config") +FLAG_MANAGER_READ_ONLY_FLAG(hotplug2, "") +FLAG_MANAGER_READ_ONLY_FLAG(hdcp_level_hal, "") +FLAG_MANAGER_READ_ONLY_FLAG(multithreaded_present, "debug.sf.multithreaded_present") /// Trunk stable server flags /// FLAG_MANAGER_SERVER_FLAG(late_boot_misc2, "") diff --git a/services/surfaceflinger/FlagManager.h b/services/surfaceflinger/FlagManager.h index 5a353bbe6c..cefce9bcda 100644 --- a/services/surfaceflinger/FlagManager.h +++ b/services/surfaceflinger/FlagManager.h @@ -46,15 +46,18 @@ public: bool use_adpf_cpu_hint() const; bool use_skia_tracing() const; + /// Trunk stable server flags /// + bool late_boot_misc2() const; + bool dont_skip_on_early() const; + /// Trunk stable readonly flags /// bool connected_display() const; bool enable_small_area_detection() const; bool misc1() const; bool vrr_config() const; - - /// Trunk stable server flags /// - bool late_boot_misc2() const; - bool dont_skip_on_early() const; + bool hotplug2() const; + bool hdcp_level_hal() const; + bool multithreaded_present() const; protected: // overridden for unit tests @@ -66,6 +69,9 @@ private: FlagManager(const FlagManager&) = delete; + void dumpFlag(std::string& result, bool readonly, const char* name, + std::function<bool()> getter) const; + std::atomic_bool mBootCompleted = false; std::atomic_bool mUnitTestMode = false; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp index 7a85da056e..38974a2f58 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.cpp @@ -332,6 +332,14 @@ FloatRect LayerSnapshot::sourceBounds() const { return geomBufferSize.toFloatRect(); } +bool LayerSnapshot::isFrontBuffered() const { + if (!externalTexture) { + return false; + } + + return externalTexture->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; +} + Hwc2::IComposerClient::BlendMode LayerSnapshot::getBlendMode( const RequestedLayerState& requested) const { auto blendMode = Hwc2::IComposerClient::BlendMode::NONE; diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshot.h b/services/surfaceflinger/FrontEnd/LayerSnapshot.h index 4fd6495422..73ee22fa21 100644 --- a/services/surfaceflinger/FrontEnd/LayerSnapshot.h +++ b/services/surfaceflinger/FrontEnd/LayerSnapshot.h @@ -143,6 +143,7 @@ struct LayerSnapshot : public compositionengine::LayerFECompositionState { std::string getIsVisibleReason() const; bool hasInputInfo() const; FloatRect sourceBounds() const; + bool isFrontBuffered() const; Hwc2::IComposerClient::BlendMode getBlendMode(const RequestedLayerState& requested) const; friend std::ostream& operator<<(std::ostream& os, const LayerSnapshot& obj); void merge(const RequestedLayerState& requested, bool forceUpdate, bool displayChanges, diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 700baa22a6..66ea15ca2d 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -4024,10 +4024,10 @@ bool Layer::isVisible() const { return getAlpha() > 0.0f || hasBlur(); } -void Layer::onPostComposition(const DisplayDevice* display, - const std::shared_ptr<FenceTime>& glDoneFence, - const std::shared_ptr<FenceTime>& presentFence, - const CompositorTiming& compositorTiming) { +void Layer::onCompositionPresented(const DisplayDevice* display, + const std::shared_ptr<FenceTime>& glDoneFence, + const std::shared_ptr<FenceTime>& presentFence, + const CompositorTiming& compositorTiming) { // mFrameLatencyNeeded is true when a new frame was latched for the // composition. if (!mBufferInfo.mFrameLatencyNeeded) return; @@ -4230,6 +4230,14 @@ ui::Dataspace Layer::getDataSpace() const { return hasBufferOrSidebandStream() ? mBufferInfo.mDataspace : mDrawingState.dataspace; } +bool Layer::isFrontBuffered() const { + if (mBufferInfo.mBuffer == nullptr) { + return false; + } + + return mBufferInfo.mBuffer->getUsage() & AHARDWAREBUFFER_USAGE_FRONT_BUFFER; +} + ui::Dataspace Layer::translateDataspace(ui::Dataspace dataspace) { ui::Dataspace updatedDataspace = dataspace; // translate legacy dataspaces to modern dataspaces diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index dd91adcba7..f71591044d 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -342,6 +342,8 @@ public: // ui::Dataspace getDataSpace() const; + virtual bool isFrontBuffered() const; + virtual sp<LayerFE> getCompositionEngineLayerFE() const; virtual sp<LayerFE> copyCompositionEngineLayerFE() const; sp<LayerFE> getCompositionEngineLayerFE(const frontend::LayerHierarchy::TraversalPath&); @@ -433,13 +435,10 @@ public: void updateCloneBufferInfo(); uint64_t mPreviousFrameNumber = 0; - /* - * called after composition. - * returns true if the layer latched a new buffer this frame. - */ - void onPostComposition(const DisplayDevice*, const std::shared_ptr<FenceTime>& /*glDoneFence*/, - const std::shared_ptr<FenceTime>& /*presentFence*/, - const CompositorTiming&); + void onCompositionPresented(const DisplayDevice*, + const std::shared_ptr<FenceTime>& /*glDoneFence*/, + const std::shared_ptr<FenceTime>& /*presentFence*/, + const CompositorTiming&); // If a buffer was replaced this frame, release the former buffer void releasePendingBuffer(nsecs_t /*dequeueReadyTime*/); @@ -915,14 +914,13 @@ public: void recordLayerHistoryBufferUpdate(const scheduler::LayerProps&, nsecs_t now); void recordLayerHistoryAnimationTx(const scheduler::LayerProps&, nsecs_t now); auto getLayerProps() const { - return scheduler::LayerProps{ - .visible = isVisible(), - .bounds = getBounds(), - .transform = getTransform(), - .setFrameRateVote = getFrameRateForLayerTree(), - .frameRateSelectionPriority = getFrameRateSelectionPriority(), - .isSmallDirty = mSmallDirty, - }; + return scheduler::LayerProps{.visible = isVisible(), + .bounds = getBounds(), + .transform = getTransform(), + .setFrameRateVote = getFrameRateForLayerTree(), + .frameRateSelectionPriority = getFrameRateSelectionPriority(), + .isSmallDirty = mSmallDirty, + .isFrontBuffered = isFrontBuffered()}; }; bool hasBuffer() const { return mBufferInfo.mBuffer != nullptr; } void setTransformHint(std::optional<ui::Transform::RotationFlags> transformHint) { diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp index 21bbb08c1c..8fc9cba794 100644 --- a/services/surfaceflinger/Scheduler/LayerHistory.cpp +++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp @@ -51,6 +51,11 @@ bool isLayerActive(const LayerInfo& info, nsecs_t threshold) { return true; } + // Make all front buffered layers active + if (FlagManager::getInstance().vrr_config() && info.isFrontBuffered() && info.isVisible()) { + return true; + } + return info.isVisible() && info.getLastUpdatedTime() >= threshold; } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 54e9022add..8d18769e5a 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -334,6 +334,14 @@ LayerInfo::RefreshRateVotes LayerInfo::getRefreshRateVote(const RefreshRateSelec return votes; } + // Vote for max refresh rate whenever we're front-buffered. + if (FlagManager::getInstance().vrr_config() && isFrontBuffered()) { + ATRACE_FORMAT_INSTANT("front buffered"); + ALOGV("%s is front-buffered", mName.c_str()); + votes.push_back({LayerHistory::LayerVoteType::Max, Fps()}); + return votes; + } + const LayerInfo::Frequent frequent = isFrequent(now); mIsFrequencyConclusive = frequent.isConclusive; if (!frequent.isFrequent) { @@ -394,6 +402,10 @@ int32_t LayerInfo::getFrameRateSelectionPriority() const { return mLayerProps->frameRateSelectionPriority; } +bool LayerInfo::isFrontBuffered() const { + return mLayerProps->isFrontBuffered; +} + FloatRect LayerInfo::getBounds() const { return mLayerProps->bounds; } diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h index 7d3cffabf7..03ab0df3cd 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.h +++ b/services/surfaceflinger/Scheduler/LayerInfo.h @@ -200,6 +200,7 @@ public: FrameRate getSetFrameRateVote() const; bool isVisible() const; int32_t getFrameRateSelectionPriority() const; + bool isFrontBuffered() const; FloatRect getBounds() const; ui::Transform getTransform() const; @@ -360,6 +361,7 @@ struct LayerProps { LayerInfo::FrameRate setFrameRateVote; int32_t frameRateSelectionPriority = -1; bool isSmallDirty = false; + bool isFrontBuffered = false; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index 1a8713d4a9..56a4ae2569 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -67,11 +67,12 @@ namespace android::scheduler { Scheduler::Scheduler(ICompositor& compositor, ISchedulerCallback& callback, FeatureFlags features, - sp<VsyncModulator> modulatorPtr) + sp<VsyncModulator> modulatorPtr, IVsyncTrackerCallback& vsyncTrackerCallback) : impl::MessageQueue(compositor), mFeatures(features), mVsyncModulator(std::move(modulatorPtr)), - mSchedulerCallback(callback) {} + mSchedulerCallback(callback), + mVsyncTrackerCallback(vsyncTrackerCallback) {} Scheduler::~Scheduler() { // MessageQueue depends on VsyncSchedule, so first destroy it. @@ -116,10 +117,10 @@ void Scheduler::setPacesetterDisplay(std::optional<PhysicalDisplayId> pacesetter } void Scheduler::registerDisplay(PhysicalDisplayId displayId, RefreshRateSelectorPtr selectorPtr) { - auto schedulePtr = std::make_shared<VsyncSchedule>(displayId, mFeatures, - [this](PhysicalDisplayId id, bool enable) { - onHardwareVsyncRequest(id, enable); - }); + auto schedulePtr = std::make_shared<VsyncSchedule>( + displayId, mFeatures, + [this](PhysicalDisplayId id, bool enable) { onHardwareVsyncRequest(id, enable); }, + mVsyncTrackerCallback); registerDisplayInternal(displayId, std::move(selectorPtr), std::move(schedulePtr)); } @@ -562,7 +563,19 @@ void Scheduler::setRenderRate(PhysicalDisplayId id, Fps renderFrameRate) { ALOGV("%s %s (%s)", __func__, to_string(mode.fps).c_str(), to_string(mode.modePtr->getVsyncRate()).c_str()); - display.schedulePtr->getTracker().setRenderRate(renderFrameRate); + display.schedulePtr->getTracker().setDisplayModeData( + {.renderRate = renderFrameRate, + .notifyExpectedPresentTimeoutOpt = getNotifyExpectedPresentTimeout(mode)}); +} + +std::optional<Period> Scheduler::getNotifyExpectedPresentTimeout(const FrameRateMode& mode) { + if (mode.modePtr->getVrrConfig() && mode.modePtr->getVrrConfig()->notifyExpectedPresentConfig) { + return Period::fromNs( + mode.modePtr->getVrrConfig() + ->notifyExpectedPresentConfig->notifyExpectedPresentTimeoutNs); + } else { + return std::nullopt; + } } void Scheduler::resync() { @@ -1165,7 +1178,7 @@ void Scheduler::onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimel } } -bool Scheduler::onPostComposition(nsecs_t presentTime) { +bool Scheduler::onCompositionPresented(nsecs_t presentTime) { std::lock_guard<std::mutex> lock(mVsyncTimelineLock); if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) { if (presentTime < mLastVsyncPeriodChangeTimeline->refreshTimeNanos) { diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h index b0520a61ff..a02180af8d 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.h +++ b/services/surfaceflinger/Scheduler/Scheduler.h @@ -102,7 +102,8 @@ class Scheduler : public IEventThreadCallback, android::impl::MessageQueue { using Impl = android::impl::MessageQueue; public: - Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>); + Scheduler(ICompositor&, ISchedulerCallback&, FeatureFlags, sp<VsyncModulator>, + IVsyncTrackerCallback&); virtual ~Scheduler(); void startTimers(); @@ -281,8 +282,8 @@ public: // Notifies the scheduler about a refresh rate timeline change. void onNewVsyncPeriodChangeTimeline(const hal::VsyncPeriodChangeTimeline& timeline); - // Notifies the scheduler post composition. Returns if recomposite is needed. - bool onPostComposition(nsecs_t presentTime); + // Notifies the scheduler once the composition is presented. Returns if recomposite is needed. + bool onCompositionPresented(nsecs_t presentTime); // Notifies the scheduler when the display size has changed. Called from SF's main thread void onActiveDisplayAreaChanged(uint32_t displayArea); @@ -429,6 +430,9 @@ private: Period getVsyncPeriod(uid_t) override EXCLUDES(mDisplayLock); void resync() override EXCLUDES(mDisplayLock); + std::optional<Period> getNotifyExpectedPresentTimeout(const FrameRateMode&) + REQUIRES(mDisplayLock); + // Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection. struct Connection { sp<EventThreadConnection> connection; @@ -462,6 +466,8 @@ private: ISchedulerCallback& mSchedulerCallback; + IVsyncTrackerCallback& mVsyncTrackerCallback; + // mDisplayLock may be locked while under mPolicyLock. mutable std::mutex mPolicyLock; diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp index 186a6bc9d6..3e7ec492fa 100644 --- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp +++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp @@ -38,19 +38,16 @@ using base::StringAppendF; namespace { -nsecs_t getExpectedCallbackTime(nsecs_t now, nsecs_t nextVsyncTime, +nsecs_t getExpectedCallbackTime(nsecs_t nextVsyncTime, const VSyncDispatch::ScheduleTiming& timing) { - const auto expectedCallbackTime = nextVsyncTime - timing.readyDuration - timing.workDuration; - const auto baseTime = - FlagManager::getInstance().dont_skip_on_early() ? now : expectedCallbackTime; - return std::max(baseTime, expectedCallbackTime); + return nextVsyncTime - timing.readyDuration - timing.workDuration; } nsecs_t getExpectedCallbackTime(VSyncTracker& tracker, nsecs_t now, const VSyncDispatch::ScheduleTiming& timing) { const auto nextVsyncTime = tracker.nextAnticipatedVSyncTimeFrom( std::max(timing.earliestVsync, now + timing.workDuration + timing.readyDuration)); - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return getExpectedCallbackTime(nextVsyncTime, timing); } } // namespace @@ -106,21 +103,23 @@ ScheduleResult VSyncDispatchTimerQueueEntry::schedule(VSyncDispatch::ScheduleTim mArmedInfo && ((nextWakeupTime > (mArmedInfo->mActualWakeupTime + mMinVsyncDistance))); if (FlagManager::getInstance().dont_skip_on_early()) { if (wouldSkipAVsyncTarget || wouldSkipAWakeup) { - return getExpectedCallbackTime(now, mArmedInfo->mActualVsyncTime, timing); + nextVsyncTime = mArmedInfo->mActualVsyncTime; + } else { + nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); } + nextWakeupTime = std::max(now, nextVsyncTime - timing.workDuration - timing.readyDuration); } else { if (wouldSkipAVsyncTarget && wouldSkipAWakeup) { - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return getExpectedCallbackTime(nextVsyncTime, timing); } + nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); + nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; } - nextVsyncTime = adjustVsyncIfNeeded(tracker, nextVsyncTime); - nextWakeupTime = nextVsyncTime - timing.workDuration - timing.readyDuration; - auto const nextReadyTime = nextVsyncTime - timing.readyDuration; mScheduleTiming = timing; mArmedInfo = {nextWakeupTime, nextVsyncTime, nextReadyTime}; - return getExpectedCallbackTime(now, nextVsyncTime, timing); + return nextWakeupTime; } void VSyncDispatchTimerQueueEntry::addPendingWorkloadUpdate(VSyncDispatch::ScheduleTiming timing) { diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp index e969fdc679..57aa010740 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp @@ -35,6 +35,7 @@ #include <gui/TraceUtils.h> #include <utils/Log.h> +#include "FlagManager.h" #include "RefreshRateSelector.h" #include "VSyncPredictor.h" @@ -47,12 +48,14 @@ static auto constexpr kMaxPercent = 100u; VSyncPredictor::~VSyncPredictor() = default; VSyncPredictor::VSyncPredictor(PhysicalDisplayId id, nsecs_t idealPeriod, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent) + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, + IVsyncTrackerCallback& callback) : mId(id), mTraceOn(property_get_bool("debug.sf.vsp_trace", false)), kHistorySize(historySize), kMinimumSamplesForPrediction(minimumSamplesForPrediction), kOutlierTolerancePercent(std::min(outlierTolerancePercent, kMaxPercent)), + mVsyncTrackerCallback(callback), mIdealPeriod(idealPeriod) { resetModel(); } @@ -275,11 +278,11 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { mLastVsyncSequence = getVsyncSequenceLocked(timePoint); const auto renderRatePhase = [&]() REQUIRES(mMutex) -> int { - if (!mRenderRate) return 0; + if (!mDisplayModeDataOpt) return 0; const auto divisor = RefreshRateSelector::getFrameRateDivisor(Fps::fromPeriodNsecs(mIdealPeriod), - *mRenderRate); + mDisplayModeDataOpt->renderRate); if (divisor <= 1) return 0; const int mod = mLastVsyncSequence->seq % divisor; @@ -289,12 +292,29 @@ nsecs_t VSyncPredictor::nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const { }(); if (renderRatePhase == 0) { - return mLastVsyncSequence->vsyncTime; + const auto vsyncTime = mLastVsyncSequence->vsyncTime; + if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime); + ATRACE_FORMAT("%s InPhase vsyncIn %.2fms", __func__, + ticks<std::milli, float>(vsyncTimePoint - TimePoint::now())); + mVsyncTrackerCallback.onVsyncGenerated(mId, vsyncTimePoint, *mDisplayModeDataOpt, + Period::fromNs(mIdealPeriod)); + } + return vsyncTime; } auto const [slope, intercept] = getVSyncPredictionModelLocked(); const auto approximateNextVsync = mLastVsyncSequence->vsyncTime + slope * renderRatePhase; - return nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); + const auto nextAnticipatedVsyncTime = + nextAnticipatedVSyncTimeFromLocked(approximateNextVsync - slope / 2); + if (FlagManager::getInstance().vrr_config() && mDisplayModeDataOpt) { + const auto nextAnticipatedVsyncTimePoint = TimePoint::fromNs(nextAnticipatedVsyncTime); + ATRACE_FORMAT("%s outOfPhase vsyncIn %.2fms", __func__, + ticks<std::milli, float>(nextAnticipatedVsyncTimePoint - TimePoint::now())); + mVsyncTrackerCallback.onVsyncGenerated(mId, nextAnticipatedVsyncTimePoint, + *mDisplayModeDataOpt, Period::fromNs(mIdealPeriod)); + } + return nextAnticipatedVsyncTime; } /* @@ -332,10 +352,14 @@ bool VSyncPredictor::isVSyncInPhaseLocked(nsecs_t timePoint, unsigned divisor) c return vsyncSequence.seq % divisor == 0; } -void VSyncPredictor::setRenderRate(Fps fps) { - ALOGV("%s %s: %s", __func__, to_string(mId).c_str(), to_string(fps).c_str()); +void VSyncPredictor::setDisplayModeData(const DisplayModeData& displayModeData) { + ALOGV("%s %s: RenderRate %s notifyExpectedPresentTimeout %s", __func__, to_string(mId).c_str(), + to_string(displayModeData.renderRate).c_str(), + displayModeData.notifyExpectedPresentTimeoutOpt + ? std::to_string(displayModeData.notifyExpectedPresentTimeoutOpt->ns()).c_str() + : "N/A"); std::lock_guard lock(mMutex); - mRenderRate = fps; + mDisplayModeDataOpt = displayModeData; } VSyncPredictor::Model VSyncPredictor::getVSyncPredictionModel() const { @@ -358,6 +382,7 @@ void VSyncPredictor::setPeriod(nsecs_t period) { mRateMap.erase(mRateMap.begin()); } + // TODO(b/308610306) mIdealPeriod to be updated with setDisplayModeData mIdealPeriod = period; if (mRateMap.find(period) == mRateMap.end()) { mRateMap[mIdealPeriod] = {period, 0}; diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h index c01c44dc6b..c271eb738e 100644 --- a/services/surfaceflinger/Scheduler/VSyncPredictor.h +++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h @@ -36,9 +36,11 @@ public: * \param [in] minimumSamplesForPrediction The minimum number of samples to collect before * predicting. \param [in] outlierTolerancePercent a number 0 to 100 that will be used to filter * samples that fall outlierTolerancePercent from an anticipated vsync event. + * \param [in] IVsyncTrackerCallback The callback for the VSyncTracker. */ VSyncPredictor(PhysicalDisplayId, nsecs_t idealPeriod, size_t historySize, - size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent); + size_t minimumSamplesForPrediction, uint32_t outlierTolerancePercent, + IVsyncTrackerCallback&); ~VSyncPredictor(); bool addVsyncTimestamp(nsecs_t timestamp) final EXCLUDES(mMutex); @@ -69,7 +71,7 @@ public: bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const final EXCLUDES(mMutex); - void setRenderRate(Fps) final EXCLUDES(mMutex); + void setDisplayModeData(const DisplayModeData&) final EXCLUDES(mMutex); void dump(std::string& result) const final EXCLUDES(mMutex); @@ -99,6 +101,7 @@ private: size_t const kHistorySize; size_t const kMinimumSamplesForPrediction; size_t const kOutlierTolerancePercent; + IVsyncTrackerCallback& mVsyncTrackerCallback; std::mutex mutable mMutex; nsecs_t mIdealPeriod GUARDED_BY(mMutex); @@ -110,7 +113,7 @@ private: size_t mLastTimestampIndex GUARDED_BY(mMutex) = 0; std::vector<nsecs_t> mTimestamps GUARDED_BY(mMutex); - std::optional<Fps> mRenderRate GUARDED_BY(mMutex); + std::optional<DisplayModeData> mDisplayModeDataOpt GUARDED_BY(mMutex); mutable std::optional<VsyncSequence> mLastVsyncSequence GUARDED_BY(mMutex); }; diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h index bc0e3bcbb2..7eedc312e2 100644 --- a/services/surfaceflinger/Scheduler/VSyncTracker.h +++ b/services/surfaceflinger/Scheduler/VSyncTracker.h @@ -16,6 +16,7 @@ #pragma once +#include <ui/DisplayId.h> #include <utils/Timers.h> #include <scheduler/Fps.h> @@ -23,6 +24,23 @@ #include "VSyncDispatch.h" namespace android::scheduler { + +struct DisplayModeData { + Fps renderRate; + std::optional<Period> notifyExpectedPresentTimeoutOpt; + + bool operator==(const DisplayModeData& other) const { + return isApproxEqual(renderRate, other.renderRate) && + notifyExpectedPresentTimeoutOpt == other.notifyExpectedPresentTimeoutOpt; + } +}; + +struct IVsyncTrackerCallback { + virtual ~IVsyncTrackerCallback() = default; + virtual void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, + const DisplayModeData&, Period vsyncPeriod) = 0; +}; + /* * VSyncTracker is an interface for providing estimates on future Vsync signal times based on * historical vsync timing data. @@ -80,16 +98,20 @@ public: virtual bool isVSyncInPhase(nsecs_t timePoint, Fps frameRate) const = 0; /* - * Sets a render rate on the tracker. If the render rate is not a divisor - * of the period, the render rate is ignored until the period changes. + * Sets the metadata about the currently active display mode such as VRR + * timeout period, vsyncPeriod and framework property such as render rate. + * If the render rate is not a divisor of the period, the render rate is + * ignored until the period changes. * The tracker will continue to track the vsync timeline and expect it * to match the current period, however, nextAnticipatedVSyncTimeFrom will * return vsyncs according to the render rate set. Setting a render rate is useful * when a display is running at 120Hz but the render frame rate is 60Hz. + * When IVsyncTrackerCallback::onVsyncGenerated callback is made we will pass along + * the vsyncPeriod, render rate and timeoutNs. * - * \param [in] Fps The render rate the tracker should operate at. + * \param [in] DisplayModeData The DisplayModeData the tracker will use. */ - virtual void setRenderRate(Fps) = 0; + virtual void setDisplayModeData(const DisplayModeData&) = 0; virtual void dump(std::string& result) const = 0; diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp index ff3f29dbbf..5fb53f9e20 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.cpp +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.cpp @@ -54,10 +54,11 @@ private: }; VsyncSchedule::VsyncSchedule(PhysicalDisplayId id, FeatureFlags features, - RequestHardwareVsync requestHardwareVsync) + RequestHardwareVsync requestHardwareVsync, + IVsyncTrackerCallback& callback) : mId(id), mRequestHardwareVsync(std::move(requestHardwareVsync)), - mTracker(createTracker(id)), + mTracker(createTracker(id, callback)), mDispatch(createDispatch(mTracker)), mController(createController(id, *mTracker, features)), mTracer(features.test(Feature::kTracePredictedVsync) @@ -100,7 +101,8 @@ void VsyncSchedule::dump(std::string& out) const { mDispatch->dump(out); } -VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) { +VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id, + IVsyncTrackerCallback& callback) { // TODO(b/144707443): Tune constants. constexpr nsecs_t kInitialPeriod = (60_Hz).getPeriodNsecs(); constexpr size_t kHistorySize = 20; @@ -108,7 +110,8 @@ VsyncSchedule::TrackerPtr VsyncSchedule::createTracker(PhysicalDisplayId id) { constexpr uint32_t kDiscardOutlierPercent = 20; return std::make_unique<VSyncPredictor>(id, kInitialPeriod, kHistorySize, - kMinSamplesForPrediction, kDiscardOutlierPercent); + kMinSamplesForPrediction, kDiscardOutlierPercent, + callback); } VsyncSchedule::DispatchPtr VsyncSchedule::createDispatch(TrackerPtr tracker) { diff --git a/services/surfaceflinger/Scheduler/VsyncSchedule.h b/services/surfaceflinger/Scheduler/VsyncSchedule.h index 47e92e107d..ca61f875c3 100644 --- a/services/surfaceflinger/Scheduler/VsyncSchedule.h +++ b/services/surfaceflinger/Scheduler/VsyncSchedule.h @@ -31,6 +31,7 @@ #include <scheduler/Time.h> #include "ThreadContext.h" +#include "VSyncTracker.h" namespace android { class EventThreadTest; @@ -56,7 +57,7 @@ class VsyncSchedule final : public IVsyncSource { public: using RequestHardwareVsync = std::function<void(PhysicalDisplayId, bool enabled)>; - VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync); + VsyncSchedule(PhysicalDisplayId, FeatureFlags, RequestHardwareVsync, IVsyncTrackerCallback&); ~VsyncSchedule(); // IVsyncSource overrides: @@ -124,7 +125,7 @@ private: friend class android::VsyncScheduleTest; friend class android::fuzz::SchedulerFuzzer; - static TrackerPtr createTracker(PhysicalDisplayId); + static TrackerPtr createTracker(PhysicalDisplayId, IVsyncTrackerCallback&); static DispatchPtr createDispatch(TrackerPtr); static ControllerPtr createController(PhysicalDisplayId, VsyncTracker&, FeatureFlags); diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 62eb17d2e7..4d02b44201 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2247,6 +2247,7 @@ void SurfaceFlinger::updateLayerHistory(nsecs_t now) { .setFrameRateVote = snapshot->frameRate, .frameRateSelectionPriority = snapshot->frameRateSelectionPriority, .isSmallDirty = snapshot->isSmallDirty, + .isFrontBuffered = snapshot->isFrontBuffered(), }; if (snapshot->clientChanges & layer_state_t::eDefaultFrameRateCompatibilityChanged) { @@ -2747,11 +2748,11 @@ CompositeResultsPerDisplay SurfaceFlinger::composite( mPowerAdvisor->reportActualWorkDuration(); } - if (mScheduler->onPostComposition(presentTime)) { + if (mScheduler->onCompositionPresented(presentTime)) { scheduleComposite(FrameHint::kNone); } - postComposition(pacesetterId, frameTargeters, presentTime); + onCompositionPresented(pacesetterId, frameTargeters, presentTime); const bool hadGpuComposited = multiDisplayUnion(mCompositionCoverage).test(CompositionCoverage::Gpu); @@ -2908,9 +2909,9 @@ ui::Rotation SurfaceFlinger::getPhysicalDisplayOrientation(DisplayId displayId, return ui::ROTATION_0; } -void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId, - const scheduler::FrameTargeters& frameTargeters, - nsecs_t presentStartTime) { +void SurfaceFlinger::onCompositionPresented(PhysicalDisplayId pacesetterId, + const scheduler::FrameTargeters& frameTargeters, + nsecs_t presentStartTime) { ATRACE_CALL(); ALOGV(__func__); @@ -3004,8 +3005,9 @@ void SurfaceFlinger::postComposition(PhysicalDisplayId pacesetterId, mLayersWithBuffersRemoved.clear(); for (const auto& layer: mLayersWithQueuedFrames) { - layer->onPostComposition(pacesetterDisplay.get(), pacesetterGpuCompositionDoneFenceTime, - pacesetterPresentFenceTime, compositorTiming); + layer->onCompositionPresented(pacesetterDisplay.get(), + pacesetterGpuCompositionDoneFenceTime, + pacesetterPresentFenceTime, compositorTiming); layer->releasePendingBuffer(presentTime.ns()); } @@ -4036,6 +4038,21 @@ void SurfaceFlinger::onChoreographerAttached() { } } +void SurfaceFlinger::onVsyncGenerated(PhysicalDisplayId displayId, TimePoint expectedPresentTime, + const scheduler::DisplayModeData& displayModeData, + Period vsyncPeriod) { + const auto status = + getHwComposer() + .notifyExpectedPresentIfRequired(displayId, vsyncPeriod, expectedPresentTime, + displayModeData.renderRate, + displayModeData + .notifyExpectedPresentTimeoutOpt); + if (status != NO_ERROR) { + ALOGE("%s failed to notifyExpectedPresentHint for display %" PRId64, __func__, + displayId.value); + } +} + void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { using namespace scheduler; @@ -4074,8 +4091,12 @@ void SurfaceFlinger::initScheduler(const sp<const DisplayDevice>& display) { mScheduler = std::make_unique<Scheduler>(static_cast<ICompositor&>(*this), static_cast<ISchedulerCallback&>(*this), features, - std::move(modulatorPtr)); + std::move(modulatorPtr), + static_cast<IVsyncTrackerCallback&>(*this)); mScheduler->registerDisplay(display->getPhysicalId(), display->holdRefreshRateSelector()); + if (FlagManager::getInstance().vrr_config()) { + mScheduler->setRenderRate(display->getPhysicalId(), activeMode.fps); + } mScheduler->startTimers(); const auto configs = mVsyncConfiguration->getCurrentConfigs(); @@ -4880,6 +4901,7 @@ bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelin .transform = layer->getTransform(), .setFrameRateVote = layer->getFrameRateForLayerTree(), .frameRateSelectionPriority = layer->getFrameRateSelectionPriority(), + .isFrontBuffered = layer->isFrontBuffered(), }; layer->recordLayerHistoryAnimationTx(layerProps, now); } @@ -5965,7 +5987,9 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, bool asProto) { }); out << "\nLayer Hierarchy\n" - << mLayerHierarchyBuilder.getHierarchy() << "\n\n"; + << mLayerHierarchyBuilder.getHierarchy() + << "\nOffscreen Hierarchy\n" + << mLayerHierarchyBuilder.getOffscreenHierarchy() << "\n\n"; compositionLayers = out.str(); dumpHwcLayersMinidump(compositionLayers); } @@ -6245,7 +6269,9 @@ void SurfaceFlinger::dumpFrontEnd(std::string& result) { }); out << "\nLayer Hierarchy\n" - << mLayerHierarchyBuilder.getHierarchy().dump() << "\n\n"; + << mLayerHierarchyBuilder.getHierarchy().dump() + << "\nOffscreen Hierarchy\n" + << mLayerHierarchyBuilder.getOffscreenHierarchy().dump() << "\n\n"; result.append(out.str()); }) .get(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 520bd221b3..1e90340449 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -195,7 +195,8 @@ class SurfaceFlinger : public BnSurfaceComposer, private HWC2::ComposerCallback, private ICompositor, private scheduler::ISchedulerCallback, - private compositionengine::ICEPowerCallback { + private compositionengine::ICEPowerCallback, + private scheduler::IVsyncTrackerCallback { public: struct SkipInitializationTag {}; @@ -656,6 +657,10 @@ private: // ICEPowerCallback overrides: void notifyCpuLoadUp() override; + // IVsyncTrackerCallback overrides + void onVsyncGenerated(PhysicalDisplayId, TimePoint expectedPresentTime, + const scheduler::DisplayModeData&, Period vsyncPeriod) override; + // Toggles the kernel idle timer on or off depending the policy decisions around refresh rates. void toggleKernelIdleTimer() REQUIRES(mStateLock); @@ -982,8 +987,8 @@ private: /* * Compositing */ - void postComposition(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&, - nsecs_t presentStartTime) REQUIRES(kMainThreadContext); + void onCompositionPresented(PhysicalDisplayId pacesetterId, const scheduler::FrameTargeters&, + nsecs_t presentStartTime) REQUIRES(kMainThreadContext); /* * Display management diff --git a/services/surfaceflinger/fuzzer/Android.bp b/services/surfaceflinger/fuzzer/Android.bp index 910e6852a9..243b8e04df 100644 --- a/services/surfaceflinger/fuzzer/Android.bp +++ b/services/surfaceflinger/fuzzer/Android.bp @@ -74,9 +74,17 @@ cc_defaults { ], fuzz_config: { cc: [ - "android-media-fuzzing-reports@google.com", + "android-cogs-eng@google.com", ], - componentid: 155276, + componentid: 1075131, + hotlists: [ + "4593311", + ], + description: "The fuzzer targets the APIs of libsurfaceflinger library", + vector: "local_no_privileges_required", + service_privilege: "privileged", + users: "multi_user", + fuzzed_code_usage: "shipped", }, } diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h index c4077dfbaa..9b2d4536cd 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_fuzzers_utils.h @@ -225,16 +225,19 @@ namespace scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: TestableScheduler(const std::shared_ptr<scheduler::RefreshRateSelector>& selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) : TestableScheduler(std::make_unique<android::mock::VsyncController>(), std::make_shared<android::mock::VSyncTracker>(), selectorPtr, - std::move(modulatorPtr), callback) {} + std::move(modulatorPtr), callback, vsyncTrackerCallback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, VsyncSchedule::TrackerPtr tracker, std::shared_ptr<RefreshRateSelector> selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) - : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr)) { + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) + : Scheduler(*this, callback, Feature::kContentDetection, std::move(modulatorPtr), + vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplayInternal(displayId, std::move(selectorPtr), std::shared_ptr<VsyncSchedule>( @@ -400,7 +403,8 @@ public: } // namespace surfaceflinger::test // TODO(b/189053744) : Create a common test/mock library for surfaceflinger -class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback { +class TestableSurfaceFlinger final : private scheduler::ISchedulerCallback, + private scheduler::IVsyncTrackerCallback { public: using HotplugEvent = SurfaceFlinger::HotplugEvent; @@ -610,8 +614,8 @@ public: mFlinger->flushTransactionQueues(getFuzzedVsyncId(mFdp)); scheduler::FrameTargeter frameTargeter(displayId, mFdp.ConsumeBool()); - mFlinger->postComposition(displayId, ftl::init::map(displayId, &frameTargeter), - mFdp.ConsumeIntegral<nsecs_t>()); + mFlinger->onCompositionPresented(displayId, ftl::init::map(displayId, &frameTargeter), + mFdp.ConsumeIntegral<nsecs_t>()); } mFlinger->setTransactionFlags(mFdp.ConsumeIntegral<uint32_t>()); @@ -656,6 +660,7 @@ public: std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, scheduler::ISchedulerCallback* callback = nullptr, + scheduler::IVsyncTrackerCallback* vsyncTrackerCallback = nullptr, bool hasMultipleModes = false) { constexpr DisplayModeId kModeId60{0}; DisplayModes modes = makeModes(mock::createDisplayMode(kModeId60, 60_Hz)); @@ -678,7 +683,8 @@ public: mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), mRefreshRateSelector, - std::move(modulatorPtr), *(callback ?: this)); + std::move(modulatorPtr), *(callback ?: this), + *(vsyncTrackerCallback ?: this)); mFlinger->mAppConnectionHandle = mScheduler->createConnection(std::move(appEventThread)); mFlinger->mSfConnectionHandle = mScheduler->createConnection(std::move(sfEventThread)); @@ -799,6 +805,10 @@ private: void triggerOnFrameRateOverridesChanged() override {} void onChoreographerAttached() override {} + // IVsyncTrackerCallback overrides + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override {} + surfaceflinger::test::Factory mFactory; sp<SurfaceFlinger> mFlinger = sp<SurfaceFlinger>::make(mFactory, SurfaceFlinger::SkipInitialization); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp index 39a7ee5c12..7aae3c4a1a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_layer_fuzzer.cpp @@ -133,7 +133,7 @@ void LayerFuzzer::invokeBufferStateLayer() { ui::LayerStack::fromValue(mFdp.ConsumeIntegral<uint32_t>())); layer->releasePendingBuffer(mFdp.ConsumeIntegral<int64_t>()); - layer->onPostComposition(nullptr, fenceTime, fenceTime, compositorTiming); + layer->onCompositionPresented(nullptr, fenceTime, fenceTime, compositorTiming); layer->setTransform(mFdp.ConsumeIntegral<uint32_t>()); layer->setTransformToDisplayInverse(mFdp.ConsumeBool()); diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp index a8727f966c..8fcfd8131a 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.cpp @@ -178,14 +178,23 @@ void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { dump<scheduler::VSyncDispatchTimerQueueEntry>(&entry, &mFdp); } +struct VsyncTrackerCallback : public scheduler::IVsyncTrackerCallback { + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override {} +}; + void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral<uint16_t>(); uint16_t historySize = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange<uint16_t>(1, UINT16_MAX); nsecs_t idealPeriod = mFdp.ConsumeIntegralInRange<nsecs_t>(1, UINT32_MAX); - scheduler::VSyncPredictor tracker{kDisplayId, idealPeriod, historySize, + VsyncTrackerCallback callback; + scheduler::VSyncPredictor tracker{kDisplayId, + idealPeriod, + historySize, minimumSamplesForPrediction, - mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/}; + mFdp.ConsumeIntegral<uint32_t>() /*outlierTolerancePercent*/, + callback}; uint16_t period = mFdp.ConsumeIntegral<uint16_t>(); tracker.setPeriod(period); for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { diff --git a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h index 8061a8f2dc..728708f05c 100644 --- a/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h +++ b/services/surfaceflinger/fuzzer/surfaceflinger_scheduler_fuzzer.h @@ -100,7 +100,7 @@ public: return true; } - void setRenderRate(Fps) override {} + void setDisplayModeData(const scheduler::DisplayModeData&) override {} nsecs_t nextVSyncTime(nsecs_t timePoint) const { if (timePoint % mPeriod == 0) { diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 19d194f5b7..a81f9b80de 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -52,4 +52,21 @@ flag { description: "Feature flag for SmallAreaDetection" bug: "283055450" is_fixed_read_only: true -}
\ No newline at end of file +} + +flag { + name: "hotplug2" + namespace: "core_graphics" + description: "Feature flag for using hotplug2 HAL API" + bug: "303460805" + is_fixed_read_only: true +} + +flag { + name: "hdcp_level_hal" + namespace: "core_graphics" + description: "Feature flag for adding a HAL API to commuicate hdcp levels" + bug: "285359126" + is_fixed_read_only: true +} + diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index fa31643df1..13796650c8 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -80,7 +80,8 @@ void DisplayTransactionTest::injectMockScheduler(PhysicalDisplayId displayId) { std::unique_ptr<EventThread>(mEventThread), std::unique_ptr<EventThread>(mSFEventThread), TestableSurfaceFlinger::DefaultDisplayMode{displayId}, - TestableSurfaceFlinger::SchedulerCallbackImpl::kMock); + TestableSurfaceFlinger::SchedulerCallbackImpl::kMock, + TestableSurfaceFlinger::VsyncTrackerCallbackImpl::kMock); } void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { diff --git a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp index 4f545a9ef3..58d7a40fc8 100644 --- a/services/surfaceflinger/tests/unittests/HWComposerTest.cpp +++ b/services/surfaceflinger/tests/unittests/HWComposerTest.cpp @@ -79,10 +79,13 @@ struct HWComposerTest : testing::Test { EXPECT_CALL(*mHal, onHotplugConnect(hwcDisplayId)); } - void setDisplayData(HalDisplayId displayId, nsecs_t lastExpectedPresentTimestamp) { + void setDisplayData(HalDisplayId displayId, TimePoint lastExpectedPresentTimestamp, + Fps lastFrameInterval) { ASSERT_TRUE(mHwc.mDisplayData.find(displayId) != mHwc.mDisplayData.end()); auto& displayData = mHwc.mDisplayData.at(displayId); + std::scoped_lock lock{displayData.expectedPresentLock}; displayData.lastExpectedPresentTimestamp = lastExpectedPresentTimestamp; + displayData.lastFrameInterval = lastFrameInterval; } }; @@ -322,48 +325,137 @@ TEST_F(HWComposerTest, notifyExpectedPresentTimeout) { ASSERT_TRUE(info); auto expectedPresentTime = systemTime() + ms2ns(10); - const int32_t frameIntervalNs = static_cast<Fps>(60_Hz).getPeriodNsecs(); - static constexpr nsecs_t kTimeoutNs = ms2ns(30); + static constexpr Fps Fps60Hz = 60_Hz; + static constexpr int32_t kFrameInterval5HzNs = static_cast<Fps>(5_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameInterval60HzNs = Fps60Hz.getPeriodNsecs(); + static constexpr int32_t kFrameInterval120HzNs = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + static constexpr Period kTimeoutNs = Period::fromNs(kFrameInterval5HzNs); + static constexpr auto kLastExpectedPresentTimestamp = TimePoint::fromNs(0); - ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, /* lastExpectedPresentTimestamp= */ 0)); + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, kLastExpectedPresentTimestamp, Fps60Hz)); { // Very first ExpectedPresent after idle, no previous timestamp EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { + // Absent timeoutNs + expectedPresentTime += 2 * kFrameInterval5HzNs; + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + /*timeoutOpt*/ std::nullopt); + } + { + // Timeout is 0 + expectedPresentTime += kFrameInterval60HzNs; + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) + .WillOnce(Return(HalError::NONE)); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + Period::fromNs(0)); + } + { // ExpectedPresent is after the timeoutNs - expectedPresentTime += ms2ns(50); + expectedPresentTime += 2 * kFrameInterval5HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, + kTimeoutNs); + } + { + // ExpectedPresent has not changed + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is after the last reported ExpectedPresent. - expectedPresentTime += ms2ns(10); + expectedPresentTime += kFrameInterval60HzNs; EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } { // ExpectedPresent is before the last reported ExpectedPresent but after the timeoutNs, // representing we changed our decision and want to present earlier than previously // reported. - expectedPresentTime -= ms2ns(20); + expectedPresentTime -= kFrameInterval120HzNs; EXPECT_CALL(*mHal, - notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, frameIntervalNs)) + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, kFrameInterval60HzNs)) .WillOnce(Return(HalError::NONE)); - mHwc.notifyExpectedPresentIfRequired(info->id, expectedPresentTime, frameIntervalNs, + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), Fps60Hz, kTimeoutNs); } } +TEST_F(HWComposerTest, notifyExpectedPresentRenderRateChanged) { + constexpr hal::HWDisplayId kHwcDisplayId = 2; + expectHotplugConnect(kHwcDisplayId); + const auto info = mHwc.onHotplug(kHwcDisplayId, hal::Connection::CONNECTED); + ASSERT_TRUE(info); + + const auto now = systemTime(); + auto expectedPresentTime = now; + static constexpr Period kTimeoutNs = Period::fromNs(static_cast<Fps>(1_Hz).getPeriodNsecs()); + + ASSERT_NO_FATAL_FAILURE(setDisplayData(info->id, TimePoint::fromNs(now), Fps::fromValue(0))); + static constexpr int32_t kFrameIntervalNs120Hz = static_cast<Fps>(120_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs96Hz = static_cast<Fps>(96_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs80Hz = static_cast<Fps>(80_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs60Hz = static_cast<Fps>(60_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs40Hz = static_cast<Fps>(40_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs30Hz = static_cast<Fps>(30_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs24Hz = static_cast<Fps>(24_Hz).getPeriodNsecs(); + static constexpr int32_t kFrameIntervalNs20Hz = static_cast<Fps>(20_Hz).getPeriodNsecs(); + static constexpr Period kVsyncPeriod = + Period::fromNs(static_cast<Fps>(240_Hz).getPeriodNsecs()); + + struct FrameRateIntervalTestData { + int32_t frameIntervalNs; + bool callExpectedPresent; + }; + const std::vector<FrameRateIntervalTestData> frameIntervals = { + {kFrameIntervalNs60Hz, true}, {kFrameIntervalNs96Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs120Hz, true}, + {kFrameIntervalNs80Hz, true}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs60Hz, false}, {kFrameIntervalNs30Hz, false}, + {kFrameIntervalNs24Hz, true}, {kFrameIntervalNs40Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs60Hz, true}, + {kFrameIntervalNs20Hz, false}, {kFrameIntervalNs120Hz, true}, + }; + + for (const auto& [frameIntervalNs, callExpectedPresent] : frameIntervals) { + { + expectedPresentTime += frameIntervalNs; + if (callExpectedPresent) { + EXPECT_CALL(*mHal, + notifyExpectedPresent(kHwcDisplayId, expectedPresentTime, + frameIntervalNs)) + .WillOnce(Return(HalError::NONE)); + } else { + EXPECT_CALL(*mHal, notifyExpectedPresent(kHwcDisplayId, _, _)).Times(0); + } + mHwc.notifyExpectedPresentIfRequired(info->id, kVsyncPeriod, + TimePoint::fromNs(expectedPresentTime), + Fps::fromPeriodNsecs(frameIntervalNs), kTimeoutNs); + } + } +} + struct MockHWC2ComposerCallback final : StrictMock<HWC2::ComposerCallback> { MOCK_METHOD2(onComposerHalHotplug, void(hal::HWDisplayId, hal::Connection)); MOCK_METHOD1(onComposerHalRefresh, void(hal::HWDisplayId)); diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp index 2f6058f7a6..190c0e8c10 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryIntegrationTest.cpp @@ -165,8 +165,10 @@ protected: DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; + mock::VsyncTrackerCallback mVsyncTrackerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp index e8831ab51f..1adf14f587 100644 --- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp @@ -144,7 +144,9 @@ protected: mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; @@ -925,6 +927,43 @@ TEST_F(LayerHistoryTest, infrequentAnimatingLayer) { EXPECT_EQ(1, animatingLayerCount(time)); } +TEST_F(LayerHistoryTest, frontBufferedLayerVotesMax) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + auto layer = createLayer(); + + EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true)); + EXPECT_CALL(*layer, getFrameRateForLayerTree()).WillRepeatedly(Return(Layer::FrameRate())); + EXPECT_CALL(*layer, isFrontBuffered()).WillRepeatedly(Return(true)); + + nsecs_t time = systemTime(); + + EXPECT_EQ(1, layerCount()); + EXPECT_EQ(0, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer is active but infrequent. + for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) { + history().record(layer->getSequence(), layer->getLayerProps(), time, time, + LayerHistory::LayerUpdateType::Buffer); + time += MAX_FREQUENT_LAYER_PERIOD_NS.count(); + } + + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); + + // layer became inactive + time += MAX_ACTIVE_LAYER_PERIOD_NS.count(); + ASSERT_EQ(1, summarizeLayerHistory(time).size()); + EXPECT_EQ(LayerHistory::LayerVoteType::Max, summarizeLayerHistory(time)[0].vote); + EXPECT_EQ(1, activeLayerCount()); + EXPECT_EQ(0, frequentLayerCount(time)); + EXPECT_EQ(0, animatingLayerCount(time)); +} + TEST_F(LayerHistoryTest, frequentLayerBecomingInfrequentAndBack) { auto layer = createLayer(); diff --git a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp index 11072bc4c9..047ef5a928 100644 --- a/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerInfoTest.cpp @@ -21,6 +21,7 @@ #include <scheduler/Fps.h> +#include "FlagUtils.h" #include "FpsOps.h" #include "Scheduler/LayerHistory.h" #include "Scheduler/LayerInfo.h" @@ -28,6 +29,8 @@ #include "TestableSurfaceFlinger.h" #include "mock/MockSchedulerCallback.h" +#include <com_android_graphics_surfaceflinger_flags.h> + namespace android::scheduler { using android::mock::createDisplayMode; @@ -61,12 +64,16 @@ protected: HI_FPS)), DisplayModeId(0)); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler(mSelector, mSchedulerCallback); + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler(mSelector, mSchedulerCallback, mVsyncTrackerCallback); TestableSurfaceFlinger mFlinger; }; namespace { +using namespace com::android::graphics::surfaceflinger; + TEST_F(LayerInfoTest, prefersPresentTime) { std::deque<FrameTimeData> frameTimes; constexpr auto kExpectedFps = 50_Hz; @@ -261,5 +268,18 @@ TEST_F(LayerInfoTest, getRefreshRateVote_noData) { ASSERT_EQ(actualVotes[0].fps, vote.fps); } +TEST_F(LayerInfoTest, isFrontBuffered) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + ASSERT_FALSE(layerInfo.isFrontBuffered()); + + LayerProps prop = {.isFrontBuffered = true}; + layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop); + ASSERT_TRUE(layerInfo.isFrontBuffered()); + + prop.isFrontBuffered = false; + layerInfo.setLastPresentTime(0, 0, LayerHistory::LayerUpdateType::Buffer, true, prop); + ASSERT_FALSE(layerInfo.isFrontBuffered()); +} + } // namespace } // namespace android::scheduler diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index e784eb76f3..57babafac3 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -898,4 +898,24 @@ TEST_F(LayerSnapshotTest, setTrustedOverlayForNonVisibleInput) { gui::WindowInfo::InputConfig::TRUSTED_OVERLAY)); } +TEST_F(LayerSnapshotTest, isFrontBuffered) { + setBuffer(1, + std::make_shared<renderengine::mock::FakeExternalTexture>( + 1U /*width*/, 1U /*height*/, 1ULL /* bufferId */, HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_HW_TEXTURE | AHARDWAREBUFFER_USAGE_FRONT_BUFFER /*usage*/)); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_TRUE(getSnapshot(1)->isFrontBuffered()); + + setBuffer(1, + std::make_shared< + renderengine::mock::FakeExternalTexture>(1U /*width*/, 1U /*height*/, + 1ULL /* bufferId */, + HAL_PIXEL_FORMAT_RGBA_8888, + GRALLOC_USAGE_HW_TEXTURE /*usage*/)); + + UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); + EXPECT_FALSE(getSnapshot(1)->isFrontBuffered()); +} + } // namespace android::surfaceflinger::frontend diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp index 87fae2c312..b5eb777a4d 100644 --- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp +++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp @@ -89,7 +89,9 @@ protected: kDisplay1Mode60->getId()); mock::SchedulerCallback mSchedulerCallback; - TestableScheduler* mScheduler = new TestableScheduler{mSelector, mSchedulerCallback}; + mock::VsyncTrackerCallback mVsyncTrackerCallback; + TestableScheduler* mScheduler = + new TestableScheduler{mSelector, mSchedulerCallback, mVsyncTrackerCallback}; surfaceflinger::frontend::LayerHierarchyBuilder mLayerHierarchyBuilder{{}}; ConnectionHandle mConnectionHandle; diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h index 3d1c900dcf..8b6f0f1b94 100644 --- a/services/surfaceflinger/tests/unittests/TestableScheduler.h +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -36,18 +36,21 @@ namespace android::scheduler { class TestableScheduler : public Scheduler, private ICompositor { public: - TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback) + TestableScheduler(RefreshRateSelectorPtr selectorPtr, ISchedulerCallback& callback, + IVsyncTrackerCallback& vsyncTrackerCallback) : TestableScheduler(std::make_unique<mock::VsyncController>(), std::make_shared<mock::VSyncTracker>(), std::move(selectorPtr), - sp<VsyncModulator>::make(VsyncConfigSet{}), callback) {} + sp<VsyncModulator>::make(VsyncConfigSet{}), callback, + vsyncTrackerCallback) {} TestableScheduler(std::unique_ptr<VsyncController> controller, std::shared_ptr<VSyncTracker> tracker, RefreshRateSelectorPtr selectorPtr, - sp<VsyncModulator> modulatorPtr, ISchedulerCallback& callback) - : Scheduler(*this, callback, + sp<VsyncModulator> modulatorPtr, ISchedulerCallback& schedulerCallback, + IVsyncTrackerCallback& vsyncTrackerCallback) + : Scheduler(*this, schedulerCallback, (FeatureFlags)Feature::kContentDetection | Feature::kSmallDirtyContentDetection, - std::move(modulatorPtr)) { + std::move(modulatorPtr), vsyncTrackerCallback) { const auto displayId = selectorPtr->getActiveMode().modePtr->getPhysicalDisplayId(); registerDisplay(displayId, std::move(selectorPtr), std::move(controller), std::move(tracker)); diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 03af56cc71..d0b2199c58 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -53,6 +53,7 @@ #include "mock/MockFrameTimeline.h" #include "mock/MockFrameTracer.h" #include "mock/MockSchedulerCallback.h" +#include "mock/MockVsyncTrackerCallback.h" #include "mock/system/window/MockNativeWindow.h" #include "Scheduler/VSyncTracker.h" @@ -204,6 +205,8 @@ public: enum class SchedulerCallbackImpl { kNoOp, kMock }; + enum class VsyncTrackerCallbackImpl { kNoOp, kMock }; + struct DefaultDisplayMode { // The ID of the injected RefreshRateSelector and its default display mode. PhysicalDisplayId displayId; @@ -213,13 +216,14 @@ public: using DisplayModesVariant = std::variant<DefaultDisplayMode, RefreshRateSelectorPtr>; - void setupScheduler(std::unique_ptr<scheduler::VsyncController> vsyncController, - std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, - std::unique_ptr<EventThread> appEventThread, - std::unique_ptr<EventThread> sfEventThread, - DisplayModesVariant modesVariant, - SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, - bool useNiceMock = false) { + void setupScheduler( + std::unique_ptr<scheduler::VsyncController> vsyncController, + std::shared_ptr<scheduler::VSyncTracker> vsyncTracker, + std::unique_ptr<EventThread> appEventThread, std::unique_ptr<EventThread> sfEventThread, + DisplayModesVariant modesVariant, + SchedulerCallbackImpl callbackImpl = SchedulerCallbackImpl::kNoOp, + VsyncTrackerCallbackImpl vsyncTrackerCallbackImpl = VsyncTrackerCallbackImpl::kNoOp, + bool useNiceMock = false) { RefreshRateSelectorPtr selectorPtr = ftl::match( modesVariant, [](DefaultDisplayMode arg) { @@ -239,10 +243,16 @@ public: mTokenManager = std::make_unique<frametimeline::impl::TokenManager>(); - using Callback = scheduler::ISchedulerCallback; - Callback& callback = callbackImpl == SchedulerCallbackImpl::kNoOp - ? static_cast<Callback&>(mNoOpSchedulerCallback) - : static_cast<Callback&>(mSchedulerCallback); + using ISchedulerCallback = scheduler::ISchedulerCallback; + ISchedulerCallback& schedulerCallback = callbackImpl == SchedulerCallbackImpl::kNoOp + ? static_cast<ISchedulerCallback&>(mNoOpSchedulerCallback) + : static_cast<ISchedulerCallback&>(mSchedulerCallback); + + using VsyncTrackerCallback = scheduler::IVsyncTrackerCallback; + VsyncTrackerCallback& vsyncTrackerCallback = + vsyncTrackerCallbackImpl == VsyncTrackerCallbackImpl::kNoOp + ? static_cast<VsyncTrackerCallback&>(mNoOpVsyncTrackerCallback) + : static_cast<VsyncTrackerCallback&>(mVsyncTrackerCallback); auto modulatorPtr = sp<scheduler::VsyncModulator>::make( mFlinger->mVsyncConfiguration->getCurrentConfigs()); @@ -253,12 +263,14 @@ public: std::move(vsyncTracker), std::move(selectorPtr), std::move(modulatorPtr), - callback); + schedulerCallback, + vsyncTrackerCallback); } else { mScheduler = new scheduler::TestableScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(selectorPtr), - std::move(modulatorPtr), callback); + std::move(modulatorPtr), + schedulerCallback, vsyncTrackerCallback); } mScheduler->initVsync(mScheduler->getVsyncSchedule()->getDispatch(), *mTokenManager, 0ms); @@ -297,7 +309,8 @@ public: EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0)); setupScheduler(std::move(vsyncController), std::move(vsyncTracker), std::move(eventThread), std::move(sfEventThread), DefaultDisplayMode{options.displayId}, - SchedulerCallbackImpl::kNoOp, options.useNiceMock); + SchedulerCallbackImpl::kNoOp, VsyncTrackerCallbackImpl::kNoOp, + options.useNiceMock); } void resetScheduler(scheduler::Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); } @@ -1071,6 +1084,8 @@ private: sp<SurfaceFlinger> mFlinger; scheduler::mock::SchedulerCallback mSchedulerCallback; scheduler::mock::NoOpSchedulerCallback mNoOpSchedulerCallback; + scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; + scheduler::mock::NoOpVsyncTrackerCallback mNoOpVsyncTrackerCallback; std::unique_ptr<frametimeline::impl::TokenManager> mTokenManager; scheduler::TestableScheduler* mScheduler = nullptr; Hwc2::mock::PowerAdvisor mPowerAdvisor; diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 00b5bf0506..d4d5b32341 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -112,7 +112,7 @@ public: EXPECT_CALL(*mFlinger.getFrameTracer(), traceFence(layerId, bufferId, frameNumber, presentFence, FrameTracer::FrameEvent::PRESENT_FENCE, /*startTime*/ 0)); - layer->onPostComposition(nullptr, glDoneFence, presentFence, compositorTiming); + layer->onCompositionPresented(nullptr, glDoneFence, presentFence, compositorTiming); } }; diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp index 41866a1c50..4be07a1ddb 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp @@ -54,7 +54,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setRenderRate(Fps) final {} + void setDisplayModeData(const DisplayModeData&) final {} void dump(std::string&) const final {} private: @@ -92,7 +92,7 @@ public: void resetModel() final {} bool needsMoreSamples() const final { return false; } bool isVSyncInPhase(nsecs_t, Fps) const final { return false; } - void setRenderRate(Fps) final {} + void setDisplayModeData(const DisplayModeData&) final {} void dump(std::string&) const final {} private: diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp index 1dc5498221..83108662b9 100644 --- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp @@ -59,7 +59,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); nsecs_t nextVSyncTime(nsecs_t timePoint) const { @@ -783,7 +783,9 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipASchedule TEST_F(VSyncDispatchTimerQueueTest, movesCallbackBackwardsAndSkipAScheduledTargetVSync) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 500)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 400)).InSequence(seq); CountingCallback cb(mDispatch); auto result = mDispatch->schedule(cb, @@ -873,7 +875,9 @@ TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesAffectSchedulingState) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 600)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq); CountingCallback cb(mDispatch); auto result = @@ -1119,6 +1123,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { Sequence seq; EXPECT_CALL(mMockClock, alarmAt(_, 600)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 0)).InSequence(seq); CountingCallback cb(mDispatch); @@ -1132,7 +1137,7 @@ TEST_F(VSyncDispatchTimerQueueTest, doesNotUpdatesVsyncTimeForCloseWakeupTime) { ASSERT_THAT(cb.mCalls.size(), Eq(1)); EXPECT_THAT(cb.mCalls[0], Eq(1000)); ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); - EXPECT_THAT(cb.mWakeupTime[0], Eq(600)); + EXPECT_THAT(cb.mWakeupTime[0], Eq(0)); ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); EXPECT_THAT(cb.mReadyTime[0], Eq(1000)); } @@ -1161,7 +1166,9 @@ TEST_F(VSyncDispatchTimerQueueTest, skipAVsyc) { TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { SET_FLAG_FOR_TEST(flags::dont_skip_on_early, true); - EXPECT_CALL(mMockClock, alarmAt(_, 500)); + Sequence seq; + EXPECT_CALL(mMockClock, alarmAt(_, 500)).InSequence(seq); + EXPECT_CALL(mMockClock, alarmAt(_, 300)).InSequence(seq); CountingCallback cb(mDispatch); auto result = mDispatch->schedule(cb, @@ -1177,6 +1184,11 @@ TEST_F(VSyncDispatchTimerQueueTest, dontskipAVsyc) { advanceToNextCallback(); ASSERT_THAT(cb.mCalls.size(), Eq(1)); + EXPECT_THAT(cb.mCalls[0], Eq(1000)); + ASSERT_THAT(cb.mWakeupTime.size(), Eq(1)); + EXPECT_THAT(cb.mWakeupTime[0], Eq(300)); + ASSERT_THAT(cb.mReadyTime.size(), Eq(1)); + EXPECT_THAT(cb.mReadyTime[0], Eq(1000)); } class VSyncDispatchTimerQueueEntryTest : public testing::Test { diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp index 43d683d0fa..30a2855955 100644 --- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp @@ -23,7 +23,9 @@ #define LOG_TAG "LibSurfaceFlingerUnittests" #define LOG_NDEBUG 0 +#include "FlagUtils.h" #include "Scheduler/VSyncPredictor.h" +#include "mock/MockVsyncTrackerCallback.h" #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -31,8 +33,11 @@ #include <chrono> #include <utility> +#include <com_android_graphics_surfaceflinger_flags.h> + using namespace testing; using namespace std::literals; +using namespace com::android::graphics::surfaceflinger; namespace android::scheduler { @@ -52,13 +57,18 @@ constexpr PhysicalDisplayId DEFAULT_DISPLAY_ID = PhysicalDisplayId::fromPort(42u struct VSyncPredictorTest : testing::Test { nsecs_t mNow = 0; nsecs_t mPeriod = 1000; + scheduler::mock::VsyncTrackerCallback mVsyncTrackerCallback; static constexpr size_t kHistorySize = 10; static constexpr size_t kMinimumSamplesForPrediction = 6; static constexpr size_t kOutlierTolerancePercent = 25; static constexpr nsecs_t mMaxRoundingError = 100; - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, kHistorySize, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, + mPeriod, + kHistorySize, + kMinimumSamplesForPrediction, + kOutlierTolerancePercent, + mVsyncTrackerCallback}; }; TEST_F(VSyncPredictorTest, reportsAnticipatedPeriod) { @@ -378,8 +388,12 @@ TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) { // See b/151146131 TEST_F(VSyncPredictorTest, hasEnoughPrecision) { - VSyncPredictor tracker{DEFAULT_DISPLAY_ID, mPeriod, 20, kMinimumSamplesForPrediction, - kOutlierTolerancePercent}; + VSyncPredictor tracker{DEFAULT_DISPLAY_ID, + mPeriod, + 20, + kMinimumSamplesForPrediction, + kOutlierTolerancePercent, + mVsyncTrackerCallback}; std::vector<nsecs_t> const simulatedVsyncs{840873348817, 840890049444, 840906762675, 840923581635, 840940161584, 840956868096, 840973702473, 840990256277, 841007116851, @@ -566,7 +580,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsRespected) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3 * mPeriod)); + tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3 * mPeriod)}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -588,12 +602,12 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); - tracker.setRenderRate(refreshRate / 4); + tracker.setDisplayModeData({.renderRate = refreshRate / 4}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 7 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setRenderRate(refreshRate / 2); + tracker.setDisplayModeData({.renderRate = refreshRate / 2}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 3 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 3 * mPeriod), Eq(mNow + 5 * mPeriod)); @@ -601,7 +615,7 @@ TEST_F(VSyncPredictorTest, setRenderRateOfDivisorIsInPhase) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 7 * mPeriod), Eq(mNow + 9 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 9 * mPeriod), Eq(mNow + 11 * mPeriod)); - tracker.setRenderRate(refreshRate / 6); + tracker.setDisplayModeData({.renderRate = refreshRate / 6}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + 1 * mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), Eq(mNow + 7 * mPeriod)); } @@ -615,7 +629,7 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { tracker.addVsyncTimestamp(mNow); } - tracker.setRenderRate(Fps::fromPeriodNsecs(3.5f * mPeriod)); + tracker.setDisplayModeData({.renderRate = Fps::fromPeriodNsecs(3.5f * mPeriod)}); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(mNow + mPeriod)); EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 100), Eq(mNow + mPeriod)); @@ -626,6 +640,39 @@ TEST_F(VSyncPredictorTest, setRenderRateIsIgnoredIfNotDivisor) { EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 5100), Eq(mNow + 6 * mPeriod)); } +TEST_F(VSyncPredictorTest, vsyncTrackerCallback) { + SET_FLAG_FOR_TEST(flags::vrr_config, true); + const auto refreshRate = Fps::fromPeriodNsecs(mPeriod); + DisplayModeData displayModeData = + DisplayModeData{.renderRate = refreshRate, + .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; + tracker.setDisplayModeData(displayModeData); + auto last = mNow; + for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) { + EXPECT_CALL(mVsyncTrackerCallback, + onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(last + mPeriod), + displayModeData, Period::fromNs(mPeriod))) + .Times(1); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod)); + mNow += mPeriod; + last = mNow; + tracker.addVsyncTimestamp(mNow); + } + + displayModeData = DisplayModeData{.renderRate = refreshRate / 2, + .notifyExpectedPresentTimeoutOpt = Period::fromNs(30)}; + tracker.setDisplayModeData(displayModeData); + { + // out of render rate phase + EXPECT_CALL(mVsyncTrackerCallback, + onVsyncGenerated(DEFAULT_DISPLAY_ID, TimePoint::fromNs(mNow + 3 * mPeriod), + displayModeData, Period::fromNs(mPeriod))) + .Times(1); + EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow + 1 * mPeriod), + Eq(mNow + 3 * mPeriod)); + } +} + } // namespace android::scheduler // TODO(b/129481165): remove the #pragma below and fix conversion issues diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp index 122192b036..aca3ccca6d 100644 --- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp +++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp @@ -50,7 +50,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/tests/unittests/mock/MockLayer.h b/services/surfaceflinger/tests/unittests/mock/MockLayer.h index 4cc78febee..3dfb649a02 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockLayer.h +++ b/services/surfaceflinger/tests/unittests/mock/MockLayer.h @@ -37,6 +37,7 @@ public: MOCK_CONST_METHOD0(getDefaultFrameRateCompatibility, scheduler::FrameRateCompatibility()); MOCK_CONST_METHOD0(getOwnerUid, uid_t()); MOCK_CONST_METHOD0(getDataSpace, ui::Dataspace()); + MOCK_METHOD(bool, isFrontBuffered, (), (const, override)); }; } // namespace android::mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h index dcf25e18a8..31eb86e4c5 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVSyncTracker.h @@ -34,7 +34,7 @@ public: MOCK_METHOD0(resetModel, void()); MOCK_CONST_METHOD0(needsMoreSamples, bool()); MOCK_CONST_METHOD2(isVSyncInPhase, bool(nsecs_t, Fps)); - MOCK_METHOD(void, setRenderRate, (Fps), (override)); + MOCK_METHOD(void, setDisplayModeData, (const scheduler::DisplayModeData&), (override)); MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/libs/gui/include/gui/Flags.h b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h index a2cff56e97..b8e24e0593 100644 --- a/libs/gui/include/gui/Flags.h +++ b/services/surfaceflinger/tests/unittests/mock/MockVsyncTrackerCallback.h @@ -16,7 +16,20 @@ #pragma once -// TODO(281695725): replace this with build time flags, whenever they are available -#ifndef FLAG_BQ_SET_FRAME_RATE -#define FLAG_BQ_SET_FRAME_RATE false -#endif
\ No newline at end of file +#include <gmock/gmock.h> + +#include "Scheduler/VSyncTracker.h" + +namespace android::scheduler::mock { + +struct VsyncTrackerCallback final : IVsyncTrackerCallback { + MOCK_METHOD(void, onVsyncGenerated, + (PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, Period), + (override)); +}; + +struct NoOpVsyncTrackerCallback final : IVsyncTrackerCallback { + void onVsyncGenerated(PhysicalDisplayId, TimePoint, const scheduler::DisplayModeData&, + Period) override{}; +}; +} // namespace android::scheduler::mock |