diff options
46 files changed, 1685 insertions, 948 deletions
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 0ce7711574..9695e07753 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -95,7 +95,7 @@ public: flags = O_RDWR; checkRead = checkWrite = true; } else { - mErrorLog << "Invalid mode requested: " << mode.c_str() << endl; + mErrorLog << "Invalid mode requested: " << mode << endl; return -EINVAL; } int fd = open(fullPath.c_str(), flags, S_IRWXU|S_IRWXG); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index b99f443fa6..0bbd4a86ba 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -246,7 +246,7 @@ static const std::string DUMP_NETSTATS_PROTO_TASK = "DUMP NETSTATS PROTO"; static const std::string DUMP_HALS_TASK = "DUMP HALS"; static const std::string DUMP_BOARD_TASK = "dumpstate_board()"; static const std::string DUMP_CHECKINS_TASK = "DUMP CHECKINS"; -static const std::string POST_PROCESS_UI_TRACES_TASK = "POST-PROCESS UI TRACES"; +static const std::string SERIALIZE_PERFETTO_TRACE_TASK = "SERIALIZE PERFETTO TRACE"; namespace android { namespace os { @@ -1086,11 +1086,11 @@ static void DumpNetstatsProto() { static void MaybeAddSystemTraceToZip() { // This function copies into the .zip the system trace that was snapshotted - // by the early call to MaybeSnapshotSystemTrace(), if any background + // by the early call to MaybeSnapshotSystemTraceAsync(), if any background // tracing was happening. bool system_trace_exists = access(SYSTEM_TRACE_SNAPSHOT, F_OK) == 0; if (!system_trace_exists) { - // No background trace was happening at the time MaybeSnapshotSystemTrace() was invoked. + // No background trace was happening at the time MaybeSnapshotSystemTraceAsync() was invoked if (!PropertiesHelper::IsUserBuild()) { MYLOGI( "No system traces found. Check for previously uploaded traces by looking for " @@ -1641,7 +1641,7 @@ Dumpstate::RunStatus Dumpstate::dumpstate() { // Enqueue slow functions into the thread pool, if the parallel run is enabled. std::future<std::string> dump_hals, dump_incident_report, dump_board, dump_checkins, - dump_netstats_report, post_process_ui_traces; + dump_netstats_report; if (ds.dump_pool_) { // Pool was shutdown in DumpstateDefaultAfterCritical method in order to // drop root user. Restarts it. @@ -3077,8 +3077,9 @@ void Dumpstate::Cancel() { } void Dumpstate::PreDumpUiData() { - MaybeSnapshotSystemTrace(); + auto snapshot_system_trace = MaybeSnapshotSystemTraceAsync(); MaybeSnapshotUiTraces(); + MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace)); } /* @@ -3264,13 +3265,15 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // duration is logged into MYLOG instead. PrintHeader(); + std::future<std::string> snapshot_system_trace; + bool is_dumpstate_restricted = options_->telephony_only || options_->wifi_only || options_->limited_only; if (!is_dumpstate_restricted) { // Snapshot the system trace now (if running) to avoid that dumpstate's // own activity pushes out interesting data from the trace ring buffer. // The trace file is added to the zip by MaybeAddSystemTraceToZip(). - MaybeSnapshotSystemTrace(); + snapshot_system_trace = MaybeSnapshotSystemTraceAsync(); // Invoke critical dumpsys to preserve system state, before doing anything else. RunDumpsysCritical(); @@ -3281,6 +3284,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, } MaybeTakeEarlyScreenshot(); + MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace)); onUiIntensiveBugreportDumpsFinished(calling_uid); MaybeCheckUserConsent(calling_uid, calling_package); if (options_->telephony_only) { @@ -3376,31 +3380,59 @@ void Dumpstate::MaybeTakeEarlyScreenshot() { TakeScreenshot(); } -void Dumpstate::MaybeSnapshotSystemTrace() { +std::future<std::string> Dumpstate::MaybeSnapshotSystemTraceAsync() { // When capturing traces via bugreport handler (BH), this function will be invoked twice: // 1) When BH invokes IDumpstate::PreDumpUiData() // 2) When BH invokes IDumpstate::startBugreport(flags = BUGREPORT_USE_PREDUMPED_UI_DATA) // In this case we don't want to re-invoke perfetto in step 2. // In all other standard invocation states, this function is invoked once // without the flag BUGREPORT_USE_PREDUMPED_UI_DATA. + // This function must run asynchronously to avoid delaying MaybeTakeEarlyScreenshot() in the + // standard invocation states (b/316110955). if (options_->use_predumped_ui_data) { - return; + return {}; + } + + // Create temporary file for the command's output + std::string outPath = ds.bugreport_internal_dir_ + "/tmp_serialize_perfetto_trace"; + auto outFd = android::base::unique_fd(TEMP_FAILURE_RETRY( + open(outPath.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (outFd < 0) { + MYLOGE("Could not open %s to serialize perfetto trace.\n", outPath.c_str()); + return {}; } // If a stale file exists already, remove it. unlink(SYSTEM_TRACE_SNAPSHOT); - // If a background system trace is happening and is marked as "suitable for - // bugreport" (i.e. bugreport_score > 0 in the trace config), this command - // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) - // case that no trace is ongoing, this command is a no-op. - // Note: this should not be enqueued as we need to freeze the trace before - // dumpstate starts. Otherwise the trace ring buffers will contain mostly - // the dumpstate's own activity which is irrelevant. - RunCommand("SERIALIZE PERFETTO TRACE", {"perfetto", "--save-for-bugreport"}, - CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build()); - // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip - // file in the later stages. + MYLOGI("Launching async '%s'", SERIALIZE_PERFETTO_TRACE_TASK.c_str()) + return std::async( + std::launch::async, [this, outPath = std::move(outPath), outFd = std::move(outFd)] { + // If a background system trace is happening and is marked as "suitable for + // bugreport" (i.e. bugreport_score > 0 in the trace config), this command + // will stop it and serialize into SYSTEM_TRACE_SNAPSHOT. In the (likely) + // case that no trace is ongoing, this command is a no-op. + // Note: this should not be enqueued as we need to freeze the trace before + // dumpstate starts. Otherwise the trace ring buffers will contain mostly + // the dumpstate's own activity which is irrelevant. + RunCommand( + SERIALIZE_PERFETTO_TRACE_TASK, {"perfetto", "--save-for-bugreport"}, + CommandOptions::WithTimeout(10).DropRoot().CloseAllFileDescriptorsOnExec().Build(), + false, outFd); + // MaybeAddSystemTraceToZip() will take care of copying the trace in the zip + // file in the later stages. + + return outPath; + }); +} + +void Dumpstate::MaybeWaitForSnapshotSystemTrace(std::future<std::string> task) { + if (!task.valid()) { + return; + } + + WaitForTask(std::move(task), SERIALIZE_PERFETTO_TRACE_TASK, STDOUT_FILENO); } void Dumpstate::MaybeSnapshotUiTraces() { diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index c66fd1cce6..20b28656ce 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -567,7 +567,8 @@ class Dumpstate { RunStatus dumpstate(); void MaybeTakeEarlyScreenshot(); - void MaybeSnapshotSystemTrace(); + std::future<std::string> MaybeSnapshotSystemTraceAsync(); + void MaybeWaitForSnapshotSystemTrace(std::future<std::string> task); void MaybeSnapshotUiTraces(); void MaybeAddUiTracesToZip(); diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 643b2c2d11..e9fb85ba60 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -1,11 +1,10 @@ set noparent -calin@google.com jsharkey@android.com maco@google.com mast@google.com +jiakaiz@google.com narayan@google.com ngeoffray@google.com rpl@google.com -toddke@google.com patb@google.com diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 4f92b3a374..6c450ab4e3 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -11,5 +11,5 @@ service servicemanager /system/bin/servicemanager onrestart class_restart --only-enabled main onrestart class_restart --only-enabled hal onrestart class_restart --only-enabled early_hal - task_profiles ServiceCapacityLow + task_profiles ProcessCapacityHigh shutdown critical diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 59b9495c42..750e170c24 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -283,6 +283,13 @@ public: */ status_t receiveMessage(InputMessage* msg); + /* Tells whether there is a message in the channel available to be received. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + /* Return a new object that has a duplicate of this channel's fd. */ std::unique_ptr<InputChannel> dup() const; @@ -518,6 +525,13 @@ public: */ int32_t getPendingBatchSource() const; + /* Returns true when there is *likely* a pending batch or a pending event in the channel. + * + * This is only a performance hint and may return false negative results. Clients should not + * rely on availability of the message based on the return value. + */ + bool probablyHasInput() const; + std::string dump() const; private: diff --git a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h index f178027b35..864ff50831 100644 --- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h +++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h @@ -31,7 +31,11 @@ namespace aidl::android::os { */ class PersistableBundle { public: - PersistableBundle() noexcept : mPBundle(APersistableBundle_new()) {} + PersistableBundle() noexcept { + if (__builtin_available(android __ANDROID_API_V__, *)) { + mPBundle = APersistableBundle_new(); + } + } // takes ownership of the APersistableBundle* PersistableBundle(APersistableBundle* _Nonnull bundle) noexcept : mPBundle(bundle) {} // takes ownership of the APersistableBundle* @@ -57,7 +61,7 @@ class PersistableBundle { if (__builtin_available(android __ANDROID_API_V__, *)) { return APersistableBundle_readFromParcel(parcel, &mPBundle); } else { - return STATUS_FAILED_TRANSACTION; + return STATUS_INVALID_OPERATION; } } @@ -68,7 +72,7 @@ class PersistableBundle { if (__builtin_available(android __ANDROID_API_V__, *)) { return APersistableBundle_writeToParcel(mPBundle, parcel); } else { - return STATUS_FAILED_TRANSACTION; + return STATUS_INVALID_OPERATION; } } @@ -327,20 +331,32 @@ class PersistableBundle { } bool getBooleanVector(const std::string& key, std::vector<bool>* _Nonnull vec) { - return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), - vec); + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal<bool>(&APersistableBundle_getBooleanVector, mPBundle, key.c_str(), + vec); + } + return false; } bool getIntVector(const std::string& key, std::vector<int32_t>* _Nonnull vec) { - return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), - vec); + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal<int32_t>(&APersistableBundle_getIntVector, mPBundle, key.c_str(), + vec); + } + return false; } bool getLongVector(const std::string& key, std::vector<int64_t>* _Nonnull vec) { - return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), - vec); + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal<int64_t>(&APersistableBundle_getLongVector, mPBundle, key.c_str(), + vec); + } + return false; } bool getDoubleVector(const std::string& key, std::vector<double>* _Nonnull vec) { - return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, key.c_str(), - vec); + if (__builtin_available(android __ANDROID_API_V__, *)) { + return getVecInternal<double>(&APersistableBundle_getDoubleVector, mPBundle, + key.c_str(), vec); + } + return false; } // Takes ownership of and frees the char** and its elements. @@ -361,15 +377,17 @@ class PersistableBundle { } bool getStringVector(const std::string& key, std::vector<std::string>* _Nonnull vec) { - int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, - &stringAllocator, nullptr); - if (bytes > 0) { - char** strings = (char**)malloc(bytes); - if (strings) { - bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, bytes, - &stringAllocator, nullptr); - *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes); - return true; + if (__builtin_available(android __ANDROID_API_V__, *)) { + int32_t bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), nullptr, 0, + &stringAllocator, nullptr); + if (bytes > 0) { + char** strings = (char**)malloc(bytes); + if (strings) { + bytes = APersistableBundle_getStringVector(mPBundle, key.c_str(), strings, + bytes, &stringAllocator, nullptr); + *vec = moveStringsInternal<std::vector<std::string>>(strings, bytes); + return true; + } } } return false; diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp index 73eb863706..ca3d5e6cf3 100644 --- a/libs/binder/ndk/stability.cpp +++ b/libs/binder/ndk/stability.cpp @@ -27,7 +27,7 @@ using ::android::internal::Stability; #error libbinder_ndk should only be built in a system context #endif -#ifdef __ANDROID_VENDOR__ +#if defined(__ANDROID_VENDOR__) && !defined(__TRUSTY__) #error libbinder_ndk should only be built in a system context #endif diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index 0ea4a3faaa..d7f631828a 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -49,6 +49,63 @@ public: } }; +static uint64_t warn_latency = std::numeric_limits<uint64_t>::max(); + +struct ProcResults { + vector<uint64_t> data; + + ProcResults(size_t capacity) { data.reserve(capacity); } + + void add_time(uint64_t time) { data.push_back(time); } + void combine_with(const ProcResults& append) { + data.insert(data.end(), append.data.begin(), append.data.end()); + } + uint64_t worst() { + return *max_element(data.begin(), data.end()); + } + void dump() { + if (data.size() == 0) { + // This avoids index-out-of-bounds below. + cout << "error: no data\n" << endl; + return; + } + + size_t num_long_transactions = 0; + for (uint64_t elem : data) { + if (elem > warn_latency) { + num_long_transactions += 1; + } + } + + if (num_long_transactions > 0) { + cout << (double)num_long_transactions / data.size() << "% of transactions took longer " + "than estimated max latency. Consider setting -m to be higher than " + << worst() / 1000 << " microseconds" << endl; + } + + sort(data.begin(), data.end()); + + uint64_t total_time = 0; + for (uint64_t elem : data) { + total_time += elem; + } + + double best = (double)data[0] / 1.0E6; + double worst = (double)data.back() / 1.0E6; + double average = (double)total_time / data.size() / 1.0E6; + cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl; + + double percentile_50 = data[(50 * data.size()) / 100] / 1.0E6; + double percentile_90 = data[(90 * data.size()) / 100] / 1.0E6; + double percentile_95 = data[(95 * data.size()) / 100] / 1.0E6; + double percentile_99 = data[(99 * data.size()) / 100] / 1.0E6; + cout << "50%: " << percentile_50 << " "; + cout << "90%: " << percentile_90 << " "; + cout << "95%: " << percentile_95 << " "; + cout << "99%: " << percentile_99 << endl; + } +}; + class Pipe { int m_readFd; int m_writeFd; @@ -79,13 +136,37 @@ public: int error = read(m_readFd, &val, sizeof(val)); ASSERT_TRUE(error >= 0); } - template <typename T> void send(const T& v) { - int error = write(m_writeFd, &v, sizeof(T)); + void send(const ProcResults& v) { + size_t num_elems = v.data.size(); + + int error = write(m_writeFd, &num_elems, sizeof(size_t)); ASSERT_TRUE(error >= 0); + + char* to_write = (char*)v.data.data(); + size_t num_bytes = sizeof(uint64_t) * num_elems; + + while (num_bytes > 0) { + int ret = write(m_writeFd, to_write, num_bytes); + ASSERT_TRUE(ret >= 0); + num_bytes -= ret; + to_write += ret; + } } - template <typename T> void recv(T& v) { - int error = read(m_readFd, &v, sizeof(T)); + void recv(ProcResults& v) { + size_t num_elems = 0; + int error = read(m_readFd, &num_elems, sizeof(size_t)); ASSERT_TRUE(error >= 0); + + v.data.resize(num_elems); + char* read_to = (char*)v.data.data(); + size_t num_bytes = sizeof(uint64_t) * num_elems; + + while (num_bytes > 0) { + int ret = read(m_readFd, read_to, num_bytes); + ASSERT_TRUE(ret >= 0); + num_bytes -= ret; + read_to += ret; + } } static tuple<Pipe, Pipe> createPipePair() { int a[2]; @@ -100,74 +181,6 @@ public: } }; -static const uint32_t num_buckets = 128; -static uint64_t max_time_bucket = 50ull * 1000000; -static uint64_t time_per_bucket = max_time_bucket / num_buckets; - -struct ProcResults { - uint64_t m_worst = 0; - uint32_t m_buckets[num_buckets] = {0}; - uint64_t m_transactions = 0; - uint64_t m_long_transactions = 0; - uint64_t m_total_time = 0; - uint64_t m_best = max_time_bucket; - - void add_time(uint64_t time) { - if (time > max_time_bucket) { - m_long_transactions++; - } - m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1; - m_best = min(time, m_best); - m_worst = max(time, m_worst); - m_transactions += 1; - m_total_time += time; - } - static ProcResults combine(const ProcResults& a, const ProcResults& b) { - ProcResults ret; - for (int i = 0; i < num_buckets; i++) { - ret.m_buckets[i] = a.m_buckets[i] + b.m_buckets[i]; - } - ret.m_worst = max(a.m_worst, b.m_worst); - ret.m_best = min(a.m_best, b.m_best); - ret.m_transactions = a.m_transactions + b.m_transactions; - ret.m_long_transactions = a.m_long_transactions + b.m_long_transactions; - ret.m_total_time = a.m_total_time + b.m_total_time; - return ret; - } - void dump() { - if (m_long_transactions > 0) { - cout << (double)m_long_transactions / m_transactions << "% of transactions took longer " - "than estimated max latency. Consider setting -m to be higher than " - << m_worst / 1000 << " microseconds" << endl; - } - - double best = (double)m_best / 1.0E6; - double worst = (double)m_worst / 1.0E6; - double average = (double)m_total_time / m_transactions / 1.0E6; - cout << "average:" << average << "ms worst:" << worst << "ms best:" << best << "ms" << endl; - - uint64_t cur_total = 0; - float time_per_bucket_ms = time_per_bucket / 1.0E6; - for (int i = 0; i < num_buckets; i++) { - float cur_time = time_per_bucket_ms * i + 0.5f * time_per_bucket_ms; - if ((cur_total < 0.5f * m_transactions) && (cur_total + m_buckets[i] >= 0.5f * m_transactions)) { - cout << "50%: " << cur_time << " "; - } - if ((cur_total < 0.9f * m_transactions) && (cur_total + m_buckets[i] >= 0.9f * m_transactions)) { - cout << "90%: " << cur_time << " "; - } - if ((cur_total < 0.95f * m_transactions) && (cur_total + m_buckets[i] >= 0.95f * m_transactions)) { - cout << "95%: " << cur_time << " "; - } - if ((cur_total < 0.99f * m_transactions) && (cur_total + m_buckets[i] >= 0.99f * m_transactions)) { - cout << "99%: " << cur_time << " "; - } - cur_total += m_buckets[i]; - } - cout << endl; - } -}; - String16 generateServiceName(int num) { char num_str[32]; @@ -204,14 +217,12 @@ void worker_fx(int num, for (int i = 0; i < server_count; i++) { if (num == i) continue; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - workers.push_back(serviceMgr->getService(generateServiceName(i))); -#pragma clang diagnostic pop + workers.push_back(serviceMgr->waitForService(generateServiceName(i))); } // Run the benchmark if client - ProcResults results; + ProcResults results(iterations); + chrono::time_point<chrono::high_resolution_clock> start, end; for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) { Parcel data, reply; @@ -305,11 +316,10 @@ void run_main(int iterations, // Collect all results from the workers. cout << "collecting results" << endl; signal_all(pipes); - ProcResults tot_results; + ProcResults tot_results(0), tmp_results(0); for (int i = 0; i < workers; i++) { - ProcResults tmp_results; pipes[i].recv(tmp_results); - tot_results = ProcResults::combine(tot_results, tmp_results); + tot_results.combine_with(tmp_results); } // Kill all the workers. @@ -323,13 +333,11 @@ void run_main(int iterations, } } if (training_round) { - // sets max_time_bucket to 2 * m_worst from the training round. - // Also needs to adjust time_per_bucket accordingly. - max_time_bucket = 2 * tot_results.m_worst; - time_per_bucket = max_time_bucket / num_buckets; - cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl; + // Sets warn_latency to 2 * worst from the training round. + warn_latency = 2 * tot_results.worst(); + cout << "Max latency during training: " << tot_results.worst() / 1.0E6 << "ms" << endl; } else { - tot_results.dump(); + tot_results.dump(); } } @@ -340,8 +348,7 @@ int main(int argc, char *argv[]) int payload_size = 0; bool cs_pair = false; bool training_round = false; - (void)argc; - (void)argv; + int max_time_us; // Parse arguments. for (int i = 1; i < argc; i++) { @@ -351,46 +358,65 @@ int main(int argc, char *argv[]) cout << "\t-m N : Specify expected max latency in microseconds." << endl; cout << "\t-p : Split workers into client/server pairs." << endl; cout << "\t-s N : Specify payload size." << endl; - cout << "\t-t N : Run training round." << endl; + cout << "\t-t : Run training round." << endl; cout << "\t-w N : Specify total number of workers." << endl; return 0; } if (string(argv[i]) == "-w") { + if (i + 1 == argc) { + cout << "-w requires an argument\n" << endl; + exit(EXIT_FAILURE); + } workers = atoi(argv[i+1]); i++; continue; } if (string(argv[i]) == "-i") { + if (i + 1 == argc) { + cout << "-i requires an argument\n" << endl; + exit(EXIT_FAILURE); + } iterations = atoi(argv[i+1]); i++; continue; } if (string(argv[i]) == "-s") { + if (i + 1 == argc) { + cout << "-s requires an argument\n" << endl; + exit(EXIT_FAILURE); + } payload_size = atoi(argv[i+1]); i++; + continue; } if (string(argv[i]) == "-p") { // client/server pairs instead of spreading // requests to all workers. If true, half // the workers become clients and half servers cs_pair = true; + continue; } if (string(argv[i]) == "-t") { // Run one training round before actually collecting data // to get an approximation of max latency. training_round = true; + continue; } if (string(argv[i]) == "-m") { + if (i + 1 == argc) { + cout << "-m requires an argument\n" << endl; + exit(EXIT_FAILURE); + } // Caller specified the max latency in microseconds. // No need to run training round in this case. - if (atoi(argv[i+1]) > 0) { - max_time_bucket = strtoull(argv[i+1], (char **)nullptr, 10) * 1000; - time_per_bucket = max_time_bucket / num_buckets; - i++; - } else { + max_time_us = atoi(argv[i+1]); + if (max_time_us <= 0) { cout << "Max latency -m must be positive." << endl; exit(EXIT_FAILURE); } + warn_latency = max_time_us * 1000ull; + i++; + continue; } } diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk index 6de7eb5b3c..d343f14240 100644 --- a/libs/binder/trusty/rust/rules.mk +++ b/libs/binder/trusty/rust/rules.mk @@ -30,6 +30,9 @@ MODULE_LIBRARY_DEPS += \ external/rust/crates/downcast-rs \ trusty/user/base/lib/trusty-sys \ +MODULE_RUSTFLAGS += \ + --cfg 'android_vendor' \ + # Trusty does not have `ProcessState`, so there are a few # doc links in `IBinder` that are still broken. MODULE_RUSTFLAGS += \ diff --git a/libs/fakeservicemanager/FakeServiceManager.cpp b/libs/fakeservicemanager/FakeServiceManager.cpp index ae242f32a3..08f30de637 100644 --- a/libs/fakeservicemanager/FakeServiceManager.cpp +++ b/libs/fakeservicemanager/FakeServiceManager.cpp @@ -122,9 +122,19 @@ std::vector<IServiceManager::ServiceDebugInfo> FakeServiceManager::getServiceDeb } void FakeServiceManager::clear() { - std::lock_guard<std::mutex> l(mMutex); + std::map<String16, sp<IBinder>> backup; + + { + std::lock_guard<std::mutex> l(mMutex); + backup = mNameToService; + mNameToService.clear(); + } + + // destructors may access FSM, so avoid recursive lock + backup.clear(); // explicit - mNameToService.clear(); + // TODO: destructors may have added more services here - may want + // to check this or abort } } // namespace android @@ -147,4 +157,4 @@ void clearFakeServiceManager() { LOG_ALWAYS_FATAL_IF(gFakeServiceManager == nullptr, "Fake Service Manager is not available. Forgot to call setupFakeServiceManager?"); gFakeServiceManager->clear(); } -} //extern "C"
\ No newline at end of file +} //extern "C" diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 669f801764..598f949f8a 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -10,6 +10,7 @@ #include <fcntl.h> #include <inttypes.h> #include <math.h> +#include <poll.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> @@ -517,6 +518,22 @@ status_t InputChannel::receiveMessage(InputMessage* msg) { return OK; } +bool InputChannel::probablyHasInput() const { + struct pollfd pfds = {.fd = mFd, .events = POLLIN}; + if (::poll(&pfds, /*nfds=*/1, /*timeout=*/0) <= 0) { + // This can be a false negative because EAGAIN and ENOMEM are not handled. The latter should + // be extremely rare. The EAGAIN is also unlikely because it happens only when the signal + // arrives while the syscall is executed, and the syscall is quick. Hitting EAGAIN too often + // would be a sign of having too many signals, which is a bigger performance problem. A + // common tradition is to repeat the syscall on each EAGAIN, but it is not necessary here. + // In other words, the missing one liner is replaced by a multiline explanation. + return false; + } + // From poll(2): The bits returned in |revents| can include any of those specified in |events|, + // or one of the values POLLERR, POLLHUP, or POLLNVAL. + return (pfds.revents & POLLIN) != 0; +} + std::unique_ptr<InputChannel> InputChannel::dup() const { base::unique_fd newFd(dupFd()); return InputChannel::create(getName(), std::move(newFd), getConnectionToken()); @@ -1406,6 +1423,10 @@ int32_t InputConsumer::getPendingBatchSource() const { return head.body.motion.source; } +bool InputConsumer::probablyHasInput() const { + return hasPendingBatch() || mChannel->probablyHasInput(); +} + ssize_t InputConsumer::findBatch(int32_t deviceId, int32_t source) const { for (size_t i = 0; i < mBatches.size(); i++) { const Batch& batch = mBatches[i]; diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp index 0661261003..650c93034c 100644 --- a/libs/input/tests/InputChannel_test.cpp +++ b/libs/input/tests/InputChannel_test.cpp @@ -81,8 +81,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should have suffixed name"; // Server->Client communication - InputMessage serverMsg; - memset(&serverMsg, 0, sizeof(InputMessage)); + InputMessage serverMsg = {}; serverMsg.header.type = InputMessage::Type::KEY; serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; EXPECT_EQ(OK, serverChannel->sendMessage(&serverMsg)) @@ -97,8 +96,7 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "client channel should receive the correct message from server channel"; // Client->Server communication - InputMessage clientReply; - memset(&clientReply, 0, sizeof(InputMessage)); + InputMessage clientReply = {}; clientReply.header.type = InputMessage::Type::FINISHED; clientReply.header.seq = 0x11223344; clientReply.body.finished.handled = true; @@ -116,6 +114,48 @@ TEST_F(InputChannelTest, OpenInputChannelPair_ReturnsAPairOfConnectedChannels) { << "server channel should receive the correct message from client channel"; } +TEST_F(InputChannelTest, ProbablyHasInput) { + std::unique_ptr<InputChannel> senderChannel, receiverChannel; + + // Open a pair of channels. + status_t result = + InputChannel::openInputChannelPair("channel name", senderChannel, receiverChannel); + ASSERT_EQ(OK, result) << "should have successfully opened a channel pair"; + + ASSERT_FALSE(receiverChannel->probablyHasInput()); + + // Send one message. + InputMessage serverMsg = {}; + serverMsg.header.type = InputMessage::Type::KEY; + serverMsg.body.key.action = AKEY_EVENT_ACTION_DOWN; + EXPECT_EQ(OK, senderChannel->sendMessage(&serverMsg)) + << "server channel should be able to send message to client channel"; + + // Verify input is available. + bool hasInput = false; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = receiverChannel->probablyHasInput(); + } while (!hasInput); + EXPECT_TRUE(hasInput) + << "client channel should observe that message is available before receiving it"; + + // Receive (consume) the message. + InputMessage clientMsg; + EXPECT_EQ(OK, receiverChannel->receiveMessage(&clientMsg)) + << "client channel should be able to receive message from server channel"; + EXPECT_EQ(serverMsg.header.type, clientMsg.header.type) + << "client channel should receive the correct message from server channel"; + EXPECT_EQ(serverMsg.body.key.action, clientMsg.body.key.action) + << "client channel should receive the correct message from server channel"; + + // Verify input is not available. + EXPECT_FALSE(receiverChannel->probablyHasInput()) + << "client should not observe any more messages after receiving the single one"; +} + TEST_F(InputChannelTest, ReceiveSignal_WhenNoSignalPresent_ReturnsAnError) { std::unique_ptr<InputChannel> serverChannel, clientChannel; diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 06b841be0d..2000335521 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -40,6 +40,182 @@ struct Pointer { bool isResampled = false; }; +// A collection of arguments to be sent as publishMotionEvent(). The saved members of this struct +// allow to check the expectations against the event acquired from the InputReceiver. To help +// simplify expectation checking it carries members not present in MotionEvent, like |rawXScale|. +struct PublishMotionArgs { + const int32_t action; + const nsecs_t downTime; + const uint32_t seq; + const int32_t eventId; + const int32_t deviceId = 1; + const uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; + const int32_t displayId = ADISPLAY_ID_DEFAULT; + const int32_t actionButton = 0; + const int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; + const int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; + const int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; + const MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; + const float xScale = 2; + const float yScale = 3; + const float xOffset = -10; + const float yOffset = -20; + const float rawXScale = 4; + const float rawYScale = -5; + const float rawXOffset = -11; + const float rawYOffset = 42; + const float xPrecision = 0.25; + const float yPrecision = 0.5; + const float xCursorPosition = 1.3; + const float yCursorPosition = 50.6; + std::array<uint8_t, 32> hmac; + int32_t flags; + ui::Transform transform; + ui::Transform rawTransform; + const nsecs_t eventTime; + size_t pointerCount; + std::vector<PointerProperties> pointerProperties; + std::vector<PointerCoords> pointerCoords; + + PublishMotionArgs(int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers, + const uint32_t seq); +}; + +PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, + const std::vector<Pointer>& pointers, const uint32_t inSeq) + : action(inAction), + downTime(inDownTime), + seq(inSeq), + eventId(InputEvent::nextId()), + eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) { + hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; + + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + if (action == AMOTION_EVENT_ACTION_CANCEL) { + flags |= AMOTION_EVENT_FLAG_CANCELED; + } + pointerCount = pointers.size(); + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties.push_back({}); + pointerProperties[i].clear(); + pointerProperties[i].id = pointers[i].id; + pointerProperties[i].toolType = ToolType::FINGER; + + pointerCoords.push_back({}); + pointerCoords[i].clear(); + pointerCoords[i].isResampled = pointers[i].isResampled; + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); + } + transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); + rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); +} + +// Checks expectations against |motionEvent| acquired from an InputConsumer. Floating point +// comparisons limit precision to EPSILON. +void verifyArgsEqualToEvent(const PublishMotionArgs& args, const MotionEvent& motionEvent) { + EXPECT_EQ(args.eventId, motionEvent.getId()); + EXPECT_EQ(args.deviceId, motionEvent.getDeviceId()); + EXPECT_EQ(args.source, motionEvent.getSource()); + EXPECT_EQ(args.displayId, motionEvent.getDisplayId()); + EXPECT_EQ(args.hmac, motionEvent.getHmac()); + EXPECT_EQ(args.action, motionEvent.getAction()); + EXPECT_EQ(args.downTime, motionEvent.getDownTime()); + EXPECT_EQ(args.flags, motionEvent.getFlags()); + EXPECT_EQ(args.edgeFlags, motionEvent.getEdgeFlags()); + EXPECT_EQ(args.metaState, motionEvent.getMetaState()); + EXPECT_EQ(args.buttonState, motionEvent.getButtonState()); + EXPECT_EQ(args.classification, motionEvent.getClassification()); + EXPECT_EQ(args.transform, motionEvent.getTransform()); + EXPECT_EQ(args.xOffset, motionEvent.getXOffset()); + EXPECT_EQ(args.yOffset, motionEvent.getYOffset()); + EXPECT_EQ(args.xPrecision, motionEvent.getXPrecision()); + EXPECT_EQ(args.yPrecision, motionEvent.getYPrecision()); + EXPECT_NEAR(args.xCursorPosition, motionEvent.getRawXCursorPosition(), EPSILON); + EXPECT_NEAR(args.yCursorPosition, motionEvent.getRawYCursorPosition(), EPSILON); + EXPECT_NEAR(args.xCursorPosition * args.xScale + args.xOffset, motionEvent.getXCursorPosition(), + EPSILON); + EXPECT_NEAR(args.yCursorPosition * args.yScale + args.yOffset, motionEvent.getYCursorPosition(), + EPSILON); + EXPECT_EQ(args.rawTransform, motionEvent.getRawTransform()); + EXPECT_EQ(args.eventTime, motionEvent.getEventTime()); + EXPECT_EQ(args.pointerCount, motionEvent.getPointerCount()); + EXPECT_EQ(0U, motionEvent.getHistorySize()); + + for (size_t i = 0; i < args.pointerCount; i++) { + SCOPED_TRACE(i); + EXPECT_EQ(args.pointerProperties[i].id, motionEvent.getPointerId(i)); + EXPECT_EQ(args.pointerProperties[i].toolType, motionEvent.getToolType(i)); + + const auto& pc = args.pointerCoords[i]; + EXPECT_EQ(pc, motionEvent.getSamplePointerCoords()[i]); + + EXPECT_NEAR(pc.getX() * args.rawXScale + args.rawXOffset, motionEvent.getRawX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.rawYScale + args.rawYOffset, motionEvent.getRawY(i), EPSILON); + EXPECT_NEAR(pc.getX() * args.xScale + args.xOffset, motionEvent.getX(i), EPSILON); + EXPECT_NEAR(pc.getY() * args.yScale + args.yOffset, motionEvent.getY(i), EPSILON); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent.getPressure(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent.getSize(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent.getTouchMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent.getTouchMinor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent.getToolMajor(i)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent.getToolMinor(i)); + + // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is + // "up", and the positive y direction is "down". + const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); + const float x = sinf(unscaledOrientation) * args.xScale; + const float y = -cosf(unscaledOrientation) * args.yScale; + EXPECT_EQ(atan2f(x, -y), motionEvent.getOrientation(i)); + } +} + +void publishMotionEvent(InputPublisher& publisher, const PublishMotionArgs& a) { + status_t status = + publisher.publishMotionEvent(a.seq, a.eventId, a.deviceId, a.source, a.displayId, + a.hmac, a.action, a.actionButton, a.flags, a.edgeFlags, + a.metaState, a.buttonState, a.classification, a.transform, + a.xPrecision, a.yPrecision, a.xCursorPosition, + a.yCursorPosition, a.rawTransform, a.downTime, a.eventTime, + a.pointerCount, a.pointerProperties.data(), + a.pointerCoords.data()); + ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; +} + +void sendAndVerifyFinishedSignal(InputConsumer& consumer, InputPublisher& publisher, uint32_t seq, + nsecs_t publishTime) { + status_t status = consumer.sendFinishedSignal(seq, false); + ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; + Result<InputPublisher::ConsumerResponse> result = publisher.receiveConsumerResponse(); + ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; + ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); + const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); + ASSERT_EQ(seq, finish.seq) + << "receiveConsumerResponse should have returned the original sequence number"; + ASSERT_FALSE(finish.handled) + << "receiveConsumerResponse should have set handled to consumer's reply"; + ASSERT_GE(finish.consumeTime, publishTime) + << "finished signal's consume time should be greater than publish time"; +} + +void waitUntilInputAvailable(const InputConsumer& inputConsumer) { + bool hasInput; + do { + // The probablyHasInput() can return false positive under rare circumstances uncontrollable + // by the tests. Re-request the availability in this case. Returning |false| for a long + // time is not intended, and would cause a test timeout. + hasInput = inputConsumer.probablyHasInput(); + } while (!hasInput); +} + } // namespace class InputPublisherAndConsumerTest : public testing::Test { @@ -63,6 +239,8 @@ protected: void publishAndConsumeKeyEvent(); void publishAndConsumeMotionStream(); + void publishAndConsumeMotionDown(nsecs_t downTime); + void publishAndConsumeBatchedMotionMove(nsecs_t downTime); void publishAndConsumeFocusEvent(); void publishAndConsumeCaptureEvent(); void publishAndConsumeDragEvent(); @@ -73,16 +251,6 @@ protected: private: // The sequence number to use when publishing the next event uint32_t mSeq = 1; - - void publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, - int32_t metaState, int32_t buttonState, MotionClassification classification, - float xScale, float yScale, float xOffset, float yOffset, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, float rawXScale, - float rawYScale, float rawXOffset, float rawYOffset, nsecs_t downTime, - nsecs_t eventTime, const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords); }; TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) { @@ -121,11 +289,14 @@ void InputPublisherAndConsumerTest::publishAndConsumeKeyEvent() { ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK"; + waitUntilInputAvailable(*mConsumer); uint32_t consumeSeq; InputEvent* event; status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); ASSERT_EQ(OK, status) << "consumer consume should return OK"; + EXPECT_FALSE(mConsumer->probablyHasInput()) + << "no events should be waiting after being consumed"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; @@ -185,176 +356,51 @@ void InputPublisherAndConsumerTest::publishAndConsumeMotionStream() { Pointer{.id = 2, .x = 300, .y = 400}}); } -void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { - constexpr int32_t deviceId = 1; - constexpr uint32_t source = AINPUT_SOURCE_TOUCHSCREEN; - constexpr int32_t displayId = ADISPLAY_ID_DEFAULT; - constexpr std::array<uint8_t, 32> hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - constexpr int32_t actionButton = 0; - int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; - - if (action == AMOTION_EVENT_ACTION_CANCEL) { - flags |= AMOTION_EVENT_FLAG_CANCELED; - } - const size_t pointerCount = pointers.size(); - constexpr int32_t edgeFlags = AMOTION_EVENT_EDGE_FLAG_TOP; - constexpr int32_t metaState = AMETA_ALT_LEFT_ON | AMETA_ALT_ON; - constexpr int32_t buttonState = AMOTION_EVENT_BUTTON_PRIMARY; - constexpr MotionClassification classification = MotionClassification::AMBIGUOUS_GESTURE; - constexpr float xScale = 2; - constexpr float yScale = 3; - constexpr float xOffset = -10; - constexpr float yOffset = -20; - constexpr float rawXScale = 4; - constexpr float rawYScale = -5; - constexpr float rawXOffset = -11; - constexpr float rawYOffset = 42; - constexpr float xPrecision = 0.25; - constexpr float yPrecision = 0.5; - constexpr float xCursorPosition = 1.3; - constexpr float yCursorPosition = 50.6; - - const nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); - std::vector<PointerProperties> pointerProperties; - std::vector<PointerCoords> pointerCoords; - for (size_t i = 0; i < pointerCount; i++) { - pointerProperties.push_back({}); - pointerProperties[i].clear(); - pointerProperties[i].id = pointers[i].id; - pointerProperties[i].toolType = ToolType::FINGER; +void InputPublisherAndConsumerTest::publishAndConsumeMotionDown(nsecs_t downTime) { + publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime, + {Pointer{.id = 0, .x = 20, .y = 30}}); +} - pointerCoords.push_back({}); - pointerCoords[i].clear(); - pointerCoords[i].isResampled = pointers[i].isResampled; - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, pointers[i].x); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, pointers[i].y); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_SIZE, 0.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, 1.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR, 1.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.5 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, 2.7 * i); - pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, 3.5 * i); - } +void InputPublisherAndConsumerTest::publishAndConsumeBatchedMotionMove(nsecs_t downTime) { + uint32_t seq = mSeq++; + const std::vector<Pointer> pointers = {Pointer{.id = 0, .x = 20, .y = 30}}; + PublishMotionArgs args(AMOTION_EVENT_ACTION_MOVE, downTime, pointers, seq); + const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); - publishAndConsumeMotionEvent(deviceId, source, displayId, hmac, action, actionButton, flags, - edgeFlags, metaState, buttonState, classification, xScale, yScale, - xOffset, yOffset, xPrecision, yPrecision, xCursorPosition, - yCursorPosition, rawXScale, rawYScale, rawXOffset, rawYOffset, - downTime, eventTime, pointerProperties, pointerCoords); + // Consume leaving a batch behind. + uint32_t consumeSeq; + InputEvent* event; + status_t status = mConsumer->consume(&mEventFactory, + /*consumeBatches=*/false, -1, &consumeSeq, &event); + ASSERT_EQ(WOULD_BLOCK, status) + << "consumer consume should return WOULD_BLOCK when a new batch is started"; + ASSERT_TRUE(mConsumer->hasPendingBatch()) << "consume should have created a batch"; + EXPECT_TRUE(mConsumer->probablyHasInput()) + << "should deterministically have input because there is a batch"; + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeMotionEvent( - int32_t deviceId, uint32_t source, int32_t displayId, std::array<uint8_t, 32> hmac, - int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, - int32_t buttonState, MotionClassification classification, float xScale, float yScale, - float xOffset, float yOffset, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, float rawXScale, float rawYScale, float rawXOffset, float rawYOffset, - nsecs_t downTime, nsecs_t eventTime, - const std::vector<PointerProperties>& pointerProperties, - const std::vector<PointerCoords>& pointerCoords) { - const uint32_t seq = mSeq++; - const int32_t eventId = InputEvent::nextId(); - ui::Transform transform; - transform.set({xScale, 0, xOffset, 0, yScale, yOffset, 0, 0, 1}); - ui::Transform rawTransform; - rawTransform.set({rawXScale, 0, rawXOffset, 0, rawYScale, rawYOffset, 0, 0, 1}); - - status_t status; - ASSERT_EQ(pointerProperties.size(), pointerCoords.size()); - const size_t pointerCount = pointerProperties.size(); - const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, - actionButton, flags, edgeFlags, metaState, buttonState, - classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, rawTransform, - downTime, eventTime, pointerCount, - pointerProperties.data(), pointerCoords.data()); - ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; + int32_t action, nsecs_t downTime, const std::vector<Pointer>& pointers) { + uint32_t seq = mSeq++; + PublishMotionArgs args(action, downTime, pointers, seq); + nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); + publishMotionEvent(*mPublisher, args); uint32_t consumeSeq; InputEvent* event; - status = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); - ASSERT_EQ(OK, status) - << "consumer consume should return OK"; - + status_t status = + mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq, &event); + ASSERT_EQ(OK, status) << "consumer consume should return OK"; ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event"; ASSERT_EQ(InputEventType::MOTION, event->getType()) << "consumer should have returned a motion event"; - - MotionEvent* motionEvent = static_cast<MotionEvent*>(event); EXPECT_EQ(seq, consumeSeq); - EXPECT_EQ(eventId, motionEvent->getId()); - EXPECT_EQ(deviceId, motionEvent->getDeviceId()); - EXPECT_EQ(source, motionEvent->getSource()); - EXPECT_EQ(displayId, motionEvent->getDisplayId()); - EXPECT_EQ(hmac, motionEvent->getHmac()); - EXPECT_EQ(action, motionEvent->getAction()); - EXPECT_EQ(flags, motionEvent->getFlags()); - EXPECT_EQ(edgeFlags, motionEvent->getEdgeFlags()); - EXPECT_EQ(metaState, motionEvent->getMetaState()); - EXPECT_EQ(buttonState, motionEvent->getButtonState()); - EXPECT_EQ(classification, motionEvent->getClassification()); - EXPECT_EQ(transform, motionEvent->getTransform()); - EXPECT_EQ(xOffset, motionEvent->getXOffset()); - EXPECT_EQ(yOffset, motionEvent->getYOffset()); - EXPECT_EQ(xPrecision, motionEvent->getXPrecision()); - EXPECT_EQ(yPrecision, motionEvent->getYPrecision()); - EXPECT_NEAR(xCursorPosition, motionEvent->getRawXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition, motionEvent->getRawYCursorPosition(), EPSILON); - EXPECT_NEAR(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition(), EPSILON); - EXPECT_NEAR(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition(), EPSILON); - EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); - EXPECT_EQ(downTime, motionEvent->getDownTime()); - EXPECT_EQ(eventTime, motionEvent->getEventTime()); - EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); - EXPECT_EQ(0U, motionEvent->getHistorySize()); - for (size_t i = 0; i < pointerCount; i++) { - SCOPED_TRACE(i); - EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); - EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - - const auto& pc = pointerCoords[i]; - EXPECT_EQ(pc, motionEvent->getSamplePointerCoords()[i]); - - EXPECT_NEAR(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i), EPSILON); - EXPECT_NEAR(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i), EPSILON); - EXPECT_NEAR(pc.getX() * xScale + xOffset, motionEvent->getX(i), EPSILON); - EXPECT_NEAR(pc.getY() * yScale + yOffset, motionEvent->getY(i), EPSILON); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), motionEvent->getPressure(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_SIZE), motionEvent->getSize(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), motionEvent->getTouchMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), motionEvent->getTouchMinor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), motionEvent->getToolMajor(i)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), motionEvent->getToolMinor(i)); - - // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is - // "up", and the positive y direction is "down". - const float unscaledOrientation = pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - const float x = sinf(unscaledOrientation) * xScale; - const float y = -cosf(unscaledOrientation) * yScale; - EXPECT_EQ(atan2f(x, -y), motionEvent->getOrientation(i)); - } - - status = mConsumer->sendFinishedSignal(seq, false); - ASSERT_EQ(OK, status) - << "consumer sendFinishedSignal should return OK"; - - Result<InputPublisher::ConsumerResponse> result = mPublisher->receiveConsumerResponse(); - ASSERT_TRUE(result.ok()) << "receiveConsumerResponse should return OK"; - ASSERT_TRUE(std::holds_alternative<InputPublisher::Finished>(*result)); - const InputPublisher::Finished& finish = std::get<InputPublisher::Finished>(*result); - ASSERT_EQ(seq, finish.seq) - << "receiveConsumerResponse should have returned the original sequence number"; - ASSERT_FALSE(finish.handled) - << "receiveConsumerResponse should have set handled to consumer's reply"; - ASSERT_GE(finish.consumeTime, publishTime) - << "finished signal's consume time should be greater than publish time"; + verifyArgsEqualToEvent(args, static_cast<const MotionEvent&>(*event)); + sendAndVerifyFinishedSignal(*mConsumer, *mPublisher, seq, publishTime); } void InputPublisherAndConsumerTest::publishAndConsumeFocusEvent() { @@ -546,6 +592,15 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionStream()); } +TEST_F(InputPublisherAndConsumerTest, PublishMotionMoveEvent_EndToEnd) { + // Publish a DOWN event before MOVE to pass the InputVerifier checks. + const nsecs_t downTime = systemTime(SYSTEM_TIME_MONOTONIC); + ASSERT_NO_FATAL_FAILURE(publishAndConsumeMotionDown(downTime)); + + // Publish the MOVE event and check expectations. + ASSERT_NO_FATAL_FAILURE(publishAndConsumeBatchedMotionMove(downTime)); +} + TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) { ASSERT_NO_FATAL_FAILURE(publishAndConsumeFocusEvent()); } diff --git a/libs/nativewindow/include/android/native_window_aidl.h b/libs/nativewindow/include/android/native_window_aidl.h index 68ac7e0b34..e496c453f2 100644 --- a/libs/nativewindow/include/android/native_window_aidl.h +++ b/libs/nativewindow/include/android/native_window_aidl.h @@ -106,7 +106,7 @@ public: if (__builtin_available(android __ANDROID_API_U__, *)) { return ANativeWindow_readFromParcel(parcel, &mWindow); } else { - return STATUS_FAILED_TRANSACTION; + return STATUS_INVALID_OPERATION; } } @@ -117,7 +117,7 @@ public: if (__builtin_available(android __ANDROID_API_U__, *)) { return ANativeWindow_writeToParcel(mWindow, parcel); } else { - return STATUS_FAILED_TRANSACTION; + return STATUS_INVALID_OPERATION; } } diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h index a98ea86073..969a5cff05 100644 --- a/libs/nativewindow/include/system/window.h +++ b/libs/nativewindow/include/system/window.h @@ -1095,10 +1095,19 @@ enum { ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL = 3, /** + * Indicates that, as a result of a user interaction, an animation is likely to start. + * This category is a signal that a user interaction heuristic determined the need of a + * high refresh rate, and is not an explicit request from the app. + * As opposed to FRAME_RATE_CATEGORY_HIGH, this vote may be ignored in favor of + * more explicit votes. + */ + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT = 4, + + /** * Indicates a frame rate suitable for animations that require a high frame rate, which may * increase smoothness but may also increase power usage. */ - ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 4 + ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH = 5 }; /* diff --git a/libs/nativewindow/libnativewindow.map.txt b/libs/nativewindow/libnativewindow.map.txt index a185a59fca..8bc1292986 100644 --- a/libs/nativewindow/libnativewindow.map.txt +++ b/libs/nativewindow/libnativewindow.map.txt @@ -2,11 +2,11 @@ LIBNATIVEWINDOW { global: AHardwareBuffer_acquire; AHardwareBuffer_allocate; - AHardwareBuffer_allocate2; # llndk # systemapi - AHardwareBuffer_createFromHandle; # llndk # systemapi + AHardwareBuffer_allocate2; # llndk systemapi + AHardwareBuffer_createFromHandle; # llndk systemapi AHardwareBuffer_describe; AHardwareBuffer_getId; # introduced=31 - AHardwareBuffer_getNativeHandle; # llndk # systemapi + AHardwareBuffer_getNativeHandle; # llndk systemapi AHardwareBuffer_isSupported; # introduced=29 AHardwareBuffer_lock; AHardwareBuffer_lockAndGetInfo; # introduced=29 @@ -17,8 +17,8 @@ LIBNATIVEWINDOW { AHardwareBuffer_unlock; AHardwareBuffer_readFromParcel; # introduced=34 AHardwareBuffer_writeToParcel; # introduced=34 - AHardwareBuffer_getDataSpace; # llndk # systemapi - AHardwareBuffer_setDataSpace; # llndk # systemapi + AHardwareBuffer_getDataSpace; # llndk systemapi + AHardwareBuffer_setDataSpace; # llndk systemapi ANativeWindowBuffer_getHardwareBuffer; # llndk ANativeWindow_OemStorageGet; # llndk ANativeWindow_OemStorageSet; # llndk @@ -29,18 +29,18 @@ LIBNATIVEWINDOW { ANativeWindow_getBuffersDefaultDataSpace; # introduced=34 ANativeWindow_getFormat; ANativeWindow_getHeight; - ANativeWindow_getLastDequeueDuration; # systemapi # introduced=30 - ANativeWindow_getLastDequeueStartTime; # systemapi # introduced=30 - ANativeWindow_getLastQueueDuration; # systemapi # introduced=30 + ANativeWindow_getLastDequeueDuration; # systemapi introduced=30 + ANativeWindow_getLastDequeueStartTime; # systemapi introduced=30 + ANativeWindow_getLastQueueDuration; # systemapi introduced=30 ANativeWindow_getWidth; ANativeWindow_lock; ANativeWindow_query; # llndk ANativeWindow_queryf; # llndk ANativeWindow_queueBuffer; # llndk - ANativeWindow_setCancelBufferInterceptor; # systemapi # introduced=30 - ANativeWindow_setDequeueBufferInterceptor; # systemapi # introduced=30 - ANativeWindow_setPerformInterceptor; # systemapi # introduced=30 - ANativeWindow_setQueueBufferInterceptor; # systemapi # introduced=30 + ANativeWindow_setCancelBufferInterceptor; # systemapi introduced=30 + ANativeWindow_setDequeueBufferInterceptor; # systemapi introduced=30 + ANativeWindow_setPerformInterceptor; # systemapi introduced=30 + ANativeWindow_setQueueBufferInterceptor; # systemapi introduced=30 ANativeWindow_release; ANativeWindow_setAutoPrerotation; # llndk ANativeWindow_setAutoRefresh; # llndk @@ -51,7 +51,7 @@ LIBNATIVEWINDOW { ANativeWindow_setBuffersGeometry; ANativeWindow_setBuffersTimestamp; # llndk ANativeWindow_setBuffersTransform; - ANativeWindow_setDequeueTimeout; # systemapi # introduced=30 + ANativeWindow_setDequeueTimeout; # systemapi introduced=30 ANativeWindow_setFrameRate; # introduced=30 ANativeWindow_setFrameRateWithChangeStrategy; # introduced=31 ANativeWindow_setSharedBufferMode; # llndk diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp index 43ebc69c46..72c6f1a73b 100644 --- a/services/inputflinger/InputFilter.cpp +++ b/services/inputflinger/InputFilter.cpp @@ -44,9 +44,11 @@ AidlKeyEvent notifyKeyArgsToKeyEvent(const NotifyKeyArgs& args) { return event; } -InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust) +InputFilter::InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust, + InputFilterPolicyInterface& policy) : mNextListener(listener), - mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener)) { + mCallbacks(ndk::SharedRefBase::make<InputFilterCallbacks>(listener, policy)), + mPolicy(policy) { LOG_ALWAYS_FATAL_IF(!rust.createInputFilter(mCallbacks, &mInputFilterRust).isOk()); LOG_ALWAYS_FATAL_IF(!mInputFilterRust); } @@ -122,6 +124,10 @@ void InputFilter::setAccessibilityStickyKeysEnabled(bool enabled) { if (mConfig.stickyKeysEnabled != enabled) { mConfig.stickyKeysEnabled = enabled; notifyConfigurationChangedLocked(); + if (!enabled) { + // When Sticky keys is disabled, send callback to clear any saved sticky state. + mPolicy.notifyStickyModifierStateChanged(0, 0); + } } } diff --git a/services/inputflinger/InputFilter.h b/services/inputflinger/InputFilter.h index a38fbf6f61..153d29dd53 100644 --- a/services/inputflinger/InputFilter.h +++ b/services/inputflinger/InputFilter.h @@ -19,6 +19,7 @@ #include <aidl/com/android/server/inputflinger/IInputFlingerRust.h> #include <utils/Mutex.h> #include "InputFilterCallbacks.h" +#include "InputFilterPolicyInterface.h" #include "InputListener.h" #include "NotifyArgs.h" @@ -47,7 +48,8 @@ public: aidl::com::android::server::inputflinger::InputFilterConfiguration; using AidlDeviceInfo = aidl::com::android::server::inputflinger::DeviceInfo; - explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust&); + explicit InputFilter(InputListenerInterface& listener, IInputFlingerRust& rust, + InputFilterPolicyInterface& policy); ~InputFilter() override = default; void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; @@ -65,6 +67,7 @@ public: private: InputListenerInterface& mNextListener; std::shared_ptr<InputFilterCallbacks> mCallbacks; + InputFilterPolicyInterface& mPolicy; std::shared_ptr<IInputFilter> mInputFilterRust; // Keep track of connected peripherals, so that if filters are enabled later, we can pass that // info to the filters diff --git a/services/inputflinger/InputFilterCallbacks.cpp b/services/inputflinger/InputFilterCallbacks.cpp index 8c8f5e8f71..a8759b7cbd 100644 --- a/services/inputflinger/InputFilterCallbacks.cpp +++ b/services/inputflinger/InputFilterCallbacks.cpp @@ -29,8 +29,9 @@ NotifyKeyArgs keyEventToNotifyKeyArgs(const AidlKeyEvent& event) { event.scanCode, event.metaState, event.downTime); } -InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener) - : mNextListener(listener) {} +InputFilterCallbacks::InputFilterCallbacks(InputListenerInterface& listener, + InputFilterPolicyInterface& policy) + : mNextListener(listener), mPolicy(policy) {} ndk::ScopedAStatus InputFilterCallbacks::sendKeyEvent(const AidlKeyEvent& event) { mNextListener.notifyKey(keyEventToNotifyKeyArgs(event)); @@ -42,6 +43,7 @@ ndk::ScopedAStatus InputFilterCallbacks::onModifierStateChanged(int32_t modifier std::scoped_lock _l(mLock); mStickyModifierState.modifierState = modifierState; mStickyModifierState.lockedModifierState = lockedModifierState; + mPolicy.notifyStickyModifierStateChanged(modifierState, lockedModifierState); ALOGI("Sticky keys modifier state changed: modifierState=%d, lockedModifierState=%d", modifierState, lockedModifierState); return ndk::ScopedAStatus::ok(); diff --git a/services/inputflinger/InputFilterCallbacks.h b/services/inputflinger/InputFilterCallbacks.h index c0a80fb6dc..31c160aeb9 100644 --- a/services/inputflinger/InputFilterCallbacks.h +++ b/services/inputflinger/InputFilterCallbacks.h @@ -20,6 +20,7 @@ #include <android/binder_auto_utils.h> #include <utils/Mutex.h> #include <mutex> +#include "InputFilterPolicyInterface.h" #include "InputListener.h" #include "NotifyArgs.h" @@ -33,7 +34,8 @@ using AidlKeyEvent = aidl::com::android::server::inputflinger::KeyEvent; class InputFilterCallbacks : public IInputFilter::BnInputFilterCallbacks { public: - explicit InputFilterCallbacks(InputListenerInterface& listener); + explicit InputFilterCallbacks(InputListenerInterface& listener, + InputFilterPolicyInterface& policy); ~InputFilterCallbacks() override = default; uint32_t getModifierState(); @@ -41,6 +43,7 @@ public: private: InputListenerInterface& mNextListener; + InputFilterPolicyInterface& mPolicy; mutable std::mutex mLock; struct StickyModifierState { uint32_t modifierState; diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 296f2449ea..4863513eb5 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -127,7 +127,8 @@ std::shared_ptr<IInputFlingerRust> createInputFlingerRust() { */ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy, - PointerChoreographerPolicyInterface& choreographerPolicy) { + PointerChoreographerPolicyInterface& choreographerPolicy, + InputFilterPolicyInterface& inputFilterPolicy) { mInputFlingerRust = createInputFlingerRust(); mDispatcher = createInputDispatcher(dispatcherPolicy); @@ -135,7 +136,8 @@ InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, std::make_unique<TracedInputListener>("InputDispatcher", *mDispatcher)); if (ENABLE_INPUT_FILTER_RUST) { - mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust); + mInputFilter = std::make_unique<InputFilter>(*mTracingStages.back(), *mInputFlingerRust, + inputFilterPolicy); mTracingStages.emplace_back( std::make_unique<TracedInputListener>("InputFilter", *mInputFilter)); } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index fa7db379e0..df944ef807 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -29,6 +29,7 @@ #include <InputDispatcherInterface.h> #include <InputDispatcherPolicyInterface.h> +#include <InputFilterPolicyInterface.h> #include <PointerChoreographerPolicyInterface.h> #include <input/Input.h> #include <input/InputTransport.h> @@ -119,7 +120,8 @@ protected: public: InputManager(const sp<InputReaderPolicyInterface>& readerPolicy, InputDispatcherPolicyInterface& dispatcherPolicy, - PointerChoreographerPolicyInterface& choreographerPolicy); + PointerChoreographerPolicyInterface& choreographerPolicy, + InputFilterPolicyInterface& inputFilterPolicy); status_t start() override; status_t stop() override; diff --git a/services/inputflinger/PointerChoreographer.cpp b/services/inputflinger/PointerChoreographer.cpp index 0be4c327b7..4571ef4481 100644 --- a/services/inputflinger/PointerChoreographer.cpp +++ b/services/inputflinger/PointerChoreographer.cpp @@ -222,6 +222,7 @@ void PointerChoreographer::processStylusHoverEventLocked(const NotifyMotionArgs& pc.setPosition(x, y); if (args.action == AMOTION_EVENT_ACTION_HOVER_EXIT) { pc.fade(PointerControllerInterface::Transition::IMMEDIATE); + pc.updatePointerIcon(PointerIconStyle::TYPE_NOT_SPECIFIED); } else { pc.unfade(PointerControllerInterface::Transition::IMMEDIATE); } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index ab815ea3ed..162a7bd6c5 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -835,7 +835,7 @@ void InputDispatcher::dispatchOnce() { // Run a dispatch loop if there are no pending commands. // The dispatch loop might enqueue commands to run afterwards. if (!haveCommandsLocked()) { - dispatchOnceInnerLocked(&nextWakeupTime); + dispatchOnceInnerLocked(/*byref*/ nextWakeupTime); } // Run all pending commands if there are any. @@ -942,7 +942,7 @@ std::chrono::nanoseconds InputDispatcher::getDispatchingTimeoutLocked( return DEFAULT_INPUT_DISPATCHING_TIMEOUT; } -void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { nsecs_t currentTime = now(); // Reset the key repeat timer whenever normal dispatch is suspended while the @@ -966,9 +966,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { bool isAppSwitchDue; if (!REMOVE_APP_SWITCH_DROPS) { isAppSwitchDue = mAppSwitchDueTime <= currentTime; - if (mAppSwitchDueTime < *nextWakeupTime) { - *nextWakeupTime = mAppSwitchDueTime; - } + nextWakeupTime = std::min(nextWakeupTime, mAppSwitchDueTime); } // Ready to start a new event. @@ -989,9 +987,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { if (currentTime >= mKeyRepeatState.nextRepeatTime) { mPendingEvent = synthesizeKeyRepeatLocked(currentTime); } else { - if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { - *nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } + nextWakeupTime = std::min(nextWakeupTime, mKeyRepeatState.nextRepeatTime); } } @@ -1157,7 +1153,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { mLastDropReason = dropReason; releasePendingEventLocked(); - *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately + nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately } } @@ -1749,7 +1745,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked } bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { // Preprocessing. if (!entry->dispatchInProgress) { if (entry->repeatCount == 0 && entry->action == AKEY_EVENT_ACTION_DOWN && @@ -1800,9 +1796,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con // Handle case where the policy asked us to try again later last time. if (entry->interceptKeyResult == KeyEntry::InterceptKeyResult::TRY_AGAIN_LATER) { if (currentTime < entry->interceptKeyWakeupTime) { - if (entry->interceptKeyWakeupTime < *nextWakeupTime) { - *nextWakeupTime = entry->interceptKeyWakeupTime; - } + nextWakeupTime = std::min(nextWakeupTime, entry->interceptKeyWakeupTime); return false; // wait until next wakeup } entry->interceptKeyResult = KeyEntry::InterceptKeyResult::UNKNOWN; @@ -1882,7 +1876,7 @@ void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry& void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " "source=0x%x, sensorType=%s", @@ -1922,7 +1916,7 @@ bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) { + DropReason* dropReason, nsecs_t& nextWakeupTime) { ATRACE_CALL(); // Preprocessing. if (!entry->dispatchInProgress) { @@ -2167,7 +2161,7 @@ bool InputDispatcher::shouldWaitToSendKeyLocked(nsecs_t currentTime, } sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, InputEventInjectionResult& outInjectionResult) { outInjectionResult = InputEventInjectionResult::FAILED; // Default result @@ -2206,7 +2200,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( ALOGW("Waiting because no window has focus but %s may eventually add a " "window when it finishes starting up. Will wait for %" PRId64 "ms", mAwaitedFocusedApplication->getName().c_str(), millis(timeout)); - *nextWakeupTime = *mNoFocusedWindowTimeoutTime; + nextWakeupTime = std::min(nextWakeupTime, *mNoFocusedWindowTimeoutTime); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } else if (currentTime > *mNoFocusedWindowTimeoutTime) { @@ -2251,7 +2245,7 @@ sp<WindowInfoHandle> InputDispatcher::findFocusedWindowTargetLocked( // prior input events. if (entry.type == EventEntry::Type::KEY) { if (shouldWaitToSendKeyLocked(currentTime, focusedWindowHandle->getName().c_str())) { - *nextWakeupTime = *mKeyIsWaitingForEventsTimeout; + nextWakeupTime = std::min(nextWakeupTime, *mKeyIsWaitingForEventsTimeout); outInjectionResult = InputEventInjectionResult::PENDING; return nullptr; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 010dbb28e4..3567288f1d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -209,7 +209,7 @@ private: // This method should only be called on the input dispatcher's own thread. void dispatchOnce(); - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); + void dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. bool enqueueInboundEventLocked(std::unique_ptr<EventEntry> entry) REQUIRES(mLock); @@ -435,9 +435,9 @@ private: bool dispatchDeviceResetLocked(nsecs_t currentTime, const DeviceResetEntry& entry) REQUIRES(mLock); bool dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<const KeyEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<const MotionEntry> entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<const FocusEntry> entry) REQUIRES(mLock); void dispatchPointerCaptureChangedLocked( @@ -449,7 +449,7 @@ private: void dispatchEventLocked(nsecs_t currentTime, std::shared_ptr<const EventEntry> entry, const std::vector<InputTarget>& inputTargets) REQUIRES(mLock); void dispatchSensorLocked(nsecs_t currentTime, const std::shared_ptr<const SensorEntry>& entry, - DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); + DropReason* dropReason, nsecs_t& nextWakeupTime) REQUIRES(mLock); void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<const DragEntry> entry) REQUIRES(mLock); void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry); @@ -521,7 +521,7 @@ private: int32_t getTargetDisplayId(const EventEntry& entry); sp<android::gui::WindowInfoHandle> findFocusedWindowTargetLocked( - nsecs_t currentTime, const EventEntry& entry, nsecs_t* nextWakeupTime, + nsecs_t currentTime, const EventEntry& entry, nsecs_t& nextWakeupTime, android::os::InputEventInjectionResult& outInjectionResult) REQUIRES(mLock); std::vector<InputTarget> findTouchedWindowTargetsLocked( nsecs_t currentTime, const MotionEntry& entry, diff --git a/services/inputflinger/include/InputFilterPolicyInterface.h b/services/inputflinger/include/InputFilterPolicyInterface.h new file mode 100644 index 0000000000..4d39b977d5 --- /dev/null +++ b/services/inputflinger/include/InputFilterPolicyInterface.h @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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 + +namespace android { + +/** + * The InputFilter policy interface. + * + * This is the interface that InputFilter uses to talk to Input Manager and other system components. + */ +class InputFilterPolicyInterface { +public: + virtual ~InputFilterPolicyInterface() = default; + + /** + * A callback to notify about sticky modifier state changes when Sticky keys feature is enabled. + * + * modifierState: Current sticky modifier state which will be sent with all subsequent + * KeyEvents. This only includes modifiers that can be 'Sticky' which includes: Meta, Ctrl, + * Shift, Alt and AltGr. + * + * lockedModifierState: Current locked modifier state representing modifiers that don't get + * cleared after non-modifier key press. This only includes modifiers that can be 'Sticky' which + * includes: Meta, Ctrl, Shift, Alt and AltGr. + * + * For more information {@see sticky_keys_filter.rs} + */ + virtual void notifyStickyModifierStateChanged(uint32_t modifierState, + uint32_t lockedModifierState) = 0; +}; + +} // namespace android diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 31dcb2e7a8..cbd719c8f4 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -280,7 +280,7 @@ private: class InputDeviceContext { public: InputDeviceContext(InputDevice& device, int32_t eventHubId); - ~InputDeviceContext(); + virtual ~InputDeviceContext(); inline InputReaderContext* getContext() { return mContext; } inline int32_t getId() { return mDeviceId; } @@ -450,7 +450,7 @@ public: inline std::optional<std::string> getDeviceTypeAssociation() const { return mDevice.getDeviceTypeAssociation(); } - inline std::optional<DisplayViewport> getAssociatedViewport() const { + virtual std::optional<DisplayViewport> getAssociatedViewport() const { return mDevice.getAssociatedViewport(); } [[nodiscard]] inline std::list<NotifyArgs> cancelTouch(nsecs_t when, nsecs_t readTime) { diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp index 255f02d278..6f697dbc31 100644 --- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp @@ -399,6 +399,8 @@ std::list<NotifyArgs> TouchpadInputMapper::reconfigure(nsecs_t when, : FloatRect{0, 0, 0, 0}; } mGestureConverter.setBoundsInLogicalDisplay(*boundsInLogicalDisplay); + + bumpGeneration(); } if (!changes.any() || changes.test(InputReaderConfiguration::Change::TOUCHPAD_SETTINGS)) { mPropertyProvider.getProperty("Use Custom Touchpad Pointer Accel Curve") diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp index 01e983a371..19788cee40 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp @@ -81,6 +81,7 @@ std::string GestureConverter::dump() const { out << StringPrintf("Button state: 0x%08x\n", mButtonState); out << "Down time: " << mDownTime << "\n"; out << "Current classification: " << ftl::enum_string(mCurrentClassification) << "\n"; + out << "Is hovering: " << mIsHovering << "\n"; out << "Enable Tap Timestamp: " << mWhenToEnableTapToClick << "\n"; return out.str(); } @@ -89,7 +90,7 @@ std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) { std::list<NotifyArgs> out; switch (mCurrentClassification) { case MotionClassification::TWO_FINGER_SWIPE: - out.push_back(endScroll(when, when)); + out += endScroll(when, when); break; case MotionClassification::MULTI_FINGER_SWIPE: out += handleMultiFingerSwipeLift(when, when); @@ -173,6 +174,8 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim const Gesture& gesture) { float deltaX = gesture.details.move.dx; float deltaY = gesture.details.move.dy; + const auto [oldXCursorPosition, oldYCursorPosition] = + mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (ENABLE_TOUCHPAD_PALM_REJECTION_V2) { bool wasHoverCancelled = mIsHoverCancelled; // Gesture will be cancelled if it started before the user started typing and @@ -184,6 +187,7 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim // This is the first event of the cancelled gesture, we won't return because we need to // generate a HOVER_EXIT event mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); + return exitHover(when, readTime, oldXCursorPosition, oldYCursorPosition); } else if (mIsHoverCancelled) { return {}; } @@ -204,24 +208,27 @@ std::list<NotifyArgs> GestureConverter::handleMove(nsecs_t when, nsecs_t readTim mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } - const auto [xCursorPosition, yCursorPosition] = + std::list<NotifyArgs> out; + const bool down = isPointerDown(mButtonState); + if (!down) { + out += enterHover(when, readTime, oldXCursorPosition, oldYCursorPosition); + } + const auto [newXCursorPosition, newYCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); PointerCoords coords; coords.clear(); - coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); - coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, newXCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, newYCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, deltaX); coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, deltaY); - const bool down = isPointerDown(mButtonState); coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, down ? 1.0f : 0.0f); - const int32_t action = mIsHoverCancelled - ? AMOTION_EVENT_ACTION_HOVER_EXIT - : (down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE); - return {makeMotionArgs(when, readTime, action, /* actionButton= */ 0, mButtonState, - /* pointerCount= */ 1, mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)}; + const int32_t action = down ? AMOTION_EVENT_ACTION_MOVE : AMOTION_EVENT_ACTION_HOVER_MOVE; + out.push_back(makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, newXCursorPosition, + newYCursorPosition)); + return out; } std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_t readTime, @@ -270,16 +277,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState |= actionButton; pressEvents.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_PRESS, actionButton, newButtonState, - /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); } } if (!isPointerDown(mButtonState) && isPointerDown(newButtonState)) { mDownTime = when; + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } out.splice(out.end(), pressEvents); @@ -295,20 +302,16 @@ std::list<NotifyArgs> GestureConverter::handleButtonsChange(nsecs_t when, nsecs_ newButtonState &= ~actionButton; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, actionButton, newButtonState, /* pointerCount= */ 1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + &coords, xCursorPosition, yCursorPosition)); } } if (isPointerDown(mButtonState) && !isPointerDown(newButtonState)) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - newButtonState, /* pointerCount= */ 1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); - // Send a HOVER_MOVE to tell the application that the mouse is hovering again. - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_HOVER_MOVE, - /*actionButton=*/0, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + newButtonState, /* pointerCount= */ 1, &coords, + xCursorPosition, yCursorPosition)); + mButtonState = newButtonState; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } mButtonState = newButtonState; return out; @@ -333,18 +336,18 @@ std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t if (mButtonState & button) { newButtonState &= ~button; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE, - button, newButtonState, /*pointerCount=*/1, - mFingerProps.data(), &coords, xCursorPosition, - yCursorPosition)); + button, newButtonState, /*pointerCount=*/1, &coords, + xCursorPosition, yCursorPosition)); } } + mButtonState = 0; if (pointerDown) { coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - newButtonState, /*pointerCount=*/1, mFingerProps.data(), - &coords, xCursorPosition, yCursorPosition)); + mButtonState, /*pointerCount=*/1, &coords, xCursorPosition, + yCursorPosition)); + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); } - mButtonState = 0; return out; } @@ -355,6 +358,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); if (mCurrentClassification != MotionClassification::TWO_FINGER_SWIPE) { + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::TWO_FINGER_SWIPE; coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); @@ -362,8 +367,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT mDownTime = when; NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); } @@ -378,8 +383,8 @@ std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readT coords.setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, -gesture.details.scroll.dy); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; out.push_back(args); return out; @@ -395,7 +400,7 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi // ensure consistency between touchscreen and touchpad flings), so we're just using // the "start fling" gestures as a marker for the end of a two-finger scroll // gesture. - return {endScroll(when, readTime)}; + return endScroll(when, readTime); } break; case GESTURES_FLING_TAP_DOWN: @@ -418,18 +423,21 @@ std::list<NotifyArgs> GestureConverter::handleFling(nsecs_t when, nsecs_t readTi return {}; } -NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { +std::list<NotifyArgs> GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { + std::list<NotifyArgs> out; const auto [xCursorPosition, yCursorPosition] = mEnablePointerChoreographer ? FloatPoint{0, 0} : mPointerController->getPosition(); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0); NotifyMotionArgs args = makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, - mButtonState, /* pointerCount= */ 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition); + mButtonState, /* pointerCount= */ 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition); args.flags |= AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE; + out.push_back(args); mCurrentClassification = MotionClassification::NONE; - return args; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); + return out; } [[nodiscard]] std::list<NotifyArgs> GestureConverter::handleMultiFingerSwipe(nsecs_t when, @@ -445,7 +453,11 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { // three and then put a fourth finger down), the gesture library will treat it as two // separate swipes with an appropriate lift event between them, so we don't have to worry // about the finger count changing mid-swipe. + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::MULTI_FINGER_SWIPE; + mSwipeFingerCount = fingerCount; constexpr float FAKE_FINGER_SPACING = 100; @@ -464,16 +476,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { fingerCount); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); for (size_t i = 1; i < mSwipeFingerCount; i++) { out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, - /* pointerCount= */ i + 1, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + /* pointerCount= */ i + 1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); } } float rotatedDeltaX = dx, rotatedDeltaY = -dy; @@ -491,8 +501,7 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET, yOffset); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0, mButtonState, /* pointerCount= */ mSwipeFingerCount, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -512,15 +521,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { AMOTION_EVENT_ACTION_POINTER_UP | ((i - 1) << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), /* actionButton= */ 0, mButtonState, /* pointerCount= */ i, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); } out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT, 0); mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); mSwipeFingerCount = 0; return out; } @@ -539,6 +547,10 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { LOG_ALWAYS_FATAL_IF(gesture.details.pinch.zoom_state != GESTURES_ZOOM_START, "First pinch gesture does not have the START zoom state (%d instead).", gesture.details.pinch.zoom_state); + std::list<NotifyArgs> out; + + out += exitHover(when, readTime, xCursorPosition, yCursorPosition); + mCurrentClassification = MotionClassification::PINCH; mPinchFingerSeparation = INITIAL_PINCH_SEPARATION_PX; mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0); @@ -551,17 +563,14 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f); mDownTime = when; - std::list<NotifyArgs> out; out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 1, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_POINTER_DOWN | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); + mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); return out; } @@ -579,8 +588,8 @@ NotifyMotionArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) { xCursorPosition + mPinchFingerSeparation / 2); mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, - mButtonState, /*pointerCount=*/2, mFingerProps.data(), - mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)}; + mButtonState, /*pointerCount=*/2, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)}; } std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) { @@ -593,20 +602,53 @@ std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) AMOTION_EVENT_ACTION_POINTER_UP | 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT, /*actionButton=*/0, mButtonState, /*pointerCount=*/2, - mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, - yCursorPosition)); - out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, - mButtonState, /*pointerCount=*/1, mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)); - mCurrentClassification = MotionClassification::NONE; + out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0, + mButtonState, /*pointerCount=*/1, mFakeFingerCoords.data(), + xCursorPosition, yCursorPosition)); mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0); + mCurrentClassification = MotionClassification::NONE; + out += enterHover(when, readTime, xCursorPosition, yCursorPosition); return out; } +std::list<NotifyArgs> GestureConverter::enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (!mIsHovering) { + mIsHovering = true; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_ENTER, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +std::list<NotifyArgs> GestureConverter::exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition) { + if (mIsHovering) { + mIsHovering = false; + return {makeHoverEvent(when, readTime, AMOTION_EVENT_ACTION_HOVER_EXIT, xCursorPosition, + yCursorPosition)}; + } else { + return {}; + } +} + +NotifyMotionArgs GestureConverter::makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition) { + PointerCoords coords; + coords.clear(); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0); + return makeMotionArgs(when, readTime, action, /*actionButton=*/0, mButtonState, + /*pointerCount=*/1, &coords, xCursorPosition, yCursorPosition); +} + NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, float xCursorPosition, float yCursorPosition) { return {mReaderContext.getNextId(), @@ -624,7 +666,7 @@ NotifyMotionArgs GestureConverter::makeMotionArgs(nsecs_t when, nsecs_t readTime mCurrentClassification, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, - pointerProperties, + mFingerProps.data(), pointerCoords, /* xPrecision= */ 1.0f, /* yPrecision= */ 1.0f, diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h index 88e7b99877..07cc56c618 100644 --- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h +++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h @@ -75,7 +75,7 @@ private: [[nodiscard]] std::list<NotifyArgs> handleFling(nsecs_t when, nsecs_t readTime, nsecs_t gestureStartTime, const Gesture& gesture); - [[nodiscard]] NotifyMotionArgs endScroll(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> endScroll(nsecs_t when, nsecs_t readTime); [[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime, uint32_t fingerCount, float dx, @@ -85,12 +85,18 @@ private: const Gesture& gesture); [[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime); + [[nodiscard]] std::list<NotifyArgs> enterHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + [[nodiscard]] std::list<NotifyArgs> exitHover(nsecs_t when, nsecs_t readTime, + float xCursorPosition, float yCursorPosition); + + NotifyMotionArgs makeHoverEvent(nsecs_t when, nsecs_t readTime, int32_t action, + float xCursorPosition, float yCursorPosition); + NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action, int32_t actionButton, int32_t buttonState, - uint32_t pointerCount, - const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xCursorPosition, - float yCursorPosition); + uint32_t pointerCount, const PointerCoords* pointerCoords, + float xCursorPosition, float yCursorPosition); void enableTapToClick(nsecs_t when); bool mIsHoverCancelled{false}; @@ -111,6 +117,9 @@ private: // button values (AMOTION_EVENT_BUTTON_...). uint32_t mButtonState = 0; nsecs_t mDownTime = 0; + // Whether we are currently in a hover state (i.e. a HOVER_ENTER event has been sent without a + // matching HOVER_EXIT). + bool mIsHovering = false; MotionClassification mCurrentClassification = MotionClassification::NONE; // Only used when mCurrentClassification is MULTI_FINGER_SWIPE. diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp index f3a6f01014..66c3256815 100644 --- a/services/inputflinger/tests/CursorInputMapper_test.cpp +++ b/services/inputflinger/tests/CursorInputMapper_test.cpp @@ -24,12 +24,14 @@ #include <android-base/logging.h> #include <com_android_input_flags.h> #include <gtest/gtest.h> +#include <input/DisplayViewport.h> #include <linux/input-event-codes.h> #include <linux/input.h> #include <utils/Timers.h> #include "FakePointerController.h" #include "InputMapperTest.h" +#include "InputReaderBase.h" #include "InterfaceMocks.h" #include "NotifyArgs.h" #include "TestEventMatchers.h" @@ -53,10 +55,76 @@ constexpr int32_t DISPLAY_ID = 0; constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; -constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified constexpr int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; +namespace { + +DisplayViewport createPrimaryViewport(ui::Rotation orientation) { + const bool isRotated = + orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270; + DisplayViewport v; + v.displayId = DISPLAY_ID; + v.orientation = orientation; + v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH; + v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT; + v.isActive = true; + v.uniqueId = "local:1"; + return v; +} + +DisplayViewport createSecondaryViewport() { + DisplayViewport v; + v.displayId = SECONDARY_DISPLAY_ID; + v.orientation = ui::Rotation::Rotation0; + v.logicalRight = DISPLAY_HEIGHT; + v.logicalBottom = DISPLAY_WIDTH; + v.physicalRight = DISPLAY_HEIGHT; + v.physicalBottom = DISPLAY_WIDTH; + v.deviceWidth = DISPLAY_HEIGHT; + v.deviceHeight = DISPLAY_WIDTH; + v.isActive = true; + v.uniqueId = "local:2"; + v.type = ViewportType::EXTERNAL; + return v; +} + +/** + * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper. + * + * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates + * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input + * device doesn't set its associated viewport when it's configured. + * + * TODO(b/319217713): work out a way to avoid this fake. + */ +class ViewportFakingInputDeviceContext : public InputDeviceContext { +public: + ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, + DisplayViewport viewport) + : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {} + + ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId, + ui::Rotation orientation) + : ViewportFakingInputDeviceContext(device, eventHubId, + createPrimaryViewport(orientation)) {} + + std::optional<DisplayViewport> getAssociatedViewport() const override { + return mAssociatedViewport; + } + + void setViewport(const DisplayViewport& viewport) { mAssociatedViewport = viewport; } + +private: + DisplayViewport mAssociatedViewport; +}; + +} // namespace + namespace input_flags = com::android::input::flags; /** @@ -84,9 +152,7 @@ protected: .WillRepeatedly(Return(false)); mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, - ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); } void createMapper() { @@ -108,6 +174,19 @@ protected: // Check that generation also got bumped ASSERT_GT(mDevice->getGeneration(), generation); } + + void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX, + int32_t rotatedY) { + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(ACTION_MOVE), + WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, + float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD))))); + } }; class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase { @@ -275,8 +354,7 @@ TEST_F(CursorInputMapperUnitTest, // When the bounds are set, then there should be a valid motion range. mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); std::list<NotifyArgs> args = mMapper->reconfigure(systemTime(), mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); @@ -490,6 +568,177 @@ TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) args.clear(); } +TEST_F(CursorInputMapperUnitTest, ProcessShouldNotRotateMotionsWhenOrientationAware) { + // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not + // need to be rotated. + mPropertyMap.addProperty("cursor.mode", "navigation"); + mPropertyMap.addProperty("cursor.orientationAware", "1"); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); +} + +TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) { + // Since InputReader works in the un-rotated coordinate space, only devices that are not + // orientation-aware are affected by display rotation. + mPropertyMap.addProperty("cursor.mode", "navigation"); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90)); + std::list<NotifyArgs> args = + mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180)); + args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1)); + + deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270)); + args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration, + InputReaderConfiguration::Change::DISPLAY_INFO); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1, 0)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, 1)); + ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1)); +} + +TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesOrientationChanges) { + mPropertyMap.addProperty("cursor.mode", "pointer"); + DisplayViewport viewport = createPrimaryViewport(ui::Rotation::Rotation90); + mFakePointerController->setDisplayViewport(viewport); + mReaderConfiguration.setDisplayViewports({viewport}); + createMapper(); + + // Verify that the coordinates are rotated. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithRelativeMotion(-20.0f, 10.0f))))); + + // Enable Pointer Capture. + setPointerCapture(true); + + // Move and verify rotation is not applied. + args = process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(ACTION_MOVE), + WithSource(AINPUT_SOURCE_MOUSE_RELATIVE), + WithCoords(10.0f, 20.0f))))); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. The + // InputDevice is not associated with any display. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createMapper(); + + // Ensure input events are generated for the secondary display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + // Ensure input events are generated for the secondary display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f))))); + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); +} + +TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdIgnoresEventsForMismatchedPointerDisplay) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the primary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(primaryViewport); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + // The mapper should not generate any events because it is associated with a display that is + // different from the pointer display. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, testing::IsEmpty()); +} + TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) { mPropertyMap.addProperty("cursor.mode", "pointer"); createMapper(); @@ -728,8 +977,7 @@ TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRang // 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); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); std::list<NotifyArgs> args = mMapper->reconfigure(systemTime(), mReaderConfiguration, InputReaderConfiguration::Change::DISPLAY_INFO); @@ -746,6 +994,54 @@ TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRang AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f)); } +TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the secondary display as the display on which the pointer should be shown. + // The InputDevice is not associated with any display. + mFakePointerController->setDisplayViewport(secondaryViewport); + mFakePointerController->setPosition(100, 200); + createDevice(); + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + std::list<NotifyArgs> args; + // Ensure input events are generated for the secondary display. + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f))))); +} + +TEST_F(CursorInputMapperUnitTestWithChoreographer, + ConfigureDisplayIdShouldGenerateEventForMismatchedPointerDisplay) { + DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90); + DisplayViewport secondaryViewport = createSecondaryViewport(); + mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport}); + // Set up the primary display as the display on which the pointer should be shown. + mFakePointerController->setDisplayViewport(primaryViewport); + createDevice(); + // Associate the InputDevice with the secondary display. + ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport); + mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration); + + // 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. + std::list<NotifyArgs> args; + args += process(ARBITRARY_TIME, EV_REL, REL_X, 10); + args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20); + args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE), + WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f))))); +} + TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) { mPropertyMap.addProperty("cursor.mode", "pointer"); createMapper(); @@ -959,14 +1255,11 @@ TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocit TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) { // Set up the default display. mFakePolicy->clearViewports(); - mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90, - /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL); + mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0)); // Set up the secondary display as the display on which the pointer should be shown. // The InputDevice is not associated with any display. - mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, /*isActive=*/true, "local:1", NO_PORT, - ViewportType::EXTERNAL); + mFakePolicy->addDisplayViewport(createSecondaryViewport()); mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID); createMapper(); diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp index 69772af6b3..163076974f 100644 --- a/services/inputflinger/tests/GestureConverter_test.cpp +++ b/services/inputflinger/tests/GestureConverter_test.cpp @@ -115,12 +115,32 @@ TEST_F(GestureConverterTest, Move) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + + // The same gesture again should only repeat the HOVER_MOVE and cursor position change, not the + // HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X - 10, POINTER_Y + 20), + WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 10, POINTER_Y + 20)); } TEST_F(GestureConverterTest, Move_Rotated) { @@ -134,10 +154,16 @@ TEST_F(GestureConverterTest, Move_Rotated) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X + 10, POINTER_Y + 5), WithRelativeMotion(10, 5), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(POINTER_X + 10, POINTER_Y + 5), + WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), + WithButtonState(0), WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X + 10, POINTER_Y + 5)); } @@ -153,8 +179,6 @@ TEST_F(GestureConverterTest, ButtonsChange) { /* up= */ GESTURES_BUTTON_NONE, /* is_tap= */ false); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); - ASSERT_EQ(3u, args.size()); - ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), @@ -210,12 +234,32 @@ TEST_F(GestureConverterTest, ButtonsChange) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } +TEST_F(GestureConverterTest, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(POINTER_X - 5, POINTER_Y + 10), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT)))); +} + TEST_F(GestureConverterTest, DragWithButton) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -275,7 +319,7 @@ TEST_F(GestureConverterTest, DragWithButton) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(POINTER_X - 5, POINTER_Y + 10), WithToolType(ToolType::FINGER), @@ -328,13 +372,20 @@ TEST_F(GestureConverterTest, Scroll) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_Rotated) { @@ -380,12 +431,19 @@ TEST_F(GestureConverterTest, Scroll_Rotated) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X - 15, POINTER_Y), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X - 15, POINTER_Y), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, Scroll_ClearsClassificationAfterGesture) { @@ -591,6 +649,12 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Vertical) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -677,6 +741,9 @@ TEST_F(GestureConverterTest, ThreeFingerSwipe_Rotated) { VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -809,6 +876,12 @@ TEST_F(GestureConverterTest, FourFingerSwipe_Horizontal) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -869,6 +942,12 @@ TEST_F(GestureConverterTest, Pinch_Inwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -929,6 +1008,12 @@ TEST_F(GestureConverterTest, Pinch_Outwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1013,6 +1098,11 @@ TEST_F(GestureConverterTest, ResetWithButtonPressed) { AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithCoords(POINTER_X, POINTER_Y), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1027,13 +1117,20 @@ TEST_F(GestureConverterTest, ResetDuringScroll) { std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), - WithCoords(POINTER_X, POINTER_Y - 10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(POINTER_X, POINTER_Y - 10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { @@ -1071,6 +1168,11 @@ TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1098,6 +1200,12 @@ TEST_F(GestureConverterTest, ResetDuringPinch) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(POINTER_X, POINTER_Y), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -1112,7 +1220,7 @@ TEST_F(GestureConverterTest, FlingTapDown) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); @@ -1131,13 +1239,7 @@ TEST_F(GestureConverterTest, Tap) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1146,6 +1248,12 @@ TEST_F(GestureConverterTest, Tap) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1175,7 +1283,7 @@ TEST_F(GestureConverterTest, Tap) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1192,13 +1300,7 @@ TEST_F(GestureConverterTest, Click) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1207,6 +1309,12 @@ TEST_F(GestureConverterTest, Click) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1243,7 +1351,7 @@ TEST_F(GestureConverterTest, Click) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1266,13 +1374,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabled, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -1301,13 +1403,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -1353,7 +1449,11 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(5u, args.size()); + ASSERT_EQ(6u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithRelativeMotion(0.f, 0.f), WithButtonState(0))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); @@ -1373,7 +1473,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, TapWithTapToClickDisabledWithDelay, WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0), WithButtonState(0))); } @@ -1390,13 +1490,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -1404,6 +1498,12 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, buttonDownGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(POINTER_X, POINTER_Y), + WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0.f, 0.f), @@ -1441,7 +1541,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, ClickWithTapToClickDisabled, WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(POINTER_X, POINTER_Y), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1463,14 +1563,7 @@ TEST_F_WITH_FLAGS(GestureConverterTest, MoveEnablesTapToClick, Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithCoords(POINTER_X - 5, POINTER_Y + 10), WithRelativeMotion(-5, 10), - WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))))); - - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(POINTER_X - 5, POINTER_Y + 10)); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -1489,7 +1582,9 @@ TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); // Key presses with IME connection should cancel ongoing move gesture nsecs_t currentTime = gestureStartTime + 100; @@ -1512,7 +1607,9 @@ TEST_F_WITH_FLAGS(GestureConverterTest, KeypressCancelsHoverMove, args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); } // TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging @@ -1535,6 +1632,21 @@ TEST_F(GestureConverterTestWithChoreographer, Move) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(-5, 10), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); + + // The same gesture again should only repeat the HOVER_MOVE, not the HOVER_ENTER. + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + ASSERT_THAT(args, + ElementsAre(VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -1552,10 +1664,16 @@ TEST_F(GestureConverterTestWithChoreographer, Move_Rotated) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(10, 5), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), WithRelativeMotion(0, 0), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + WithCoords(0, 0), WithRelativeMotion(10, 5), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { @@ -1621,12 +1739,32 @@ TEST_F(GestureConverterTestWithChoreographer, ButtonsChange) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } +TEST_F(GestureConverterTestWithChoreographer, ButtonDownAfterMoveExitsHover) { + InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); + GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); + converter.setDisplayId(ADISPLAY_ID_DEFAULT); + + Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); + std::list<NotifyArgs> args = + converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); + + Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, + /*down=*/GESTURES_BUTTON_LEFT, /*up=*/GESTURES_BUTTON_NONE, + /*is_tap=*/false); + args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, downGesture); + ASSERT_THAT(args.front(), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), WithButtonState(0), + WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT)))); +} + TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID); GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID); @@ -1679,7 +1817,7 @@ TEST_F(GestureConverterTestWithChoreographer, DragWithButton) { WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); @@ -1730,12 +1868,20 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, 0 - 15), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -15), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { @@ -1781,11 +1927,19 @@ TEST_F(GestureConverterTestWithChoreographer, Scroll_Rotated) { args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(-15, 0), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(-15, 0), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, Scroll_ClearsClassificationAfterGesture) { @@ -1990,6 +2144,12 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Vertical) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2076,6 +2236,9 @@ TEST_F(GestureConverterTestWithChoreographer, ThreeFingerSwipe_Rotated) { VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON), WithPointerCount(1u), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2208,6 +2371,12 @@ TEST_F(GestureConverterTestWithChoreographer, FourFingerSwipe_Horizontal) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2267,6 +2436,12 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Inwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2326,6 +2501,12 @@ TEST_F(GestureConverterTestWithChoreographer, Pinch_Outwards) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2409,6 +2590,11 @@ TEST_F(GestureConverterTestWithChoreographer, ResetWithButtonPressed) { AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0), WithCoords(0, 0), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithButtonState(0), WithCoords(0, 0), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2423,12 +2609,20 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringScroll) { std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithCoords(0, -10), - WithGestureScrollDistance(0, 0, EPSILON), - WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE), - WithToolType(ToolType::FINGER), - WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), + WithCoords(0, -10), + WithGestureScrollDistance(0, 0, EPSILON), + WithMotionClassification( + MotionClassification::TWO_FINGER_SWIPE), + WithToolType(ToolType::FINGER), + WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))))); } TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { @@ -2466,6 +2660,11 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringThreeFingerSwipe) { WithMotionClassification( MotionClassification::MULTI_FINGER_SWIPE), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2493,6 +2692,12 @@ TEST_F(GestureConverterTestWithChoreographer, ResetDuringPinch) { WithMotionClassification(MotionClassification::PINCH), WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u), WithToolType(ToolType::FINGER), + WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), + WithCoords(0, 0), + WithMotionClassification(MotionClassification::NONE), + WithToolType(ToolType::FINGER), WithDisplayId(ADISPLAY_ID_DEFAULT))))); } @@ -2507,7 +2712,7 @@ TEST_F(GestureConverterTestWithChoreographer, FlingTapDown) { converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, tapDownGesture); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); } @@ -2522,13 +2727,7 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2537,6 +2736,11 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2563,7 +2767,7 @@ TEST_F(GestureConverterTestWithChoreographer, Tap) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2580,12 +2784,7 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2594,6 +2793,11 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2628,7 +2832,7 @@ TEST_F(GestureConverterTestWithChoreographer, Click) { WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2651,13 +2855,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -2686,12 +2884,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(currentTime, currentTime, currentTime, flingGesture); - - ASSERT_EQ(1u, args.size()); - ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), - WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture tapGesture(kGestureButtonsChange, currentTime, currentTime, /* down= */ GESTURES_BUTTON_LEFT, @@ -2737,7 +2930,11 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl /* up= */ GESTURES_BUTTON_LEFT, /* is_tap= */ true); args = converter.handleGesture(currentTime, currentTime, currentTime, tapGesture); - ASSERT_EQ(5u, args.size()); + ASSERT_EQ(6u, args.size()); + ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithRelativeMotion(0.f, 0.f), WithButtonState(0))); + args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithRelativeMotion(0.f, 0.f), WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY))); @@ -2757,7 +2954,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, TapWithTapToClickDisabl WithButtonState(0))); args.pop_front(); ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()), - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithRelativeMotion(0, 0), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithRelativeMotion(0, 0), WithButtonState(0))); } @@ -2774,13 +2971,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa /* vy= */ 0, GESTURES_FLING_TAP_DOWN); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, flingGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the FlingTapDown test. Gesture buttonDownGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /* down= */ GESTURES_BUTTON_LEFT, @@ -2789,6 +2980,11 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_EXIT), + WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), + WithToolType(ToolType::FINGER), WithButtonState(0), + WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), + VariantWith<NotifyMotionArgs>( AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithCoords(0, 0), WithRelativeMotion(0.f, 0.f), WithToolType(ToolType::FINGER), @@ -2823,7 +3019,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, ClickWithTapToClickDisa WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), WithDisplayId(ADISPLAY_ID_DEFAULT))), VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), + AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER), WithCoords(0, 0), WithRelativeMotion(0, 0), WithToolType(ToolType::FINGER), WithButtonState(0), WithPressure(0.0f), @@ -2845,13 +3041,7 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, MoveEnablesTapToClick, Gesture moveGesture(kGestureMove, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, -5, 10); std::list<NotifyArgs> args = converter.handleGesture(ARBITRARY_TIME, READ_TIME, ARBITRARY_TIME, moveGesture); - - ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), WithCoords(0, 0), - WithRelativeMotion(-5, 10), WithToolType(ToolType::FINGER), - WithButtonState(0), WithPressure(0.0f), - WithDisplayId(ADISPLAY_ID_DEFAULT))))); + // We don't need to check args here, since it's covered by the Move test. // Future taps should be re-enabled ASSERT_FALSE(mReader->getContext()->isPreventingTouchpadTaps()); @@ -2870,7 +3060,9 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMov converter.handleGesture(gestureStartTime, READ_TIME, gestureStartTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); // Key presses with IME connection should cancel ongoing move gesture nsecs_t currentTime = gestureStartTime + 100; @@ -2893,7 +3085,9 @@ TEST_F_WITH_FLAGS(GestureConverterTestWithChoreographer, KeypressCancelsHoverMov args = converter.handleGesture(currentTime, READ_TIME, currentTime, moveGesture); ASSERT_THAT(args, ElementsAre(VariantWith<NotifyMotionArgs>( - WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_ENTER)), + VariantWith<NotifyMotionArgs>( + WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))); } } // namespace android diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index c92736ebf1..8be1b3ba00 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -1093,9 +1093,10 @@ public: FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle, const std::unique_ptr<InputDispatcher>& dispatcher, const std::string name, - int32_t displayId, std::optional<sp<IBinder>> token = std::nullopt) + int32_t displayId, bool createInputChannel = true) : mName(name) { - if (token == std::nullopt) { + sp<IBinder> token; + if (createInputChannel) { base::Result<std::unique_ptr<InputChannel>> channel = dispatcher->createInputChannel(name); token = (*channel)->getConnectionToken(); @@ -1105,7 +1106,7 @@ public: inputApplicationHandle->updateInfo(); mInfo.applicationInfo = *inputApplicationHandle->getInfo(); - mInfo.token = *token; + mInfo.token = token; mInfo.id = sId++; mInfo.name = name; mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT; @@ -2370,7 +2371,7 @@ TEST_F(InputDispatcherTest, HoverWhileWindowAppears) { sp<FakeWindowHandle> obscuringWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); obscuringWindow->setFrame(Rect(0, 0, 200, 200)); obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID); @@ -2419,7 +2420,7 @@ TEST_F(InputDispatcherTest, HoverMoveWhileWindowAppears) { sp<FakeWindowHandle> obscuringWindow = sp<FakeWindowHandle>::make(application, mDispatcher, "Obscuring window", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); obscuringWindow->setFrame(Rect(0, 0, 200, 200)); obscuringWindow->setTouchOcclusionMode(TouchOcclusionMode::BLOCK_UNTRUSTED); obscuringWindow->setOwnerInfo(SECONDARY_WINDOW_PID, SECONDARY_WINDOW_UID); @@ -7332,6 +7333,94 @@ TEST_F(InputDispatcherFocusOnTwoDisplaysTest, CancelTouch_MultiDisplay) { monitorInSecondary.assertNoEvents(); } +/** + * Send a key to the primary display and to the secondary display. + * Then cause the key on the primary display to be canceled by sending in a stale key. + * Ensure that the key on the primary display is canceled, and that the key on the secondary display + * does not get canceled. + */ +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, WhenDropKeyEvent_OnlyCancelCorrespondingKeyGesture) { + // Send a key down on primary display + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(ADISPLAY_ID_DEFAULT) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); + windowInPrimary->consumeKeyEvent( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT))); + windowInSecondary->assertNoEvents(); + + // Send a key down on second display + mDispatcher->notifyKey( + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .displayId(SECOND_DISPLAY_ID) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build()); + windowInSecondary->consumeKeyEvent( + AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN), WithDisplayId(SECOND_DISPLAY_ID))); + windowInPrimary->assertNoEvents(); + + // Send a valid key up event on primary display that will be dropped because it is stale + NotifyKeyArgs staleKeyUp = + KeyArgsBuilder(AKEY_EVENT_ACTION_UP, AINPUT_SOURCE_KEYBOARD) + .displayId(ADISPLAY_ID_DEFAULT) + .policyFlags(DEFAULT_POLICY_FLAGS | POLICY_FLAG_DISABLE_KEY_REPEAT) + .build(); + static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms; + mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT); + std::this_thread::sleep_for(STALE_EVENT_TIMEOUT); + mDispatcher->notifyKey(staleKeyUp); + + // Only the key gesture corresponding to the dropped event should receive the cancel event. + // Therefore, windowInPrimary should get the cancel event and windowInSecondary should not + // receive any events. + windowInPrimary->consumeKeyEvent(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP), + WithDisplayId(ADISPLAY_ID_DEFAULT), + WithFlags(AKEY_EVENT_FLAG_CANCELED))); + windowInSecondary->assertNoEvents(); +} + +/** + * Similar to 'WhenDropKeyEvent_OnlyCancelCorrespondingKeyGesture' but for motion events. + */ +TEST_F(InputDispatcherFocusOnTwoDisplaysTest, WhenDropMotionEvent_OnlyCancelCorrespondingGesture) { + // Send touch down on primary display. + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .displayId(ADISPLAY_ID_DEFAULT) + .build()); + windowInPrimary->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(ADISPLAY_ID_DEFAULT))); + windowInSecondary->assertNoEvents(); + + // Send touch down on second display. + mDispatcher->notifyMotion( + MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .displayId(SECOND_DISPLAY_ID) + .build()); + windowInPrimary->assertNoEvents(); + windowInSecondary->consumeMotionEvent( + AllOf(WithMotionAction(ACTION_DOWN), WithDisplayId(SECOND_DISPLAY_ID))); + + // inject a valid MotionEvent on primary display that will be stale when it arrives. + NotifyMotionArgs staleMotionUp = + MotionArgsBuilder(ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(100).y(200)) + .build(); + static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 10ms; + mFakePolicy->setStaleEventTimeout(STALE_EVENT_TIMEOUT); + std::this_thread::sleep_for(STALE_EVENT_TIMEOUT); + mDispatcher->notifyMotion(staleMotionUp); + + // For stale motion events, we let the gesture to complete. This behaviour is different from key + // events, where we would cancel the current keys instead. + windowInPrimary->consumeMotionEvent(WithMotionAction(ACTION_UP)); + windowInSecondary->assertNoEvents(); +} + class InputFilterTest : public InputDispatcherTest { protected: void testNotifyMotion(int32_t displayId, bool expectToBeFiltered, @@ -7654,8 +7743,7 @@ class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest { ADISPLAY_ID_DEFAULT); mWindow1->setFrame(Rect(0, 0, 100, 100)); - mWindow2 = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window 2", - ADISPLAY_ID_DEFAULT, mWindow1->getToken()); + mWindow2 = mWindow1->clone(ADISPLAY_ID_DEFAULT); mWindow2->setFrame(Rect(100, 100, 200, 200)); mDispatcher->onWindowInfosChanged({{*mWindow1->getInfo(), *mWindow2->getInfo()}, {}, 0, 0}); @@ -8922,7 +9010,7 @@ class InputDispatcherMultiWindowOcclusionTests : public InputDispatcherTest { mNoInputWindow = sp<FakeWindowHandle>::make(mApplication, mDispatcher, "Window without input channel", ADISPLAY_ID_DEFAULT, - /*token=*/std::make_optional<sp<IBinder>>(nullptr)); + /*createInputChannel=*/false); mNoInputWindow->setNoInputChannel(true); mNoInputWindow->setFrame(Rect(0, 0, 100, 100)); // It's perfectly valid for this window to not have an associated input channel @@ -8990,8 +9078,7 @@ protected: InputDispatcherTest::SetUp(); mApp = std::make_shared<FakeApplicationHandle>(); mWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT); - mMirror = sp<FakeWindowHandle>::make(mApp, mDispatcher, "TestWindowMirror", - ADISPLAY_ID_DEFAULT, mWindow->getToken()); + mMirror = mWindow->clone(ADISPLAY_ID_DEFAULT); mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp); mWindow->setFocusable(true); mMirror->setFocusable(true); diff --git a/services/inputflinger/tests/InputMapperTest.cpp b/services/inputflinger/tests/InputMapperTest.cpp index 5f43bd225b..2aecab98fd 100644 --- a/services/inputflinger/tests/InputMapperTest.cpp +++ b/services/inputflinger/tests/InputMapperTest.cpp @@ -21,8 +21,11 @@ #include <ui/Rotation.h> #include <utils/Timers.h> +#include "NotifyArgs.h" + namespace android { +using testing::_; using testing::Return; void InputMapperUnitTest::SetUpWithBus(int bus) { @@ -53,13 +56,14 @@ void InputMapperUnitTest::createDevice() { /*generation=*/2, mIdentifier); mDevice->addEmptyEventHubDevice(EVENTHUB_ID); mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID); - std::list<NotifyArgs> _ = + std::list<NotifyArgs> args = mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{}); + ASSERT_THAT(args, testing::ElementsAre(testing::VariantWith<NotifyDeviceResetArgs>(_))); } void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution) { - EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_)) + EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _)) .WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) { outAxisInfo->valid = valid; outAxisInfo->minValue = min; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 91aa0cabfb..870090a9db 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -4162,319 +4162,6 @@ TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) { ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags); } -// --- CursorInputMapperTestBase --- - -class CursorInputMapperTestBase : public InputMapperTest { -protected: - static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - - std::shared_ptr<FakePointerController> mFakePointerController; - - void SetUp() override { - InputMapperTest::SetUp(); - - mFakePointerController = std::make_shared<FakePointerController>(); - mFakePolicy->setPointerController(mFakePointerController); - } - - void testMotionRotation(CursorInputMapper& mapper, int32_t originalX, int32_t originalY, - int32_t rotatedX, int32_t rotatedY); - - void prepareDisplay(ui::Rotation orientation) { - setDisplayInfoAndReconfigure(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, orientation, - DISPLAY_UNIQUE_ID, NO_PORT, ViewportType::INTERNAL); - } - - void prepareSecondaryDisplay() { - setDisplayInfoAndReconfigure(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, - ui::ROTATION_0, SECONDARY_DISPLAY_UNIQUE_ID, NO_PORT, - ViewportType::EXTERNAL); - } - - static void assertCursorPointerCoords(const PointerCoords& coords, float x, float y, - float pressure) { - ASSERT_NO_FATAL_FAILURE(assertPointerCoords(coords, x, y, pressure, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.0f, 0.0f, EPSILON)); - } -}; - -const int32_t CursorInputMapperTestBase::TRACKBALL_MOVEMENT_THRESHOLD = 6; - -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); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, originalY); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_NO_FATAL_FAILURE( - assertCursorPointerCoords(args.pointerCoords[0], - float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD, - 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, Process_WhenOrientationAware_ShouldNotRotateMotions) { - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - addConfigurationProperty("cursor.mode", "navigation"); - // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not - // need to be rotated. - addConfigurationProperty("cursor.orientationAware", "1"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); -} - -TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) { - mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID); - addConfigurationProperty("cursor.mode", "navigation"); - // Since InputReader works in the un-rotated coordinate space, only devices that are not - // orientation-aware are affected by display rotation. - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - clearViewports(); - prepareDisplay(ui::ROTATION_0); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_90); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_180); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1)); - - clearViewports(); - prepareDisplay(ui::ROTATION_270); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1)); - ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1)); -} - -TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) { - addConfigurationProperty("cursor.mode", "pointer"); - CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>(); - - NotifyDeviceResetArgs resetArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs)); - ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime); - ASSERT_EQ(DEVICE_ID, resetArgs.deviceId); - - // Ensure the display is rotated. - prepareDisplay(ui::ROTATION_90); - - NotifyMotionArgs args; - - // Verify that the coordinates are rotated. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); - ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X)); - ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); - - // Enable Pointer Capture. - mFakePolicy->setPointerCapture(true); - configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE); - NotifyPointerCaptureChangedArgs captureArgs; - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs)); - ASSERT_TRUE(captureArgs.request.enable); - - // Move and verify rotation is not applied. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args)); - ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source); - ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); - ASSERT_EQ(10, args.pointerCoords[0].getX()); - ASSERT_EQ(20, args.pointerCoords[0].getY()); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) { - CursorInputMapper& mapper = 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 for the secondary display. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled( - AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE), - WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID), - WithCoords(110.0f, 220.0f)))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) { - 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); - - 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(110.0f, 220.0f)))); - ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f)); -} - -TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) { - 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); - - // The mapper should not generate any events because it is associated with a display that is - // different from the pointer display. - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20); - process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0); - ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled()); -} - -// --- CursorInputMapperTestWithChoreographer --- - -// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging -// logic can be removed. -class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase { -protected: - void SetUp() override { - input_flags::enable_pointer_choreographer(true); - CursorInputMapperTestBase::SetUp(); - } -}; - -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)))); -} - // --- TouchInputMapperTest --- class TouchInputMapperTest : public InputMapperTest { diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp index 193b84d313..1ac043c679 100644 --- a/services/inputflinger/tests/PointerChoreographer_test.cpp +++ b/services/inputflinger/tests/PointerChoreographer_test.cpp @@ -145,15 +145,18 @@ private: }; TEST_F(PointerChoreographerTest, ForwardsArgsToInnerListener) { - const std::vector<NotifyArgs> allArgs{NotifyInputDevicesChangedArgs{}, - NotifyConfigurationChangedArgs{}, - NotifyKeyArgs{}, - NotifyMotionArgs{}, - NotifySensorArgs{}, - NotifySwitchArgs{}, - NotifyDeviceResetArgs{}, - NotifyPointerCaptureChangedArgs{}, - NotifyVibratorStateArgs{}}; + const std::vector<NotifyArgs> + allArgs{NotifyInputDevicesChangedArgs{}, + NotifyConfigurationChangedArgs{}, + KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).build(), + MotionArgsBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN) + .pointer(FIRST_TOUCH_POINTER) + .build(), + NotifySensorArgs{}, + NotifySwitchArgs{}, + NotifyDeviceResetArgs{}, + NotifyPointerCaptureChangedArgs{}, + NotifyVibratorStateArgs{}}; for (auto notifyArgs : allArgs) { mChoreographer.notify(notifyArgs); @@ -1444,6 +1447,15 @@ TEST_F(PointerChoreographerTest, SetsPointerIconForStylus) { ASSERT_FALSE(mChoreographer.setPointerIcon(PointerIconStyle::TYPE_TEXT, DISPLAY_ID, SECOND_DEVICE_ID)); pc->assertPointerIconNotSet(); + + // The stylus stops hovering. This should cause the icon to be reset. + mChoreographer.notifyMotion( + MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_EXIT, AINPUT_SOURCE_STYLUS) + .pointer(STYLUS_POINTER) + .deviceId(DEVICE_ID) + .displayId(DISPLAY_ID) + .build()); + pc->assertPointerIconSet(PointerIconStyle::TYPE_NOT_SPECIFIED); } TEST_F(PointerChoreographerTest, SetsCustomPointerIconForStylus) { diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp index 91958e8177..4be1e8c54a 100644 --- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp +++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp @@ -37,6 +37,8 @@ constexpr auto ACTION_UP = AMOTION_EVENT_ACTION_UP; constexpr auto BUTTON_PRESS = AMOTION_EVENT_ACTION_BUTTON_PRESS; constexpr auto BUTTON_RELEASE = AMOTION_EVENT_ACTION_BUTTON_RELEASE; constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE; +constexpr auto HOVER_ENTER = AMOTION_EVENT_ACTION_HOVER_ENTER; +constexpr auto HOVER_EXIT = AMOTION_EVENT_ACTION_HOVER_EXIT; constexpr int32_t DISPLAY_ID = 0; constexpr int32_t DISPLAY_WIDTH = 480; constexpr int32_t DISPLAY_HEIGHT = 800; @@ -151,12 +153,14 @@ TEST_F(TouchpadInputMapperTest, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); @@ -218,12 +222,14 @@ TEST_F(TouchpadInputMapperTestWithChoreographer, HoverAndLeftButtonPress) { setScanCodeState(KeyState::UP, {BTN_LEFT}); args += process(EV_SYN, SYN_REPORT, 0); ASSERT_THAT(args, - ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)), + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_EXIT)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_DOWN)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_PRESS)), VariantWith<NotifyMotionArgs>(WithMotionAction(BUTTON_RELEASE)), VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_UP)), - VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_MOVE)))); + VariantWith<NotifyMotionArgs>(WithMotionAction(HOVER_ENTER)))); // Liftoff args.clear(); diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp index 0dd4dd6525..019fefaca4 100644 --- a/services/sensorservice/Android.bp +++ b/services/sensorservice/Android.bp @@ -15,7 +15,7 @@ aconfig_declarations { cc_aconfig_library { name: "sensorservice_flags_c_lib", - aconfig_declarations: "dynamic_sensors_flags", + aconfig_declarations: "sensorservice_flags", host_supported: true, } @@ -82,6 +82,7 @@ cc_library { "android.hardware.sensors@2.1", "android.hardware.common-V2-ndk", "android.hardware.common.fmq-V1-ndk", + "server_configurable_flags", ], static_libs: [ @@ -89,6 +90,7 @@ cc_library { "android.hardware.sensors@1.0-convert", "android.hardware.sensors-V1-convert", "android.hardware.sensors-V2-ndk", + "sensorservice_flags_c_lib", ], generated_headers: ["framework-cppstream-protos"], diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index dd83fdefc3..45fab7e592 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -25,6 +25,7 @@ #include <android-base/logging.h> #include <android/util/ProtoOutputStream.h> +#include <com_android_frameworks_sensorservice_flags.h> #include <cutils/atomic.h> #include <frameworks/base/core/proto/android/service/sensor_service.proto.h> #include <hardware/sensors-base.h> @@ -42,6 +43,7 @@ using namespace android::hardware::sensors; using android::util::ProtoOutputStream; +namespace sensorservice_flags = com::android::frameworks::sensorservice::flags; namespace android { // --------------------------------------------------------------------------- @@ -416,8 +418,15 @@ void SensorDevice::onDynamicSensorsConnected(const std::vector<sensor_t>& dynami } void SensorDevice::onDynamicSensorsDisconnected( - const std::vector<int32_t>& /* dynamicSensorHandlesRemoved */) { - // TODO: Currently dynamic sensors do not seem to be removed + const std::vector<int32_t>& dynamicSensorHandlesRemoved) { + if (sensorservice_flags::sensor_device_on_dynamic_sensor_disconnected()) { + for (auto handle : dynamicSensorHandlesRemoved) { + auto it = mConnectedDynamicSensors.find(handle); + if (it != mConnectedDynamicSensors.end()) { + mConnectedDynamicSensors.erase(it); + } + } + } } void SensorDevice::writeWakeLockHandled(uint32_t count) { @@ -483,12 +492,16 @@ status_t SensorDevice::activateLocked(void* ident, int handle, int enabled) { } else { ALOGD_IF(DEBUG_CONNECTIONS, "disable index=%zd", info.batchParams.indexOfKey(ident)); - // If a connected dynamic sensor is deactivated, remove it from the - // dictionary. + // TODO(b/316958439): Remove these line after + // sensor_device_on_dynamic_sensor_disconnected is ramped up. Bounded + // here since this function is coupled with + // dynamic_sensors_hal_disconnect_dynamic_sensor flag. If a connected + // dynamic sensor is deactivated, remove it from the dictionary. auto it = mConnectedDynamicSensors.find(handle); if (it != mConnectedDynamicSensors.end()) { - mConnectedDynamicSensors.erase(it); + mConnectedDynamicSensors.erase(it); } + // End of TODO(b/316958439) if (info.removeBatchParamsForIdent(ident) >= 0) { if (info.numActiveClients() == 0) { diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp index 97fca395b4..9c4f7a510b 100644 --- a/services/surfaceflinger/Scheduler/LayerInfo.cpp +++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp @@ -62,6 +62,10 @@ void LayerInfo::setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now, LayerUp mLastAnimationTime = std::max(lastPresentTime, now); break; case LayerUpdateType::SetFrameRate: + if (FlagManager::getInstance().vrr_config()) { + break; + } + FALLTHROUGH_INTENDED; case LayerUpdateType::Buffer: FrameTimeData frameTime = {.presentTime = lastPresentTime, .queueTime = mLastUpdatedTime, @@ -528,6 +532,8 @@ FrameRateCategory LayerInfo::FrameRate::convertCategory(int8_t category) { return FrameRateCategory::Low; case ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL: return FrameRateCategory::Normal; + case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH_HINT: + return FrameRateCategory::HighHint; case ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH: return FrameRateCategory::High; default: diff --git a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp index e06221a43d..c3709e5cc2 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp +++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp @@ -420,6 +420,11 @@ float RefreshRateSelector::calculateLayerScoreLocked(const LayerRequirement& lay const float seamlessness = isSeamlessSwitch ? 1.0f : kSeamedSwitchPenalty; if (layer.vote == LayerVoteType::ExplicitCategory) { + // HighHint is considered later for touch boost. + if (layer.frameRateCategory == FrameRateCategory::HighHint) { + return 0.f; + } + if (getFrameRateCategoryRange(layer.frameRateCategory).includes(refreshRate)) { return 1.f; } @@ -507,6 +512,7 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi int explicitExact = 0; int explicitGteLayers = 0; int explicitCategoryVoteLayers = 0; + int interactiveLayers = 0; int seamedFocusedLayers = 0; int categorySmoothSwitchOnlyLayers = 0; @@ -534,7 +540,13 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi explicitGteLayers++; break; case LayerVoteType::ExplicitCategory: - explicitCategoryVoteLayers++; + if (layer.frameRateCategory == FrameRateCategory::HighHint) { + // HighHint does not count as an explicit signal from an app. It may be + // be a touch signal. + interactiveLayers++; + } else { + explicitCategoryVoteLayers++; + } if (layer.frameRateCategory == FrameRateCategory::NoPreference) { // Count this layer for Min vote as well. The explicit vote avoids // touch boost and idle for choosing a category, while Min vote is for correct @@ -831,13 +843,14 @@ auto RefreshRateSelector::getRankedFrameRatesLocked(const std::vector<LayerRequi const auto touchRefreshRates = rankFrameRates(anchorGroup, RefreshRateOrder::Descending); using fps_approx_ops::operator<; - if (signals.touch && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 && + const bool hasInteraction = signals.touch || interactiveLayers > 0; + if (hasInteraction && explicitDefaultVoteLayers == 0 && explicitCategoryVoteLayers == 0 && touchBoostForExplicitExact && scores.front().frameRateMode.fps < touchRefreshRates.front().frameRateMode.fps) { ALOGV("Touch Boost"); ATRACE_FORMAT_INSTANT("%s (Touch Boost [late])", to_string(touchRefreshRates.front().frameRateMode.fps).c_str()); - return {touchRefreshRates, GlobalSignals{.touch = true}}; + return {touchRefreshRates, GlobalSignals{.touch = signals.touch}}; } // If we never scored any layers, and we don't favor high refresh rates, prefer to stay with the @@ -1512,6 +1525,7 @@ FpsRange RefreshRateSelector::getFrameRateCategoryRange(FrameRateCategory catego return FpsRange{60_Hz, 90_Hz}; case FrameRateCategory::Low: return FpsRange{30_Hz, 30_Hz}; + case FrameRateCategory::HighHint: case FrameRateCategory::NoPreference: case FrameRateCategory::Default: LOG_ALWAYS_FATAL("Should not get fps range for frame rate category: %s", diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h index 2806450c5f..84ef89fb4a 100644 --- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h +++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h @@ -88,6 +88,7 @@ enum class FrameRateCategory : int32_t { NoPreference, Low, Normal, + HighHint, High, ftl_last = High diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig index 42249acdc5..f097a13d0e 100644 --- a/services/surfaceflinger/surfaceflinger_flags.aconfig +++ b/services/surfaceflinger/surfaceflinger_flags.aconfig @@ -98,6 +98,7 @@ flag { namespace: "core_graphics" description: "Whether to use the closest known refresh rate to determine the fps consistency." bug: "299201319" + is_fixed_read_only: true } # This flag is broken. diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp index ba32c68b61..55b20b3b47 100644 --- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp @@ -650,7 +650,7 @@ TEST_F(LayerSnapshotTest, frameRateWithCategory) { // │ └── 13 // └── 2 setFrameRate(11, 244.f, 0, 0); - setFrameRateCategory(122, 3 /* Normal */); + setFrameRateCategory(122, ANATIVEWINDOW_FRAME_RATE_CATEGORY_NORMAL); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); // verify parent 1 gets no vote @@ -845,7 +845,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { // │ │ └── 1221 // │ └── 13 // └── 2 - setFrameRateCategory(12, 4 /* high */); + setFrameRateCategory(12, ANATIVEWINDOW_FRAME_RATE_CATEGORY_HIGH); setFrameRate(122, 123.f, 0, 0); setFrameRateSelectionStrategy(12, 1 /* OverrideChildren */); @@ -887,7 +887,7 @@ TEST_F(LayerSnapshotTest, frameRateSelectionStrategyWithCategory) { // │ │ └── 1221 // │ └── 13 // └── 2 - setFrameRateCategory(12, 0 /* default */); + setFrameRateCategory(12, ANATIVEWINDOW_FRAME_RATE_CATEGORY_DEFAULT); setFrameRateSelectionStrategy(12, 0 /* Default */); UPDATE_AND_VERIFY(mSnapshotBuilder, STARTING_ZORDER); // verify parent 1 gets no vote diff --git a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp index 0cacf810c0..1e526ba348 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp @@ -1607,6 +1607,82 @@ TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_60_12 } } +TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_HighHint) { + auto selector = createSelector(makeModes(kMode24, kMode30, kMode60, kMode120), kModeId60); + + std::vector<LayerRequirement> layers = {{.weight = 1.f}, {.weight = 1.f}}; + auto& lr1 = layers[0]; + auto& lr2 = layers[1]; + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::NoVote; + lr2.name = "NoVote"; + auto actualFrameRateMode = selector.getBestFrameRateMode(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitDefault; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitDefault"; + actualFrameRateMode = selector.getBestFrameRateMode(layers); + EXPECT_EQ(30_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::HighHint; + lr2.name = "ExplicitCategory HighHint#2"; + actualFrameRateMode = selector.getBestFrameRateMode(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitCategory; + lr2.frameRateCategory = FrameRateCategory::Low; + lr2.name = "ExplicitCategory Low"; + actualFrameRateMode = selector.getBestFrameRateMode(layers); + EXPECT_EQ(30_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitExactOrMultiple; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExactOrMultiple"; + actualFrameRateMode = selector.getBestFrameRateMode(layers); + // Gets touch boost + EXPECT_EQ(120_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + + lr1.vote = LayerVoteType::ExplicitCategory; + lr1.frameRateCategory = FrameRateCategory::HighHint; + lr1.name = "ExplicitCategory HighHint"; + lr2.vote = LayerVoteType::ExplicitExact; + lr2.desiredRefreshRate = 30_Hz; + lr2.name = "30Hz ExplicitExact"; + actualFrameRateMode = selector.getBestFrameRateMode(layers); + if (selector.supportsAppFrameRateOverrideByContent()) { + // Gets touch boost + EXPECT_EQ(120_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId120, actualFrameRateMode.modePtr->getId()); + } else { + EXPECT_EQ(30_Hz, actualFrameRateMode.fps); + EXPECT_EQ(kModeId30, actualFrameRateMode.modePtr->getId()); + } +} + TEST_P(RefreshRateSelectorTest, getBestFrameRateMode_withFrameRateCategory_smoothSwitchOnly_60_120_nonVrr) { if (GetParam() != Config::FrameRateOverride::Enabled) { |