Merge "Implement virtual stylus" into main
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 0ce7711..9695e07 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -95,7 +95,7 @@
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 b99f443..0bbd4a8 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -246,7 +246,7 @@
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 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 @@
// 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::PreDumpUiData() {
- MaybeSnapshotSystemTrace();
+ auto snapshot_system_trace = MaybeSnapshotSystemTraceAsync();
MaybeSnapshotUiTraces();
+ MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace));
}
/*
@@ -3264,13 +3265,15 @@
// 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 @@
}
MaybeTakeEarlyScreenshot();
+ MaybeWaitForSnapshotSystemTrace(std::move(snapshot_system_trace));
onUiIntensiveBugreportDumpsFinished(calling_uid);
MaybeCheckUserConsent(calling_uid, calling_package);
if (options_->telephony_only) {
@@ -3376,31 +3380,59 @@
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 c66fd1c..20b2865 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -567,7 +567,8 @@
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/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index dfbb886..e100eac 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -74,7 +74,7 @@
}
template <typename I>
-using shared_ptr = std::result_of_t<decltype(getService<I>)&(std::string)>;
+using shared_ptr = std::invoke_result_t<decltype(getService<I>)&, std::string>;
template <typename I>
class HalWrapper {
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 643b2c2..e9fb85b 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/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 805e8dc..d97ba89 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -16,83 +16,44 @@
#pragma once
-#include <condition_variable>
#include <chrono>
-#include <functional>
-#include <mutex>
-#include <thread>
+#include <future>
#include <hidl/Status.h>
+#include <utils/Errors.h>
namespace android {
namespace lshal {
-class BackgroundTaskState {
-public:
- explicit BackgroundTaskState(std::function<void(void)> &&func)
- : mFunc(std::forward<decltype(func)>(func)) {}
- void notify() {
- std::unique_lock<std::mutex> lock(mMutex);
- mFinished = true;
- lock.unlock();
- mCondVar.notify_all();
- }
- template<class C, class D>
- bool wait(std::chrono::time_point<C, D> end) {
- std::unique_lock<std::mutex> lock(mMutex);
- mCondVar.wait_until(lock, end, [this](){ return this->mFinished; });
- return mFinished;
- }
- void operator()() {
- mFunc();
- }
-private:
- std::mutex mMutex;
- std::condition_variable mCondVar;
- bool mFinished = false;
- std::function<void(void)> mFunc;
-};
-
-void *callAndNotify(void *data) {
- BackgroundTaskState &state = *static_cast<BackgroundTaskState *>(data);
- state();
- state.notify();
- return nullptr;
-}
-
-template<class R, class P>
-bool timeout(std::chrono::duration<R, P> delay, std::function<void(void)> &&func) {
- auto now = std::chrono::system_clock::now();
- BackgroundTaskState state{std::forward<decltype(func)>(func)};
- pthread_t thread;
- if (pthread_create(&thread, nullptr, callAndNotify, &state)) {
- std::cerr << "FATAL: could not create background thread." << std::endl;
- return false;
- }
- bool success = state.wait(now + delay);
- if (!success) {
- pthread_kill(thread, SIGINT);
- }
- pthread_join(thread, nullptr);
- return success;
-}
-
+// Call function on interfaceObject and wait for result until the given timeout has reached.
+// Callback functions pass to timeoutIPC() may be executed after the this function
+// has returned, especially if deadline has been reached. Hence, care must be taken when passing
+// data between the background thread and the main thread. See b/311143089.
template<class R, class P, class Function, class I, class... Args>
-typename std::result_of<Function(I *, Args...)>::type
+typename std::invoke_result<Function, I *, Args...>::type
timeoutIPC(std::chrono::duration<R, P> wait, const sp<I> &interfaceObject, Function &&func,
Args &&... args) {
using ::android::hardware::Status;
- typename std::result_of<Function(I *, Args...)>::type ret{Status::ok()};
- auto boundFunc = std::bind(std::forward<Function>(func),
- interfaceObject.get(), std::forward<Args>(args)...);
- bool success = timeout(wait, [&ret, &boundFunc] {
- ret = std::move(boundFunc());
- });
- if (!success) {
+
+ // Execute on a background thread but do not defer execution.
+ auto future =
+ std::async(std::launch::async, func, interfaceObject, std::forward<Args>(args)...);
+ auto status = future.wait_for(wait);
+ if (status == std::future_status::ready) {
+ return future.get();
+ }
+
+ // This future belongs to a background thread that we no longer care about.
+ // Putting this in the global list avoids std::future::~future() that may wait for the
+ // result to come back.
+ // This leaks memory, but lshal is a debugging tool, so this is fine.
+ static std::vector<decltype(future)> gDeadPool{};
+ gDeadPool.emplace_back(std::move(future));
+
+ if (status == std::future_status::timeout) {
return Status::fromStatusT(TIMED_OUT);
}
- return ret;
+ return Status::fromExceptionCode(Status::Exception::EX_ILLEGAL_STATE, "Illegal future_status");
}
-
-} // namespace lshal
-} // namespace android
+} // namespace lshal
+} // namespace android
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
index 366c938..bd5fa32 100644
--- a/cmds/lshal/main.cpp
+++ b/cmds/lshal/main.cpp
@@ -18,5 +18,6 @@
int main(int argc, char **argv) {
using namespace ::android::lshal;
- return Lshal{}.main(Arg{argc, argv});
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(Lshal{}.main(Arg{argc, argv}));
}
diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp
index cba7c4b..c24f827 100644
--- a/cmds/lshal/test.cpp
+++ b/cmds/lshal/test.cpp
@@ -14,6 +14,10 @@
* limitations under the License.
*/
+#include <chrono>
+#include <future>
+#include <mutex>
+#include "android/hidl/base/1.0/IBase.h"
#define LOG_TAG "Lshal"
#include <android-base/logging.h>
@@ -36,6 +40,8 @@
using namespace testing;
+using std::chrono_literals::operator""ms;
+
using ::android::hidl::base::V1_0::DebugInfo;
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::manager::V1_0::IServiceManager;
@@ -934,12 +940,9 @@
return hardware::Void();
}));
EXPECT_CALL(*serviceManager, get(_, _))
- .WillRepeatedly(
- Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> {
- int id = getIdFromInstanceName(instance);
- if (id > inheritanceLevel) return nullptr;
- return sp<IBase>(service);
- }));
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return sp<IBase>(service);
+ }));
const std::string expected = "[fake description 0]\n"
"Interface\n"
@@ -957,6 +960,110 @@
EXPECT_EQ("", err.str());
}
+// In SlowService, everything goes slooooooow. Each IPC call will wait for
+// the specified time before calling the callback function or returning.
+class SlowService : public IBase {
+public:
+ explicit SlowService(std::chrono::milliseconds wait) : mWait(wait) {}
+ android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ cb(getInterfaceName(1));
+ storeHistory("interfaceDescriptor");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1));
+ ret.push_back(IBase::descriptor);
+ cb(ret);
+ storeHistory("interfaceChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> getHashChain(getHashChain_cb cb) override {
+ std::this_thread::sleep_for(mWait);
+ std::vector<hidl_hash> ret;
+ ret.push_back(getHashFromId(0));
+ ret.push_back(getHashFromId(0xff));
+ cb(ret);
+ storeHistory("getHashChain");
+ return hardware::Void();
+ }
+ android::hardware::Return<void> debug(const hidl_handle&,
+ const hidl_vec<hidl_string>&) override {
+ std::this_thread::sleep_for(mWait);
+ storeHistory("debug");
+ return Void();
+ }
+
+ template <class R, class P, class Pred>
+ bool waitForHistory(std::chrono::duration<R, P> wait, Pred predicate) {
+ std::unique_lock<std::mutex> lock(mLock);
+ return mCv.wait_for(lock, wait, [&]() { return predicate(mCallHistory); });
+ }
+
+private:
+ void storeHistory(std::string hist) {
+ {
+ std::lock_guard<std::mutex> lock(mLock);
+ mCallHistory.emplace_back(std::move(hist));
+ }
+ mCv.notify_all();
+ }
+
+ const std::chrono::milliseconds mWait;
+ std::mutex mLock;
+ std::condition_variable mCv;
+ // List of functions that have finished being called on this interface.
+ std::vector<std::string> mCallHistory;
+};
+
+class TimeoutTest : public ListTest {
+public:
+ void setMockServiceManager(sp<IBase> service) {
+ EXPECT_CALL(*serviceManager, list(_))
+ .WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) {
+ std::vector<hidl_string> ret;
+ ret.push_back(getInterfaceName(1) + "/default");
+ cb(ret);
+ return hardware::Void();
+ }));
+ EXPECT_CALL(*serviceManager, get(_, _))
+ .WillRepeatedly(Invoke([&](const hidl_string&, const hidl_string&) -> sp<IBase> {
+ return service;
+ }));
+ }
+};
+
+TEST_F(TimeoutTest, BackgroundThreadIsKept) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 200ms;
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_THAT(err.str(), HasSubstr("Skipping \"a.h.foo1@1.0::IFoo/default\""));
+ EXPECT_TRUE(service->waitForHistory(serviceIpcTimeout * 5, [](const auto& hist) {
+ return hist.size() == 1 && hist[0] == "interfaceChain";
+ })) << "The background thread should continue after the main thread moves on, but it is killed";
+}
+
+TEST_F(TimeoutTest, BackgroundThreadDoesNotBlockMainThread) {
+ auto lshalIpcTimeout = 100ms;
+ auto serviceIpcTimeout = 2000ms;
+ auto start = std::chrono::system_clock::now();
+ lshal->setWaitTimeForTest(lshalIpcTimeout, lshalIpcTimeout);
+ sp<SlowService> service = new SlowService(serviceIpcTimeout);
+ setMockServiceManager(service);
+
+ optind = 1; // mimic Lshal::parseArg()
+ EXPECT_NE(0u, mockList->main(createArg({"lshal", "--types=b", "-i", "--neat"})));
+ EXPECT_LE(std::chrono::system_clock::now(), start + 5 * lshalIpcTimeout)
+ << "The main thread should not be blocked by the background task";
+}
+
class ListVintfTest : public ListTest {
public:
virtual void SetUp() override {
@@ -1079,5 +1186,6 @@
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
- return RUN_ALL_TESTS();
+ // Use _exit() to force terminate background threads in Timeout.h
+ _exit(RUN_ALL_TESTS());
}
diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc
index 4f92b3a..6c450ab 100644
--- a/cmds/servicemanager/servicemanager.rc
+++ b/cmds/servicemanager/servicemanager.rc
@@ -11,5 +11,5 @@
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/android/performance_hint.h b/include/android/performance_hint.h
index 9d2c791..30200c7 100644
--- a/include/android/performance_hint.h
+++ b/include/android/performance_hint.h
@@ -123,7 +123,8 @@
*
* @return APerformanceHintManager instance on success, nullptr on failure.
*/
-APerformanceHintManager* _Nullable APerformanceHint_getManager() __INTRODUCED_IN(__ANDROID_API_T__);
+APerformanceHintManager* _Nullable APerformanceHint_getManager()
+ __INTRODUCED_IN(__ANDROID_API_T__);
/**
* Creates a session for the given set of threads and sets their initial target work
@@ -260,7 +261,8 @@
*
* @param aWorkDuration The {@link AWorkDuration} created by calling {@link AWorkDuration_create()}
*/
-void AWorkDuration_release(AWorkDuration* _Nonnull WorkDuration) __INTRODUCED_IN(__ANDROID_API_V__);
+void AWorkDuration_release(AWorkDuration* _Nonnull aWorkDuration)
+ __INTRODUCED_IN(__ANDROID_API_V__);
/**
* Sets the work period start timestamp in nanoseconds.
diff --git a/include/android/thermal.h b/include/android/thermal.h
index 0b57e93..fa168cd 100644
--- a/include/android/thermal.h
+++ b/include/android/thermal.h
@@ -111,7 +111,7 @@
* It's passed the updated thermal status as parameter, as well as the
* pointer provided by the client that registered a callback.
*/
-typedef void (*AThermal_StatusCallback)(void* data, AThermalStatus status);
+typedef void (*AThermal_StatusCallback)(void* _Nullable data, AThermalStatus status);
/**
* Acquire an instance of the thermal manager. This must be freed using
@@ -121,7 +121,7 @@
*
* @return manager instance on success, nullptr on failure.
*/
-AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30);
+AThermalManager* _Nonnull AThermal_acquireManager() __INTRODUCED_IN(30);
/**
* Release the thermal manager pointer acquired via
@@ -131,7 +131,7 @@
*
* @param manager The manager to be released.
*/
-void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30);
+void AThermal_releaseManager(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
/**
* Gets the current thermal status.
@@ -143,7 +143,8 @@
*
* @return current thermal status, ATHERMAL_STATUS_ERROR on failure.
*/
-AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30);
+AThermalStatus
+AThermal_getCurrentThermalStatus(AThermalManager* _Nonnull manager) __INTRODUCED_IN(30);
/**
* Register the thermal status listener for thermal status change.
@@ -160,8 +161,9 @@
* EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
-int AThermal_registerThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
+int AThermal_registerThermalStatusListener(AThermalManager* _Nonnull manager,
+ AThermal_StatusCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(30);
/**
* Unregister the thermal status listener previously resgistered.
@@ -178,8 +180,9 @@
* EPERM if the required permission is not held.
* EPIPE if communication with the system service has failed.
*/
-int AThermal_unregisterThermalStatusListener(AThermalManager *manager,
- AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30);
+int AThermal_unregisterThermalStatusListener(AThermalManager* _Nonnull manager,
+ AThermal_StatusCallback _Nullable callback,
+ void* _Nullable data) __INTRODUCED_IN(30);
/**
* Provides an estimate of how much thermal headroom the device currently has before
@@ -219,8 +222,8 @@
* as described above. Returns NaN if the device does not support this functionality or
* if this function is called significantly faster than once per second.
*/
-float AThermal_getThermalHeadroom(AThermalManager *manager,
- int forecastSeconds) __INTRODUCED_IN(31);
+float AThermal_getThermalHeadroom(AThermalManager* _Nonnull manager,
+ int forecastSeconds) __INTRODUCED_IN(31);
/**
* This struct defines an instance of headroom threshold value and its status.
@@ -282,9 +285,10 @@
* EPIPE if communication with the system service has failed.
* ENOSYS if the feature is disabled by the current system.
*/
-int AThermal_getThermalHeadroomThresholds(AThermalManager* manager,
- const AThermalHeadroomThreshold ** outThresholds,
- size_t* size) __INTRODUCED_IN(35);
+int AThermal_getThermalHeadroomThresholds(AThermalManager* _Nonnull manager,
+ const AThermalHeadroomThreshold* _Nonnull
+ * _Nullable outThresholds,
+ size_t* _Nonnull size) __INTRODUCED_IN(35);
#ifdef __cplusplus
}
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 59b9495..750e170 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -283,6 +283,13 @@
*/
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 @@
*/
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/include/private/performance_hint_private.h b/include/private/performance_hint_private.h
index d50c5f8..d8f9db4 100644
--- a/include/private/performance_hint_private.h
+++ b/include/private/performance_hint_private.h
@@ -53,6 +53,26 @@
* CPU resources to what was used previously, and must wake up if inactive.
*/
CPU_LOAD_RESUME = 3,
+
+ /**
+ * This hint indicates an increase in GPU workload intensity. It means that
+ * this hint session needs extra GPU resources to meet the target duration.
+ * This hint must be sent before reporting the actual duration to the session.
+ */
+ GPU_LOAD_UP = 5,
+
+ /**
+ * This hint indicates a decrease in GPU workload intensity. It means that
+ * this hint session can reduce GPU resources and still meet the target duration.
+ */
+ GPU_LOAD_DOWN = 6,
+
+ /*
+ * This hint indicates an upcoming GPU workload that is completely changed and
+ * unknown. It means that the hint session should reset GPU resources to a known
+ * baseline to prepare for an arbitrary load, and must wake up if inactive.
+ */
+ GPU_LOAD_RESET = 7,
};
/**
diff --git a/libs/binder/UtilsHost.h b/libs/binder/UtilsHost.h
index b582f17..d6fe9fa 100644
--- a/libs/binder/UtilsHost.h
+++ b/libs/binder/UtilsHost.h
@@ -16,6 +16,7 @@
#pragma once
+#include <functional>
#include <optional>
#include <ostream>
#include <string>
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 f178027..864ff50 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 @@
*/
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 @@
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 @@
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 @@
}
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 @@
}
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/include_ndk/android/persistable_bundle.h b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
index eff8104..98c0cb2 100644
--- a/libs/binder/ndk/include_ndk/android/persistable_bundle.h
+++ b/libs/binder/ndk/include_ndk/android/persistable_bundle.h
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+#pragma once
+
#include <android/binder_parcel.h>
+#include <stdbool.h>
+#include <stdint.h>
#include <sys/cdefs.h>
#include <sys/types.h>
@@ -31,14 +36,30 @@
struct APersistableBundle;
typedef struct APersistableBundle APersistableBundle;
+enum {
+ /**
+ * This can be returned from functions that need to distinguish between an empty
+ * value and a non-existent key.
+ */
+ APERSISTABLEBUNDLE_KEY_NOT_FOUND = -1,
+
+ /**
+ * This can be returned from functions that take a APersistableBundle_stringAllocator.
+ * This means the allocator has failed and returned a nullptr.
+ */
+ APERSISTABLEBUNDLE_ALLOCATOR_FAILED = -2,
+};
+
/**
* This is a user supplied allocator that allocates a buffer for the
- * APersistableBundle APIs to fill in with a string.
+ * APersistableBundle APIs to fill in with a UTF-8 string.
+ * The caller that supplies this function is responsible for freeing the
+ * returned data.
*
* \param the required size in bytes for the allocated buffer
- * \param void* _Nullable context if needed by the callback
+ * \param context pointer if needed by the callback
*
- * \return allocated buffer of sizeBytes. Null if allocation failed.
+ * \return allocated buffer of sizeBytes for a UTF-8 string. Null if allocation failed.
*/
typedef char* _Nullable (*_Nonnull APersistableBundle_stringAllocator)(int32_t sizeBytes,
void* _Nullable context);
@@ -54,10 +75,12 @@
/**
* Create a new APersistableBundle based off an existing APersistableBundle.
+ * This is a deep copy, so the new APersistableBundle has its own values from
+ * copying the original underlying PersistableBundle.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to duplicate
+ * \param pBundle to duplicate
*
* \return Pointer to a new APersistableBundle
*/
@@ -68,11 +91,11 @@
* Delete an APersistableBundle. This must always be called when finished using
* the object.
*
- * \param bundle to delete
+ * \param pBundle to delete. No-op if null.
*
* Available since API level __ANDROID_API_V__.
*/
-void APersistableBundle_delete(APersistableBundle* _Nonnull pBundle)
+void APersistableBundle_delete(APersistableBundle* _Nullable pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -80,8 +103,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param lhs bundle to compare agains the other param
- * \param rhs bundle to compare agains the other param
+ * \param lhs bundle to compare against the other param
+ * \param rhs bundle to compare against the other param
*
* \return true when equal, false when not
*/
@@ -134,11 +157,11 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to get the size of (number of mappings)
+ * \param pBundle to get the size of (number of mappings)
*
* \return number of mappings in the object
*/
-int32_t APersistableBundle_size(APersistableBundle* _Nonnull pBundle)
+int32_t APersistableBundle_size(const APersistableBundle* _Nonnull pBundle)
__INTRODUCED_IN(__ANDROID_API_V__);
/**
@@ -146,8 +169,8 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping to erase
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8 to erase
*
* \return number of entries erased. Either 0 or 1.
*/
@@ -158,8 +181,8 @@
* Put a boolean associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
* \param value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
@@ -171,9 +194,9 @@
* Put an int32_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -184,9 +207,9 @@
* Put an int64_t associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -197,9 +220,9 @@
* Put a double associated with the provided key.
* New values with the same key will overwrite existing values.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -209,10 +232,11 @@
/**
* Put a string associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -222,11 +246,12 @@
/**
* Put a boolean vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -237,11 +262,12 @@
/**
* Put an int32_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -252,11 +278,12 @@
/**
* Put an int64_t vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -267,11 +294,12 @@
/**
* Put a double vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -282,11 +310,12 @@
/**
* Put a string vector associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The values are copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
- * \param size in number of elements in the vector
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param vec vector to put for the mapping
+ * \param num number of elements in the vector
*
* Available since API level __ANDROID_API_V__.
*/
@@ -298,10 +327,11 @@
/**
* Put an APersistableBundle associated with the provided key.
* New values with the same key will overwrite existing values.
+ * The value is deep-copied.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param value to put for the mapping
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val value to put for the mapping
*
* Available since API level __ANDROID_API_V__.
*/
@@ -315,9 +345,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -330,9 +360,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -344,9 +374,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -359,9 +389,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to
*
* \return true if a value exists for the provided key
*/
@@ -371,17 +401,19 @@
/**
* Get a string associated with the provided key.
+ * The caller is responsible for freeing the returned data.
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to write the value to
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to write the value to in UTF-8
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
- * \return size of string associated with the provided key on success
- * 0 if no string exists for the provided key
- * -1 if the provided allocator fails and returns false
+ * \return size of string in bytes associated with the provided key on success
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getString(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, char* _Nullable* _Nonnull val,
@@ -393,7 +425,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -401,13 +433,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, bool* _Nullable buffer,
@@ -419,7 +452,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -427,13 +460,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getIntVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int32_t* _Nullable buffer,
@@ -444,7 +478,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -452,13 +486,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getLongVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, int64_t* _Nullable buffer,
@@ -470,7 +505,7 @@
* provided pre-allocated buffer from the user.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -478,13 +513,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the values to
+ * \param bufferSizeBytes size of the pre-allocated buffer
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
*/
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key, double* _Nullable buffer,
@@ -496,9 +532,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes of stored vector.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -506,17 +543,18 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param buffer pointer to a pre-allocated buffer to write the string pointers to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the stored vector in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
* 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_KEY_NOT_FOUND if the key was not found
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringVector(const APersistableBundle* _Nonnull pBundle,
const char* _Nonnull key,
@@ -531,9 +569,9 @@
*
* Available since API level __ANDROID_API_V__.
*
- * \param bundle to operate on
- * \param key for the mapping
- * \param nonnull pointer to an APersistableBundle pointer to write to point to
+ * \param pBundle to operate on
+ * \param key for the mapping in UTF-8
+ * \param val pointer to an APersistableBundle pointer to write to point to
* a new copy of the stored APersistableBundle. The caller takes ownership of
* the new APersistableBundle and must be deleted with
* APersistableBundle_delete.
@@ -550,9 +588,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -560,16 +599,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -583,9 +621,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -593,16 +632,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -614,9 +652,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -624,16 +663,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys, int32_t bufferSizeBytes,
@@ -645,9 +683,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -655,16 +694,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getDoubleKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -678,9 +716,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -688,16 +727,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getStringKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -711,9 +749,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -721,16 +760,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getBooleanVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -744,9 +782,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -754,16 +793,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getIntVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -777,9 +815,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -787,16 +826,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getLongVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -810,9 +848,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -820,16 +859,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
*/
int32_t APersistableBundle_getDoubleVectorKeys(const APersistableBundle* _Nonnull pBundle,
char* _Nullable* _Nullable outKeys,
@@ -843,9 +880,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -853,15 +891,14 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
* false
*/
int32_t APersistableBundle_getStringVectorKeys(const APersistableBundle* _Nonnull pBundle,
@@ -876,9 +913,10 @@
* provided pre-allocated buffer from the user. The user must provide an
* APersistableBundle_stringAllocator for the individual strings to be
* allocated.
+ * The caller is responsible for freeing the returned data in bytes.
*
* This function returns the size in bytes required to fit the fill list of keys.
- * The supplied buffer will be filled in based on the smaller of the suplied
+ * The supplied buffer will be filled in based on the smaller of the supplied
* bufferSizeBytes or the actual size of the stored data.
* If the buffer is null or if the supplied bufferSizeBytes is smaller than the
* actual stored data, then not all of the stored data will be returned.
@@ -886,16 +924,15 @@
* Users can call this function with null buffer and 0 bufferSizeBytes to get
* the required size of the buffer to use on a subsequent call.
*
- * \param bundle to operate on
- * \param nonnull pointer to a pre-allocated buffer to write the values to
- * \param size of the pre-allocated buffer
- * \param function pointer to the string dup allocator
+ * \param pBundle to operate on
+ * \param outKeys pointer to a pre-allocated buffer to write the UTF-8 keys to
+ * \param bufferSizeBytes size of the pre-allocated buffer
+ * \param stringAllocator function pointer to the string allocator
+ * \param context pointer that will be passed to the stringAllocator
*
* \return size of the buffer of keys in bytes. This is the required size of the
* pre-allocated user supplied buffer if all of the stored contents are desired.
- * 0 if no string vector exists for the provided key
- * -1 if the user supplied APersistableBundle_stringAllocator returns
- * false
+ * APERSISTABLEBUNDLE_ALLOCATOR_FAILED if the provided allocator fails
*/
int32_t APersistableBundle_getPersistableBundleKeys(
const APersistableBundle* _Nonnull pBundle, char* _Nullable* _Nullable outKeys,
diff --git a/libs/binder/ndk/persistable_bundle.cpp b/libs/binder/ndk/persistable_bundle.cpp
index 404611c..9b6877d 100644
--- a/libs/binder/ndk/persistable_bundle.cpp
+++ b/libs/binder/ndk/persistable_bundle.cpp
@@ -76,7 +76,7 @@
return pBundle->mPBundle.writeToParcel(AParcel_viewPlatformParcel(parcel));
}
-int32_t APersistableBundle_size(APersistableBundle* pBundle) {
+int32_t APersistableBundle_size(const APersistableBundle* pBundle) {
size_t size = pBundle->mPBundle.size();
LOG_ALWAYS_FATAL_IF(size > INT32_MAX,
"The APersistableBundle has gotten too large! There will be an overflow in "
@@ -167,40 +167,42 @@
void* context) {
android::String16 outVal;
bool ret = pBundle->mPBundle.getString(android::String16(key), &outVal);
- if (ret) {
- android::String8 tmp8(outVal);
- *val = stringAllocator(tmp8.bytes() + 1, context);
- if (*val) {
- strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
- return tmp8.bytes();
- } else {
- return -1;
- }
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
+ android::String8 tmp8(outVal);
+ *val = stringAllocator(tmp8.bytes() + 1, context);
+ if (*val) {
+ strncpy(*val, tmp8.c_str(), tmp8.bytes() + 1);
+ return tmp8.bytes();
+ } else {
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
- return 0;
}
int32_t APersistableBundle_getBooleanVector(const APersistableBundle* pBundle, const char* key,
bool* buffer, int32_t bufferSizeBytes) {
std::vector<bool> newVec;
- pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getBooleanVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<bool>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getIntVector(const APersistableBundle* pBundle, const char* key,
int32_t* buffer, int32_t bufferSizeBytes) {
std::vector<int32_t> newVec;
- pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getIntVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int32_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getLongVector(const APersistableBundle* pBundle, const char* key,
int64_t* buffer, int32_t bufferSizeBytes) {
std::vector<int64_t> newVec;
- pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getLongVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<int64_t>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getDoubleVector(const APersistableBundle* pBundle, const char* key,
double* buffer, int32_t bufferSizeBytes) {
std::vector<double> newVec;
- pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getDoubleVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getVecInternal<double>(newVec, buffer, bufferSizeBytes);
}
int32_t APersistableBundle_getStringVector(const APersistableBundle* pBundle, const char* key,
@@ -208,7 +210,8 @@
APersistableBundle_stringAllocator stringAllocator,
void* context) {
std::vector<android::String16> newVec;
- pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ bool ret = pBundle->mPBundle.getStringVector(android::String16(key), &newVec);
+ if (!ret) return APERSISTABLEBUNDLE_KEY_NOT_FOUND;
return getStringsInternal<std::vector<android::String16>>(newVec, vec, bufferSizeBytes,
stringAllocator, context);
}
diff --git a/libs/binder/ndk/persistable_bundle_internal.h b/libs/binder/ndk/persistable_bundle_internal.h
index 279c66f..bee10fd 100644
--- a/libs/binder/ndk/persistable_bundle_internal.h
+++ b/libs/binder/ndk/persistable_bundle_internal.h
@@ -61,7 +61,7 @@
int32_t numAvailable = bufferSizeBytes / sizeof(char*);
int32_t numFill = numAvailable < num ? numAvailable : num;
if (!stringAllocator) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
if (numFill > 0 && buffer) {
@@ -70,7 +70,7 @@
android::String8 tmp8 = android::String8(val);
buffer[i] = stringAllocator(tmp8.bytes() + 1, context);
if (buffer[i] == nullptr) {
- return -1;
+ return APERSISTABLEBUNDLE_ALLOCATOR_FAILED;
}
strncpy(buffer[i], tmp8.c_str(), tmp8.bytes() + 1);
i++;
diff --git a/libs/binder/ndk/stability.cpp b/libs/binder/ndk/stability.cpp
index 73eb863..ca3d5e6 100644
--- a/libs/binder/ndk/stability.cpp
+++ b/libs/binder/ndk/stability.cpp
@@ -27,7 +27,7 @@
#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 0ea4a3f..10912c7 100644
--- a/libs/binder/tests/binderThroughputTest.cpp
+++ b/libs/binder/tests/binderThroughputTest.cpp
@@ -49,6 +49,63 @@
}
};
+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 @@
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 @@
}
};
-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,34 +217,37 @@
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;
+ p.signal();
+ p.wait();
+
+ 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;
- int target = cs_pair ? num % server_count : rand() % workers.size();
- int sz = payload_size;
- while (sz >= sizeof(uint32_t)) {
- data.writeInt32(0);
- sz -= sizeof(uint32_t);
- }
- start = chrono::high_resolution_clock::now();
- status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
- end = chrono::high_resolution_clock::now();
+ // Skip the benchmark if server of a cs_pair.
+ if (!(cs_pair && num < server_count)) {
+ for (int i = 0; i < iterations; i++) {
+ Parcel data, reply;
+ int target = cs_pair ? num % server_count : rand() % workers.size();
+ int sz = payload_size;
- uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
- results.add_time(cur_time);
+ while (sz >= sizeof(uint32_t)) {
+ data.writeInt32(0);
+ sz -= sizeof(uint32_t);
+ }
+ start = chrono::high_resolution_clock::now();
+ status_t ret = workers[target]->transact(BINDER_NOP, data, &reply);
+ end = chrono::high_resolution_clock::now();
- if (ret != NO_ERROR) {
- cout << "thread " << num << " failed " << ret << "i : " << i << endl;
- exit(EXIT_FAILURE);
+ uint64_t cur_time = uint64_t(chrono::duration_cast<chrono::nanoseconds>(end - start).count());
+ results.add_time(cur_time);
+
+ if (ret != NO_ERROR) {
+ cout << "thread " << num << " failed " << ret << "i : " << i << endl;
+ exit(EXIT_FAILURE);
+ }
}
}
@@ -289,8 +305,15 @@
pipes.push_back(make_worker(i, iterations, workers, payload_size, cs_pair));
}
wait_all(pipes);
+ // All workers have now been spawned and added themselves to service
+ // manager. Signal each worker to obtain a handle to the server workers from
+ // servicemanager.
+ signal_all(pipes);
+ // Wait for each worker to finish obtaining a handle to all server workers
+ // from servicemanager.
+ wait_all(pipes);
- // Run the workers and wait for completion.
+ // Run the benchmark and wait for completion.
chrono::time_point<chrono::high_resolution_clock> start, end;
cout << "waiting for workers to complete" << endl;
start = chrono::high_resolution_clock::now();
@@ -305,11 +328,10 @@
// 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 +345,11 @@
}
}
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 +360,7 @@
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 +370,65 @@
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/tests/format.h b/libs/binder/tests/format.h
index b5440a4..c588de7 100644
--- a/libs/binder/tests/format.h
+++ b/libs/binder/tests/format.h
@@ -18,7 +18,7 @@
// ETA for this blocker is 2023-10-27~2023-11-10.
// Also, remember to remove fmtlib's format.cc from trusty makefiles.
-#if __has_include(<format>)
+#if __has_include(<format>) && !defined(_LIBCPP_HAS_NO_INCOMPLETE_FORMAT)
#include <format>
#else
#include <fmt/format.h>
diff --git a/libs/binder/trusty/rust/rules.mk b/libs/binder/trusty/rust/rules.mk
index 6de7eb5..d343f14 100644
--- a/libs/binder/trusty/rust/rules.mk
+++ b/libs/binder/trusty/rust/rules.mk
@@ -30,6 +30,9 @@
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 ae242f3..08f30de 100644
--- a/libs/fakeservicemanager/FakeServiceManager.cpp
+++ b/libs/fakeservicemanager/FakeServiceManager.cpp
@@ -122,9 +122,19 @@
}
void FakeServiceManager::clear() {
- std::lock_guard<std::mutex> l(mMutex);
+ std::map<String16, sp<IBinder>> backup;
- mNameToService.clear();
+ {
+ std::lock_guard<std::mutex> l(mMutex);
+ backup = mNameToService;
+ mNameToService.clear();
+ }
+
+ // destructors may access FSM, so avoid recursive lock
+ backup.clear(); // explicit
+
+ // TODO: destructors may have added more services here - may want
+ // to check this or abort
}
} // namespace android
@@ -147,4 +157,4 @@
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/gui/Choreographer.cpp b/libs/gui/Choreographer.cpp
index c6f3363..7d37fd3 100644
--- a/libs/gui/Choreographer.cpp
+++ b/libs/gui/Choreographer.cpp
@@ -344,8 +344,11 @@
handleRefreshRateUpdates();
}
-void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId, int32_t, int32_t) {
- LOG_ALWAYS_FATAL("dispatchHdcpLevelsChanged was called but was never registered");
+void Choreographer::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int32_t connectedLevel,
+ int32_t maxLevel) {
+ ALOGV("choreographer %p ~ received hdcp levels change event (displayId=%s, connectedLevel=%d, "
+ "maxLevel=%d), ignoring.",
+ this, to_string(displayId).c_str(), connectedLevel, maxLevel);
}
void Choreographer::handleMessage(const Message& message) {
diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp
index ba1d196..95b2641 100644
--- a/libs/gui/WindowInfo.cpp
+++ b/libs/gui/WindowInfo.cpp
@@ -258,8 +258,7 @@
mInfo = handle->mInfo;
}
-std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
- const WindowInfo& info = *window.getInfo();
+std::ostream& operator<<(std::ostream& out, const WindowInfo& info) {
std::string transform;
info.transform.dump(transform, "transform", " ");
out << "name=" << info.name << ", id=" << info.id << ", displayId=" << info.displayId
@@ -277,4 +276,10 @@
return out;
}
+std::ostream& operator<<(std::ostream& out, const WindowInfoHandle& window) {
+ const WindowInfo& info = *window.getInfo();
+ out << info;
+ return out;
+}
+
} // namespace android::gui
diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h
index b72b71a..e72fd59 100644
--- a/libs/gui/include/gui/WindowInfo.h
+++ b/libs/gui/include/gui/WindowInfo.h
@@ -269,6 +269,8 @@
status_t readFromParcel(const android::Parcel* parcel) override;
};
+std::ostream& operator<<(std::ostream& out, const WindowInfo& window);
+
/*
* Handle for a window that can receive input.
*
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index dd8dc8d..b74c3b2 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -220,7 +220,7 @@
"libPlatformProperties",
"libtinyxml2",
"libutils",
- "libvintf",
+ "libz", // needed by libkernelconfigs
"server_configurable_flags",
],
@@ -239,6 +239,7 @@
"libgui_window_info_static",
"libui-types",
"libtflite_static",
+ "libkernelconfigs",
],
whole_static_libs: [
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 669f801..598f949 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 @@
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 @@
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/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp
index 3c1ae3e..ab8c341 100644
--- a/libs/input/KeyLayoutMap.cpp
+++ b/libs/input/KeyLayoutMap.cpp
@@ -27,8 +27,7 @@
#include <utils/Timers.h>
#include <utils/Tokenizer.h>
#if defined(__ANDROID__)
-#include <vintf/RuntimeInfo.h>
-#include <vintf/VintfObject.h>
+#include <vintf/KernelConfigs.h>
#endif
#include <cstdlib>
@@ -98,12 +97,10 @@
bool kernelConfigsArePresent(const std::set<std::string>& configs) {
#if defined(__ANDROID__)
- std::shared_ptr<const android::vintf::RuntimeInfo> runtimeInfo =
- android::vintf::VintfObject::GetInstance()->getRuntimeInfo(
- vintf::RuntimeInfo::FetchFlag::CONFIG_GZ);
- LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched");
+ std::map<std::string, std::string> kernelConfigs;
+ const status_t result = android::kernelconfigs::LoadKernelConfigs(&kernelConfigs);
+ LOG_ALWAYS_FATAL_IF(result != OK, "Kernel configs could not be fetched");
- const std::map<std::string, std::string>& kernelConfigs = runtimeInfo->kernelConfigs();
for (const std::string& requiredConfig : configs) {
const auto configIt = kernelConfigs.find(requiredConfig);
if (configIt == kernelConfigs.end()) {
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
index 138898f..13cfb49 100644
--- a/libs/input/tests/Android.bp
+++ b/libs/input/tests/Android.bp
@@ -36,8 +36,10 @@
"libgmock",
"libgui_window_info_static",
"libinput",
+ "libkernelconfigs",
"libtflite_static",
"libui-types",
+ "libz", // needed by libkernelconfigs
],
cflags: [
"-Wall",
@@ -61,7 +63,6 @@
"libPlatformProperties",
"libtinyxml2",
"libutils",
- "libvintf",
"server_configurable_flags",
],
data: [
diff --git a/libs/input/tests/InputChannel_test.cpp b/libs/input/tests/InputChannel_test.cpp
index 0661261..650c930 100644
--- a/libs/input/tests/InputChannel_test.cpp
+++ b/libs/input/tests/InputChannel_test.cpp
@@ -81,8 +81,7 @@
<< "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 @@
<< "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 @@
<< "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 06b841b..2000335 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -40,6 +40,182 @@
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 @@
void publishAndConsumeKeyEvent();
void publishAndConsumeMotionStream();
+ void publishAndConsumeMotionDown(nsecs_t downTime);
+ void publishAndConsumeBatchedMotionMove(nsecs_t downTime);
void publishAndConsumeFocusEvent();
void publishAndConsumeCaptureEvent();
void publishAndConsumeDragEvent();
@@ -73,16 +251,6 @@
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 @@
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 @@
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;
+void InputPublisherAndConsumerTest::publishAndConsumeMotionDown(nsecs_t downTime) {
+ publishAndConsumeMotionEvent(AMOTION_EVENT_ACTION_DOWN, downTime,
+ {Pointer{.id = 0, .x = 20, .y = 30}});
+}
- 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;
+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);
- 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;
-
- 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);
- }
-
- 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 @@
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 68ac7e0..e496c45 100644
--- a/libs/nativewindow/include/android/native_window_aidl.h
+++ b/libs/nativewindow/include/android/native_window_aidl.h
@@ -106,7 +106,7 @@
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 @@
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 a98ea86..969a5cf 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1095,10 +1095,19 @@
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 a185a59..8bc1292 100644
--- a/libs/nativewindow/libnativewindow.map.txt
+++ b/libs/nativewindow/libnativewindow.map.txt
@@ -2,11 +2,11 @@
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 @@
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 @@
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 @@
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/libs/renderengine/skia/AutoBackendTexture.cpp b/libs/renderengine/skia/AutoBackendTexture.cpp
index 92fe4c0..ee95e59 100644
--- a/libs/renderengine/skia/AutoBackendTexture.cpp
+++ b/libs/renderengine/skia/AutoBackendTexture.cpp
@@ -77,7 +77,7 @@
backendFormat,
isOutputBuffer);
} else {
- LOG_ALWAYS_FATAL("Unexpected backend %d", backend);
+ LOG_ALWAYS_FATAL("Unexpected backend %u", static_cast<unsigned>(backend));
}
mColorType = GrAHardwareBufferUtils::GetSkColorTypeFromBufferFormat(desc.format);
@@ -145,8 +145,8 @@
"\n\tGrBackendTexture: (%i x %i) hasMipmaps: %i isProtected: %i "
"texType: %i\n\t\tVkImageInfo: success: %i fFormat: %i "
"fSampleCount: %u fLevelCount: %u colorType %i",
- msg, tex.isValid(), dataspace, tex.width(), tex.height(),
- tex.hasMipmaps(), tex.isProtected(),
+ msg, tex.isValid(), static_cast<int32_t>(dataspace), tex.width(),
+ tex.height(), tex.hasMipmaps(), tex.isProtected(),
static_cast<int>(tex.textureType()), retrievedImageInfo,
imageInfo.fFormat, imageInfo.fSampleCount, imageInfo.fLevelCount,
colorType);
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index f8ee3fc..b82a79f 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -88,49 +88,51 @@
SensorManager* sensorManager;
auto iterator = sPackageInstances.find(packageName);
+ const uid_t uid = IPCThreadState::self()->getCallingUid();
+ const int deviceId = getDeviceIdForUid(uid);
+
+ // Return the cached instance if the device association of the package has not changed.
if (iterator != sPackageInstances.end()) {
sensorManager = iterator->second;
- } else {
- String16 opPackageName = packageName;
- const uid_t uid = IPCThreadState::self()->getCallingUid();
-
- // It is possible that the calling code has no access to the package name.
- // In this case we will get the packages for the calling UID and pick the
- // first one for attributing the app op. This will work correctly for
- // runtime permissions as for legacy apps we will toggle the app op for
- // all packages in the UID. The caveat is that the operation may be attributed
- // to the wrong package and stats based on app ops may be slightly off.
- if (opPackageName.size() <= 0) {
- sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
- if (binder != nullptr) {
- Vector<String16> packages;
- interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
- if (!packages.isEmpty()) {
- opPackageName = packages[0];
- } else {
- ALOGE("No packages for calling UID");
- }
- } else {
- ALOGE("Cannot get permission service");
- }
+ if (sensorManager->mDeviceId == deviceId) {
+ return *sensorManager;
}
-
- // Check if the calling UID is observed on a virtual device. If so, provide that device's
- // sensors by default instead of the default device's sensors.
- const int deviceId = getDeviceIdForUid(uid);
- sensorManager = new SensorManager(opPackageName, deviceId);
-
- // If we had no package name, we looked it up from the UID and the sensor
- // manager instance we created should also be mapped to the empty package
- // name, to avoid looking up the packages for a UID and get the same result.
- if (packageName.size() <= 0) {
- sPackageInstances.insert(std::make_pair(String16(), sensorManager));
- }
-
- // Stash the per package sensor manager.
- sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
}
+ // It is possible that the calling code has no access to the package name.
+ // In this case we will get the packages for the calling UID and pick the
+ // first one for attributing the app op. This will work correctly for
+ // runtime permissions as for legacy apps we will toggle the app op for
+ // all packages in the UID. The caveat is that the operation may be attributed
+ // to the wrong package and stats based on app ops may be slightly off.
+ String16 opPackageName = packageName;
+ if (opPackageName.size() <= 0) {
+ sp<IBinder> binder = defaultServiceManager()->getService(String16("permission"));
+ if (binder != nullptr) {
+ Vector<String16> packages;
+ interface_cast<IPermissionController>(binder)->getPackagesForUid(uid, packages);
+ if (!packages.isEmpty()) {
+ opPackageName = packages[0];
+ } else {
+ ALOGE("No packages for calling UID");
+ }
+ } else {
+ ALOGE("Cannot get permission service");
+ }
+ }
+
+ sensorManager = new SensorManager(opPackageName, deviceId);
+
+ // If we had no package name, we looked it up from the UID and the sensor
+ // manager instance we created should also be mapped to the empty package
+ // name, to avoid looking up the packages for a UID and get the same result.
+ if (packageName.size() <= 0) {
+ sPackageInstances.insert(std::make_pair(String16(), sensorManager));
+ }
+
+ // Stash the per package sensor manager.
+ sPackageInstances.insert(std::make_pair(opPackageName, sensorManager));
+
return *sensorManager;
}
diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp
index 69f42bc..a7955cf 100644
--- a/services/inputflinger/Android.bp
+++ b/services/inputflinger/Android.bp
@@ -104,6 +104,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -123,14 +125,6 @@
android: {
shared_libs: [
"libgui",
- "libstatspull",
- "libstatssocket",
- ],
- },
- host: {
- static_libs: [
- "libstatspull",
- "libstatssocket",
],
},
},
diff --git a/services/inputflinger/InputFilter.cpp b/services/inputflinger/InputFilter.cpp
index 43ebc69..72c6f1a 100644
--- a/services/inputflinger/InputFilter.cpp
+++ b/services/inputflinger/InputFilter.cpp
@@ -44,9 +44,11 @@
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 @@
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 a38fbf6..153d29d 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 @@
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 @@
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 8c8f5e8..a8759b7 100644
--- a/services/inputflinger/InputFilterCallbacks.cpp
+++ b/services/inputflinger/InputFilterCallbacks.cpp
@@ -29,8 +29,9 @@
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 @@
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 c0a80fb..31c160a 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 @@
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 @@
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 296f244..4863513 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -127,7 +127,8 @@
*/
InputManager::InputManager(const sp<InputReaderPolicyInterface>& readerPolicy,
InputDispatcherPolicyInterface& dispatcherPolicy,
- PointerChoreographerPolicyInterface& choreographerPolicy) {
+ PointerChoreographerPolicyInterface& choreographerPolicy,
+ InputFilterPolicyInterface& inputFilterPolicy) {
mInputFlingerRust = createInputFlingerRust();
mDispatcher = createInputDispatcher(dispatcherPolicy);
@@ -135,7 +136,8 @@
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 fa7db37..df944ef 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 @@
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 0be4c32..4571ef4 100644
--- a/services/inputflinger/PointerChoreographer.cpp
+++ b/services/inputflinger/PointerChoreographer.cpp
@@ -222,6 +222,7 @@
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/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 8b57730..c7bacee 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -65,6 +65,8 @@
"libprotobuf-cpp-lite",
"libstatslog",
"libutils",
+ "libstatspull",
+ "libstatssocket",
"server_configurable_flags",
],
static_libs: [
@@ -75,14 +77,6 @@
android: {
shared_libs: [
"libgui",
- "libstatspull",
- "libstatssocket",
- ],
- },
- host: {
- static_libs: [
- "libstatspull",
- "libstatssocket",
],
},
},
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index ab815ea..e998d91 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -763,6 +763,21 @@
return state.hasActiveStylus();
}
+Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) {
+ struct HashFunction {
+ size_t operator()(const WindowInfo& info) const { return info.id; }
+ };
+
+ std::unordered_set<WindowInfo, HashFunction> windowSet;
+ for (const WindowInfo& info : update.windowInfos) {
+ const auto [_, inserted] = windowSet.insert(info);
+ if (!inserted) {
+ return Error() << "Duplicate entry for " << info;
+ }
+ }
+ return {};
+}
+
} // namespace
// --- InputDispatcher ---
@@ -835,7 +850,7 @@
// 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 +957,7 @@
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 +981,7 @@
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 +1002,7 @@
if (currentTime >= mKeyRepeatState.nextRepeatTime) {
mPendingEvent = synthesizeKeyRepeatLocked(currentTime);
} else {
- if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) {
- *nextWakeupTime = mKeyRepeatState.nextRepeatTime;
- }
+ nextWakeupTime = std::min(nextWakeupTime, mKeyRepeatState.nextRepeatTime);
}
}
@@ -1157,7 +1168,7 @@
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 +1760,7 @@
}
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 +1811,7 @@
// 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 +1891,7 @@
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 +1931,7 @@
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 +2176,7 @@
}
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 +2215,7 @@
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 +2260,7 @@
// 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;
}
@@ -3847,8 +3856,7 @@
connection->getInputChannelName().c_str(), seq, toString(handled));
}
- if (connection->status == Connection::Status::BROKEN ||
- connection->status == Connection::Status::ZOMBIE) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4009,7 +4017,7 @@
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
- if (connection->status == Connection::Status::BROKEN) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -4121,7 +4129,7 @@
void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
const nsecs_t downTime, const std::shared_ptr<Connection>& connection,
ftl::Flags<InputTarget::Flags> targetFlags) {
- if (connection->status == Connection::Status::BROKEN) {
+ if (connection->status != Connection::Status::NORMAL) {
return;
}
@@ -6768,6 +6776,15 @@
}
void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
+ if (auto result = validateWindowInfosUpdate(update); !result.ok()) {
+ {
+ // acquire lock
+ std::scoped_lock _l(mLock);
+ logDispatchStateLocked();
+ }
+ LOG_ALWAYS_FATAL("Incorrect WindowInfosUpdate provided: %s",
+ result.error().message().c_str());
+ };
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 010dbb2..3567288 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -209,7 +209,7 @@
// 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 @@
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 @@
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 @@
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 0000000..4d39b97
--- /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/Android.bp b/services/inputflinger/reader/Android.bp
index e1806a0..f954370 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -86,6 +86,7 @@
"liblog",
"libPlatformProperties",
"libstatslog",
+ "libstatspull",
"libutils",
],
static_libs: [
@@ -99,15 +100,9 @@
"libinputreader_headers",
],
target: {
- android: {
- shared_libs: [
- "libstatspull",
- ],
- },
host: {
static_libs: [
"libbinder",
- "libstatspull",
],
},
},
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index f7bbc51..3ca691e 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -810,14 +810,20 @@
case EV_SYN: {
switch (event.code) {
case SYN_REPORT:
- currentFrameDropped = false;
+ if (currentFrameDropped) {
+ // To recover after a SYN_DROPPED, we need to query the state of the device
+ // to synchronize our device state with the kernel's to account for the
+ // dropped events on receiving the next SYN_REPORT.
+ // Note we don't drop the SYN_REPORT at this point but it is used by the
+ // InputDevice to reset and repopulate mapper state
+ readDeviceState();
+ currentFrameDropped = false;
+ }
break;
case SYN_DROPPED:
// When we receive SYN_DROPPED, all events in the current frame should be
- // dropped. We query the state of the device to synchronize our device state
- // with the kernel's to account for the dropped events.
+ // dropped up to and including next SYN_REPORT
currentFrameDropped = true;
- readDeviceState();
break;
default:
break;
@@ -1141,6 +1147,22 @@
return OK;
}
+base::Result<std::vector<int32_t>> EventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const {
+ std::scoped_lock _l(mLock);
+ const Device* device = getDeviceLocked(deviceId);
+ if (device == nullptr || !device->hasValidFd() || !device->absBitmask.test(axis)) {
+ return base::ResultError("device problem or axis not supported", NAME_NOT_FOUND);
+ }
+ std::vector<int32_t> outValues(slotCount + 1);
+ outValues[0] = axis;
+ const size_t bufferSize = outValues.size() * sizeof(int32_t);
+ if (ioctl(device->fd, EVIOCGMTSLOTS(bufferSize), outValues.data()) != OK) {
+ return base::ErrnoError();
+ }
+ return std::move(outValues);
+}
+
bool EventHub::markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
std::scoped_lock _l(mLock);
diff --git a/services/inputflinger/reader/InputDevice.cpp b/services/inputflinger/reader/InputDevice.cpp
index fb32f96..a41064b 100644
--- a/services/inputflinger/reader/InputDevice.cpp
+++ b/services/inputflinger/reader/InputDevice.cpp
@@ -350,6 +350,7 @@
if (mDropUntilNextSync) {
if (rawEvent->type == EV_SYN && rawEvent->code == SYN_REPORT) {
+ out += reset(rawEvent->when);
mDropUntilNextSync = false;
ALOGD_IF(debugRawEvents(), "Recovered from input event buffer overrun.");
} else {
@@ -359,7 +360,6 @@
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_DROPPED) {
ALOGI("Detected input event buffer overrun for device %s.", getName().c_str());
mDropUntilNextSync = true;
- out += reset(rawEvent->when);
} else {
for_each_mapper_in_subdevice(rawEvent->deviceId, [&](InputMapper& mapper) {
out += mapper.process(rawEvent);
diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h
index 0bcab42..a7e0675 100644
--- a/services/inputflinger/reader/include/EventHub.h
+++ b/services/inputflinger/reader/include/EventHub.h
@@ -336,6 +336,10 @@
virtual int32_t getSwitchState(int32_t deviceId, int32_t sw) const = 0;
virtual status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const = 0;
+ /* Query Multi-Touch slot values for an axis. Returns error or an 1 indexed array of size
+ * (slotCount + 1). The value at the 0 index is set to queried axis. */
+ virtual base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const = 0;
virtual int32_t getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const = 0;
/*
@@ -552,6 +556,8 @@
int32_t locationKeyCode) const override final;
status_t getAbsoluteAxisValue(int32_t deviceId, int32_t axis,
int32_t* outValue) const override final;
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override final;
bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const override final;
diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index 31dcb2e..ba7234b 100644
--- a/services/inputflinger/reader/include/InputDevice.h
+++ b/services/inputflinger/reader/include/InputDevice.h
@@ -280,7 +280,7 @@
class InputDeviceContext {
public:
InputDeviceContext(InputDevice& device, int32_t eventHubId);
- ~InputDeviceContext();
+ virtual ~InputDeviceContext();
inline InputReaderContext* getContext() { return mContext; }
inline int32_t getId() { return mDeviceId; }
@@ -372,6 +372,10 @@
inline status_t getAbsoluteAxisValue(int32_t code, int32_t* outValue) const {
return mEventHub->getAbsoluteAxisValue(mId, code, outValue);
}
+ inline base::Result<std::vector<int32_t>> getMtSlotValues(int32_t axis,
+ size_t slotCount) const {
+ return mEventHub->getMtSlotValues(mId, axis, slotCount);
+ }
inline bool markSupportedKeyCodes(const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const {
return mEventHub->markSupportedKeyCodes(mId, keyCodes, outFlags);
@@ -450,7 +454,7 @@
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/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f068cc8..9e7e956 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -61,6 +61,36 @@
scanCode >= BTN_WHEEL;
}
+static bool isMediaKey(int32_t keyCode) {
+ switch (keyCode) {
+ case AKEYCODE_MEDIA_PLAY:
+ case AKEYCODE_MEDIA_PAUSE:
+ case AKEYCODE_MEDIA_PLAY_PAUSE:
+ case AKEYCODE_MUTE:
+ case AKEYCODE_HEADSETHOOK:
+ case AKEYCODE_MEDIA_STOP:
+ case AKEYCODE_MEDIA_NEXT:
+ case AKEYCODE_MEDIA_PREVIOUS:
+ case AKEYCODE_MEDIA_REWIND:
+ case AKEYCODE_MEDIA_RECORD:
+ case AKEYCODE_MEDIA_FAST_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_FORWARD:
+ case AKEYCODE_MEDIA_SKIP_BACKWARD:
+ case AKEYCODE_MEDIA_STEP_FORWARD:
+ case AKEYCODE_MEDIA_STEP_BACKWARD:
+ case AKEYCODE_MEDIA_AUDIO_TRACK:
+ case AKEYCODE_VOLUME_UP:
+ case AKEYCODE_VOLUME_DOWN:
+ case AKEYCODE_VOLUME_MUTE:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP:
+ case AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN:
+ return true;
+ default:
+ return false;
+ }
+}
+
// --- KeyboardInputMapper ---
KeyboardInputMapper::KeyboardInputMapper(InputDeviceContext& deviceContext,
@@ -301,7 +331,8 @@
// For internal keyboards and devices for which the default wake behavior is explicitly
// prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
// wake key individually.
- if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault) {
+ if (down && getDeviceContext().isExternal() && !mParameters.doNotWakeByDefault &&
+ !(mKeyboardType != AINPUT_KEYBOARD_TYPE_ALPHABETIC && isMediaKey(keyCode))) {
policyFlags |= POLICY_FLAG_WAKE;
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 2dd05f5..0c58dab 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -35,12 +35,8 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
- // The evdev multi-touch protocol does not allow userspace applications to query the initial or
- // current state of the pointers at any time. This means if we clear our accumulated state when
- // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
- // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
- // rebuilding the state from scratch, we work around this kernel API limitation by never
- // fully clearing any state specific to the multi-touch protocol.
+ mPointerIdBits.clear();
+ mMultiTouchMotionAccumulator.reset(mDeviceContext);
return TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 255f02d..6f697db 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -399,6 +399,8 @@
: 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/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0fc903..b3f1700 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -30,24 +30,12 @@
size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
+ populateCurrentSlot(deviceContext);
+}
- mCurrentSlot = -1;
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot before we
- // start reading events from the device. It is possible that the current slot index will
- // not be the same as it was when the first event was written into the evdev buffer, which
- // means the input mapper could start out of sync with the initial state of the events in
- // the evdev buffer. In the extremely unlikely case that this happens, the data from two
- // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
- // touch point to "jump", but at least there will be no stuck touches.
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
- } else {
- ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
- }
- }
+void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
+ resetSlots();
+ syncSlots(deviceContext);
}
void MultiTouchMotionAccumulator::resetSlots() {
@@ -84,54 +72,10 @@
if (!mUsingSlotsProtocol) {
slot.mInUse = true;
}
-
- switch (rawEvent->code) {
- case ABS_MT_POSITION_X:
- slot.mAbsMtPositionX = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_POSITION_Y:
- slot.mAbsMtPositionY = rawEvent->value;
- warnIfNotInUse(*rawEvent, slot);
- break;
- case ABS_MT_TOUCH_MAJOR:
- slot.mAbsMtTouchMajor = rawEvent->value;
- break;
- case ABS_MT_TOUCH_MINOR:
- slot.mAbsMtTouchMinor = rawEvent->value;
- slot.mHaveAbsMtTouchMinor = true;
- break;
- case ABS_MT_WIDTH_MAJOR:
- slot.mAbsMtWidthMajor = rawEvent->value;
- break;
- case ABS_MT_WIDTH_MINOR:
- slot.mAbsMtWidthMinor = rawEvent->value;
- slot.mHaveAbsMtWidthMinor = true;
- break;
- case ABS_MT_ORIENTATION:
- slot.mAbsMtOrientation = rawEvent->value;
- break;
- case ABS_MT_TRACKING_ID:
- if (mUsingSlotsProtocol && rawEvent->value < 0) {
- // The slot is no longer in use but it retains its previous contents,
- // which may be reused for subsequent touches.
- slot.mInUse = false;
- } else {
- slot.mInUse = true;
- slot.mAbsMtTrackingId = rawEvent->value;
- }
- break;
- case ABS_MT_PRESSURE:
- slot.mAbsMtPressure = rawEvent->value;
- break;
- case ABS_MT_DISTANCE:
- slot.mAbsMtDistance = rawEvent->value;
- break;
- case ABS_MT_TOOL_TYPE:
- slot.mAbsMtToolType = rawEvent->value;
- slot.mHaveAbsMtToolType = true;
- break;
+ if (rawEvent->code == ABS_MT_POSITION_X || rawEvent->code == ABS_MT_POSITION_Y) {
+ warnIfNotInUse(*rawEvent, slot);
}
+ slot.populateAxisValue(rawEvent->code, rawEvent->value);
}
} else if (rawEvent->type == EV_SYN && rawEvent->code == SYN_MT_REPORT) {
// MultiTouch Sync: The driver has returned all data for *one* of the pointers.
@@ -139,6 +83,36 @@
}
}
+void MultiTouchMotionAccumulator::syncSlots(const InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ constexpr std::array<int32_t, 11> axisCodes = {ABS_MT_POSITION_X, ABS_MT_POSITION_Y,
+ ABS_MT_TOUCH_MAJOR, ABS_MT_TOUCH_MINOR,
+ ABS_MT_WIDTH_MAJOR, ABS_MT_WIDTH_MINOR,
+ ABS_MT_ORIENTATION, ABS_MT_TRACKING_ID,
+ ABS_MT_PRESSURE, ABS_MT_DISTANCE,
+ ABS_MT_TOOL_TYPE};
+ const size_t numSlots = mSlots.size();
+ for (int32_t axisCode : axisCodes) {
+ if (!deviceContext.hasAbsoluteAxis(axisCode)) {
+ continue;
+ }
+ const auto result = deviceContext.getMtSlotValues(axisCode, numSlots);
+ if (result.ok()) {
+ const std::vector<int32_t>& mtSlotValues = result.value();
+ for (size_t i = 1; i <= numSlots; ++i) {
+ // The returned slot values are in a 1-indexed vector of size numSlots + 1.
+ mSlots[i - 1].populateAxisValue(axisCode, mtSlotValues[i]);
+ }
+ } else {
+ ALOGE("Could not retrieve multi-touch slot value for axis=%d error=%s status=%d",
+ axisCode, result.error().message().c_str(), result.error().code().value());
+ }
+ }
+ populateCurrentSlot(deviceContext);
+}
+
void MultiTouchMotionAccumulator::finishSync() {
if (!mUsingSlotsProtocol) {
resetSlots();
@@ -160,6 +134,21 @@
[](const Slot& slot) { return slot.mInUse; });
}
+void MultiTouchMotionAccumulator::populateCurrentSlot(
+ const android::InputDeviceContext& deviceContext) {
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGE("Could not retrieve current multi-touch slot index. status=%s",
+ statusToString(status).c_str());
+ }
+}
+
// --- MultiTouchMotionAccumulator::Slot ---
ToolType MultiTouchMotionAccumulator::Slot::getToolType() const {
@@ -176,4 +165,52 @@
return ToolType::UNKNOWN;
}
+void MultiTouchMotionAccumulator::Slot::populateAxisValue(int32_t axisCode, int32_t value) {
+ switch (axisCode) {
+ case ABS_MT_POSITION_X:
+ mAbsMtPositionX = value;
+ break;
+ case ABS_MT_POSITION_Y:
+ mAbsMtPositionY = value;
+ break;
+ case ABS_MT_TOUCH_MAJOR:
+ mAbsMtTouchMajor = value;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ mAbsMtTouchMinor = value;
+ mHaveAbsMtTouchMinor = true;
+ break;
+ case ABS_MT_WIDTH_MAJOR:
+ mAbsMtWidthMajor = value;
+ break;
+ case ABS_MT_WIDTH_MINOR:
+ mAbsMtWidthMinor = value;
+ mHaveAbsMtWidthMinor = true;
+ break;
+ case ABS_MT_ORIENTATION:
+ mAbsMtOrientation = value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ if (value < 0) {
+ // The slot is no longer in use but it retains its previous contents,
+ // which may be reused for subsequent touches.
+ mInUse = false;
+ } else {
+ mInUse = true;
+ mAbsMtTrackingId = value;
+ }
+ break;
+ case ABS_MT_PRESSURE:
+ mAbsMtPressure = value;
+ break;
+ case ABS_MT_DISTANCE:
+ mAbsMtDistance = value;
+ break;
+ case ABS_MT_TOOL_TYPE:
+ mAbsMtToolType = value;
+ mHaveAbsMtToolType = true;
+ break;
+ }
+}
+
} // namespace android
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 0e3e2bb..a0f2147 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -68,12 +68,14 @@
int32_t mAbsMtToolType = 0;
void clear() { *this = Slot(); }
+ void populateAxisValue(int32_t axisCode, int32_t value);
};
MultiTouchMotionAccumulator();
void configure(const InputDeviceContext& deviceContext, size_t slotCount,
bool usingSlotsProtocol);
+ void reset(const InputDeviceContext& deviceContext);
void process(const RawEvent* rawEvent);
void finishSync();
@@ -85,12 +87,14 @@
}
private:
- int32_t mCurrentSlot;
+ int32_t mCurrentSlot{-1};
std::vector<Slot> mSlots;
bool mUsingSlotsProtocol;
void resetSlots();
+ void syncSlots(const InputDeviceContext& deviceContext);
void warnIfNotInUse(const RawEvent& event, const Slot& slot);
+ void populateCurrentSlot(const android::InputDeviceContext& deviceContext);
};
} // namespace android
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 01e983a..19788ce 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -81,6 +81,7 @@
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> 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 88e7b99..07cc56c 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -75,7 +75,7 @@
[[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 @@
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 @@
// 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/Android.bp b/services/inputflinger/tests/Android.bp
index db31ded..55aa226 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -68,6 +68,7 @@
"TimerProvider_test.cpp",
"TestInputListener.cpp",
"TouchpadInputMapper_test.cpp",
+ "MultiTouchInputMapper_test.cpp",
"KeyboardInputMapper_test.cpp",
"UinputDevice.cpp",
"UnwantedInteractionBlocker_test.cpp",
diff --git a/services/inputflinger/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index 6d6b7d8..66c3256 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -16,14 +16,26 @@
#include "CursorInputMapper.h"
+#include <list>
+#include <string>
+#include <tuple>
+#include <variant>
+
#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"
+#include "ui/Rotation.h"
#define TAG "CursorInputMapper_test"
@@ -40,23 +52,95 @@
constexpr auto HOVER_MOVE = AMOTION_EVENT_ACTION_HOVER_MOVE;
constexpr auto INVALID_CURSOR_POSITION = AMOTION_EVENT_INVALID_CURSOR_POSITION;
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;
/**
* Unit tests for CursorInputMapper.
- * This class is named 'CursorInputMapperUnitTest' to avoid name collision with the existing
- * 'CursorInputMapperTest'. If all of the CursorInputMapper tests are migrated here, the name
- * can be simplified to 'CursorInputMapperTest'.
- * TODO(b/283812079): move CursorInputMapper tests here.
+ * These classes are named 'CursorInputMapperUnitTest...' to avoid name collision with the existing
+ * 'CursorInputMapperTest...' classes. If all of the CursorInputMapper tests are migrated here, the
+ * name can be simplified to 'CursorInputMapperTest'.
+ *
+ * TODO(b/283812079): move the remaining CursorInputMapper tests here. The ones that are left all
+ * depend on viewport association, for which we'll need to fake InputDeviceContext.
*/
-class CursorInputMapperUnitTest : public InputMapperUnitTest {
+class CursorInputMapperUnitTestBase : public InputMapperUnitTest {
protected:
- void SetUp() override {
- InputMapperUnitTest::SetUp();
+ void SetUp() override { SetUpWithBus(BUS_USB); }
+ void SetUpWithBus(int bus) override {
+ InputMapperUnitTest::SetUpWithBus(bus);
// Current scan code state - all keys are UP by default
setScanCodeState(KeyState::UP,
@@ -68,10 +152,11 @@
.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() {
+ createDevice();
mMapper = createInputMapper<CursorInputMapper>(*mDeviceContext, mReaderConfiguration);
}
@@ -83,19 +168,55 @@
mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
InputReaderConfiguration::Change::POINTER_CAPTURE);
ASSERT_THAT(args,
- ElementsAre(
- VariantWith<NotifyDeviceResetArgs>(AllOf(WithDeviceId(DEVICE_ID)))));
+ ElementsAre(VariantWith<NotifyDeviceResetArgs>(
+ AllOf(WithDeviceId(DEVICE_ID), WithEventTime(ARBITRARY_TIME)))));
// 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 {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ CursorInputMapperUnitTestBase::SetUp();
+ }
+};
+
+TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsMouseInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ ASSERT_EQ(AINPUT_SOURCE_MOUSE, mMapper->getSources());
+}
+
+TEST_F(CursorInputMapperUnitTest, GetSourcesReturnsTrackballInNavigationMode) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mMapper->getSources());
+}
+
/**
* Move the mouse and then click the button. Check whether HOVER_EXIT is generated when hovering
* ends. Currently, it is not.
*/
TEST_F(CursorInputMapperUnitTest, HoverAndLeftButtonPress) {
+ createMapper();
std::list<NotifyArgs> args;
// Move the cursor a little
@@ -139,6 +260,7 @@
* When it's not SOURCE_MOUSE, CursorInputMapper doesn't populate cursor position values.
*/
TEST_F(CursorInputMapperUnitTest, ProcessPointerCapture) {
+ createMapper();
setPointerCapture(true);
std::list<NotifyArgs> args;
@@ -213,4 +335,1192 @@
WithRelativeMotion(10.0f, 20.0f)))));
}
+TEST_F(CursorInputMapperUnitTest,
+ PopulateDeviceInfoReturnsRangeFromPointerControllerInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ mFakePolicy->clearViewports();
+ mFakePointerController->clearBounds();
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ // Initially there should not be a valid motion range because there's no viewport or pointer
+ // bounds.
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+
+ // When the bounds are set, then there should be a valid motion range.
+ mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(systemTime(), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ InputDeviceInfo info2;
+ mMapper->populateDeviceInfo(info2);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 1,
+ 800 - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 2,
+ 480 - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperUnitTest, PopulateDeviceInfoReturnsScaledRangeInNavigationMode) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
+ -1.0f, 1.0f, 0.0f,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
+ -1.0f, 1.0f, 0.0f,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_TRACKBALL, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldSetAllFieldsAndIncludeGlobalMetaState) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ EXPECT_CALL(mMockInputReaderContext, getGlobalMetaState())
+ .WillRepeatedly(Return(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON));
+
+ std::list<NotifyArgs> args;
+
+ // Button press.
+ // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPointerCount(1), WithPointerId(0, 0),
+ WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+ WithPressure(1.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME), WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithPointerCount(1), WithPointerId(0, 0),
+ WithToolType(ToolType::MOUSE), WithCoords(0.0f, 0.0f),
+ WithPressure(1.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
+ args.clear();
+
+ // Button release. Should have same down time.
+ args += process(ARBITRARY_TIME + 1, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME + 1, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME + 1),
+ WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(0), WithPointerCount(1),
+ WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithEventTime(ARBITRARY_TIME + 1),
+ WithDeviceId(DEVICE_ID),
+ WithSource(AINPUT_SOURCE_TRACKBALL), WithFlags(0),
+ WithEdgeFlags(0), WithPolicyFlags(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON),
+ WithButtonState(0), WithPointerCount(1),
+ WithPointerId(0, 0), WithToolType(ToolType::MOUSE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f),
+ WithPrecision(TRACKBALL_MOVEMENT_THRESHOLD,
+ TRACKBALL_MOVEMENT_THRESHOLD),
+ WithDownTime(ARBITRARY_TIME)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentXYUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Motion in X but not Y.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // Motion in Y but not X.
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(0.0f, -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(0.0f)))));
+ args.clear();
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleIndependentButtonUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Button press.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ // Button release.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleCombinedXYAndButtonUpdates) {
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Combined X, Y and Button.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, -2);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithCoords(1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ -2.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f)))));
+ args.clear();
+
+ // Move X, Y a bit while pressed.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 2);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithCoords(2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
+ 1.0f / TRACKBALL_MOVEMENT_THRESHOLD),
+ WithPressure(1.0f)))));
+ args.clear();
+
+ // Release Button.
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ 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();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ // press BTN_LEFT, release BTN_LEFT
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(100.0f, 200.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
+}
+
+class CursorInputMapperButtonKeyTest
+ : public CursorInputMapperUnitTest,
+ public testing::WithParamInterface<
+ std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
+ int32_t /*expectedKeyCode*/>> {};
+
+TEST_P(CursorInputMapperButtonKeyTest, ProcessShouldHandleButtonKey) {
+ auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithKeyCode(expectedKeyCode))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(expectedButtonState),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(expectedButtonState),
+ WithCoords(100.0f, 200.0f), WithPressure(0.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(100.0f, 200.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+ WithKeyCode(expectedKeyCode)))));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SideExtraBackAndForward, CursorInputMapperButtonKeyTest,
+ testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
+ std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
+ AKEYCODE_FORWARD)));
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldMoveThePointerAroundInPointerMode) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(110.0f, 220.0f), WithPressure(0.0f), WithSize(0.0f),
+ WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
+ WithOrientation(0.0f), WithDistance(0.0f)))));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
+}
+
+/**
+ * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
+ * pointer acceleration or speed processing should not be applied.
+ */
+TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesVelocityProcessing) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+ /*highThreshold=*/100.f, /*acceleration=*/10.f);
+ mReaderConfiguration.pointerVelocityControlParameters = testParams;
+ mFakePolicy->setVelocityControlParams(testParams);
+ createMapper();
+
+ std::list<NotifyArgs> args;
+
+ // Move and verify scale is 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+ NotifyMotionArgs motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_GT(relX, 10);
+ ASSERT_GT(relY, 20);
+ args.clear();
+
+ // Enable Pointer Capture
+ setPointerCapture(true);
+
+ // Move and verify scale 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE), WithCoords(10, 20)))));
+}
+
+// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
+// logic can be removed.
+class CursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ CursorInputMapperUnitTestBase::SetUp();
+ }
+};
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, PopulateDeviceInfoReturnsRangeFromPolicy) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ mFakePolicy->clearViewports();
+ mFakePointerController->clearBounds();
+ createMapper();
+
+ InputDeviceInfo info;
+ mMapper->populateDeviceInfo(info);
+
+ // Initially there should not be a valid motion range because there's no viewport or pointer
+ // bounds.
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
+ ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+
+ // When the viewport and the default pointer display ID is set, then there should be a valid
+ // motion range.
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(systemTime(), mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_THAT(args, testing::IsEmpty());
+
+ InputDeviceInfo info2;
+ mMapper->populateDeviceInfo(info2);
+
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0,
+ DISPLAY_WIDTH - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0,
+ DISPLAY_HEIGHT - 1, 0.0f, 0.0f));
+ ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
+ AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
+}
+
+TEST_F(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();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ // press BTN_LEFT, release BTN_LEFT
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_LEFT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f)))));
+ args.clear();
+
+ // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 1);
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY |
+ AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_RIGHT, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_MOVE),
+ WithButtonState(AMOTION_EVENT_BUTTON_TERTIARY),
+ WithCoords(0.0f, 0.0f), WithPressure(1.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, BTN_MIDDLE, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithButtonState(0),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+}
+
+class CursorInputMapperButtonKeyTestWithChoreographer
+ : public CursorInputMapperUnitTestWithChoreographer,
+ public testing::WithParamInterface<
+ std::tuple<int32_t /*evdevCode*/, int32_t /*expectedButtonState*/,
+ int32_t /*expectedKeyCode*/>> {};
+
+TEST_P(CursorInputMapperButtonKeyTestWithChoreographer,
+ ProcessShouldHandleButtonKeyWithZeroCoords) {
+ auto [evdevCode, expectedButtonState, expectedKeyCode] = GetParam();
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ std::list<NotifyArgs> args;
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 1);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_DOWN),
+ WithKeyCode(expectedKeyCode))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(expectedButtonState),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS),
+ WithButtonState(expectedButtonState),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f)))));
+ args.clear();
+
+ args += process(ARBITRARY_TIME, EV_KEY, evdevCode, 0);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithButtonState(0), WithCoords(0.0f, 0.0f),
+ WithPressure(0.0f))),
+ VariantWith<NotifyKeyArgs>(AllOf(WithKeyAction(AKEY_EVENT_ACTION_UP),
+ WithKeyCode(expectedKeyCode)))));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ SideExtraBackAndForward, CursorInputMapperButtonKeyTestWithChoreographer,
+ testing::Values(std::make_tuple(BTN_SIDE, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_EXTRA, AMOTION_EVENT_BUTTON_FORWARD, AKEYCODE_FORWARD),
+ std::make_tuple(BTN_BACK, AMOTION_EVENT_BUTTON_BACK, AKEYCODE_BACK),
+ std::make_tuple(BTN_FORWARD, AMOTION_EVENT_BUTTON_FORWARD,
+ AKEYCODE_FORWARD)));
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithCoords(0.0f, 0.0f), WithPressure(0.0f), WithSize(0.0f),
+ WithTouchDimensions(0.0f, 0.0f), WithToolDimensions(0.0f, 0.0f),
+ WithOrientation(0.0f), WithDistance(0.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
+ /*highThreshold=*/100.f, /*acceleration=*/10.f);
+ mReaderConfiguration.pointerVelocityControlParameters = testParams;
+ mFakePolicy->setVelocityControlParams(testParams);
+ createMapper();
+
+ NotifyMotionArgs motionArgs;
+ std::list<NotifyArgs> args;
+
+ // Move and verify scale is 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE),
+ WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE)))));
+ motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_GT(relX, 10);
+ ASSERT_GT(relY, 20);
+ args.clear();
+
+ // Enable Pointer Capture
+ setPointerCapture(true);
+
+ // Move and verify scale 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)))));
+ motionArgs = std::get<NotifyMotionArgs>(args.front());
+ const float relX2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
+ const float relY2 = motionArgs.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
+ ASSERT_EQ(10, relX2);
+ ASSERT_EQ(20, relY2);
+}
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
+ // Set up the default display.
+ mFakePolicy->clearViewports();
+ 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(createSecondaryViewport());
+ mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
+
+ createMapper();
+
+ mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
+ mFakePointerController->setPosition(100, 200);
+
+ // Ensure input events are generated without display ID or coords, because they will be decided
+ // later by PointerChoreographer.
+ 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);
+ EXPECT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
+ WithCoords(0.0f, 0.0f)))));
+}
+
+namespace {
+
+// Minimum timestamp separation between subsequent input events from a Bluetooth device.
+constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
+// Maximum smoothing time delta so that we don't generate events too far into the future.
+constexpr nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
+
+} // namespace
+
+class BluetoothCursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(false);
+ SetUpWithBus(BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmoothening) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // Process several events that come in quick succession, according to their timestamps.
+ for (int i = 0; i < 3; i++) {
+ constexpr static nsecs_t delta = ms2ns(1);
+ static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
+ kernelEventTime += delta;
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningIsCapped) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // Process several events with the same timestamp from the kernel.
+ // Ensure that we do not generate events too far into the future.
+ constexpr static int32_t numEvents =
+ MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
+ for (int i = 0; i < numEvents; i++) {
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+
+ // By processing more events with the same timestamp, we should not generate events with a
+ // timestamp that is more than the specified max time delta from the timestamp at its injection.
+ const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
+ for (int i = 0; i < 3; i++) {
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTest, TimestampSmootheningNotUsed) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
+ // smoothening is not needed, its timestamp is not affected.
+ kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
+ expectedEventTime = kernelEventTime;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+}
+
+// --- BluetoothCursorInputMapperUnitTestWithChoreographer ---
+
+class BluetoothCursorInputMapperUnitTestWithChoreographer : public CursorInputMapperUnitTestBase {
+protected:
+ void SetUp() override {
+ input_flags::enable_pointer_choreographer(true);
+ SetUpWithBus(BUS_BLUETOOTH);
+
+ mFakePointerController = std::make_shared<FakePointerController>();
+ mFakePolicy->setPointerController(mFakePointerController);
+ }
+};
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmoothening) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // Process several events that come in quick succession, according to their timestamps.
+ for (int i = 0; i < 3; i++) {
+ constexpr static nsecs_t delta = ms2ns(1);
+ static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
+ kernelEventTime += delta;
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningIsCapped) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // Process several events with the same timestamp from the kernel.
+ // Ensure that we do not generate events too far into the future.
+ constexpr static int32_t numEvents =
+ MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
+ for (int i = 0; i < numEvents; i++) {
+ expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
+
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+ }
+
+ // By processing more events with the same timestamp, we should not generate events with a
+ // timestamp that is more than the specified max time delta from the timestamp at its injection.
+ const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
+ for (int i = 0; i < 3; i++) {
+ argsList += process(ARBITRARY_TIME, EV_REL, REL_X, 1);
+ argsList += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(cappedEventTime)))));
+ argsList.clear();
+ }
+}
+
+TEST_F(BluetoothCursorInputMapperUnitTestWithChoreographer, TimestampSmootheningNotUsed) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ createMapper();
+ std::list<NotifyArgs> argsList;
+
+ nsecs_t kernelEventTime = ARBITRARY_TIME;
+ nsecs_t expectedEventTime = ARBITRARY_TIME;
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+
+ // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
+ // smoothening is not needed, its timestamp is not affected.
+ kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
+ expectedEventTime = kernelEventTime;
+
+ argsList += process(kernelEventTime, EV_REL, REL_X, 1);
+ argsList += process(kernelEventTime, EV_SYN, SYN_REPORT, 0);
+ EXPECT_THAT(argsList,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
+ WithEventTime(expectedEventTime)))));
+ argsList.clear();
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/FakeEventHub.cpp b/services/inputflinger/tests/FakeEventHub.cpp
index 212fceb..daa000f 100644
--- a/services/inputflinger/tests/FakeEventHub.cpp
+++ b/services/inputflinger/tests/FakeEventHub.cpp
@@ -431,6 +431,38 @@
return -1;
}
+void FakeEventHub::setMtSlotValues(int32_t deviceId, int32_t axis,
+ const std::vector<int32_t>& values) {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ FAIL() << "Missing device";
+ }
+ device->mtSlotValues[axis] = values;
+}
+
+base::Result<std::vector<int32_t>> FakeEventHub::getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const {
+ Device* device = getDevice(deviceId);
+ if (!device) {
+ ADD_FAILURE() << "Missing device";
+ return base::ResultError("Missing device", UNKNOWN_ERROR);
+ }
+ const auto& mtSlotValuesIterator = device->mtSlotValues.find(axis);
+ if (mtSlotValuesIterator == device->mtSlotValues.end()) {
+ return base::ResultError("axis not supported", NAME_NOT_FOUND);
+ }
+ const auto& mtSlotValues = mtSlotValuesIterator->second;
+ if (mtSlotValues.size() != slotCount) {
+ ADD_FAILURE() << "MtSlot values specified for " << mtSlotValues.size()
+ << " slots but expected for " << slotCount << " Slots";
+ return base::ResultError("Slot count mismatch", NAME_NOT_FOUND);
+ }
+ std::vector<int32_t> outValues(slotCount + 1);
+ outValues[0] = axis;
+ std::copy(mtSlotValues.begin(), mtSlotValues.end(), outValues.begin() + 1);
+ return std::move(outValues);
+}
+
int32_t FakeEventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKeyCode) const {
Device* device = getDevice(deviceId);
if (!device) {
diff --git a/services/inputflinger/tests/FakeEventHub.h b/services/inputflinger/tests/FakeEventHub.h
index 8e06940..f07b344 100644
--- a/services/inputflinger/tests/FakeEventHub.h
+++ b/services/inputflinger/tests/FakeEventHub.h
@@ -65,6 +65,7 @@
bool enabled;
std::optional<RawLayoutInfo> layoutInfo;
std::string sysfsRootPath;
+ std::unordered_map<int32_t, std::vector<int32_t>> mtSlotValues;
status_t enable() {
enabled = true;
@@ -154,6 +155,11 @@
int32_t value);
void assertQueueIsEmpty();
void setSysfsRootPath(int32_t deviceId, std::string sysfsRootPath) const;
+ // Populate fake slot values to be returned by the getter, size of the values should be equal to
+ // the slot count
+ void setMtSlotValues(int32_t deviceId, int32_t axis, const std::vector<int32_t>& values);
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override;
private:
Device* getDevice(int32_t deviceId) const;
diff --git a/services/inputflinger/tests/FakePointerController.cpp b/services/inputflinger/tests/FakePointerController.cpp
index 80319f2..31e1173 100644
--- a/services/inputflinger/tests/FakePointerController.cpp
+++ b/services/inputflinger/tests/FakePointerController.cpp
@@ -28,6 +28,10 @@
mMaxY = maxY;
}
+void FakePointerController::clearBounds() {
+ mHaveBounds = false;
+}
+
const std::map<int32_t, std::vector<int32_t>>& FakePointerController::getSpots() {
return mSpotsByDisplay;
}
diff --git a/services/inputflinger/tests/FakePointerController.h b/services/inputflinger/tests/FakePointerController.h
index 800f864..061ae62 100644
--- a/services/inputflinger/tests/FakePointerController.h
+++ b/services/inputflinger/tests/FakePointerController.h
@@ -33,6 +33,7 @@
virtual ~FakePointerController() {}
void setBounds(float minX, float minY, float maxX, float maxY);
+ void clearBounds();
const std::map<int32_t, std::vector<int32_t>>& getSpots();
void setPosition(float x, float y) override;
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index 69772af..1630769 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -115,12 +115,32 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
/* 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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
/* 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 @@
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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
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 c92736e..48d7e55 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1093,9 +1093,10 @@
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 @@
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- mInfo.token = *token;
+ mInfo.token = token;
mInfo.id = sId++;
mInfo.name = name;
mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
@@ -1679,6 +1680,24 @@
window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
+using InputDispatcherDeathTest = InputDispatcherTest;
+
+/**
+ * When 'onWindowInfosChanged' arguments contain a duplicate entry for the same window, dispatcher
+ * should crash.
+ */
+TEST_F(InputDispatcherDeathTest, DuplicateWindowInfosAbortDispatcher) {
+ testing::GTEST_FLAG(death_test_style) = "threadsafe";
+ ScopedSilentDeath _silentDeath;
+
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
+ "Fake Window", ADISPLAY_ID_DEFAULT);
+ ASSERT_DEATH(mDispatcher->onWindowInfosChanged(
+ {{*window->getInfo(), *window->getInfo()}, {}, 0, 0}),
+ "Incorrect WindowInfosUpdate provided");
+}
+
TEST_F(InputDispatcherTest, WhenDisplayNotSpecified_InjectMotionToDefaultDisplay) {
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher,
@@ -2370,7 +2389,7 @@
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 +2438,7 @@
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);
@@ -6973,6 +6992,39 @@
mWindow->assertNoEvents();
}
+TEST_F(InputDispatcherFallbackKeyTest, WindowRemovedDuringPolicyCall) {
+ setFallback(AKEYCODE_B);
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+
+ // Do not handle this key event.
+ consumeKey(/*handled=*/false,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_DOWN), WithKeyCode(AKEYCODE_B),
+ WithFlags(AKEY_EVENT_FLAG_FALLBACK)));
+
+ mFakePolicy->setUnhandledKeyHandler([&](const KeyEvent& event) {
+ // When the unhandled key is reported to the policy next, remove the input channel.
+ mDispatcher->removeInputChannel(mWindow->getToken());
+ return KeyEventBuilder(event).keyCode(AKEYCODE_B).build();
+ });
+ // Release the original key, and let the app now handle the previously unhandled key.
+ // This should result in the previously generated fallback key to be cancelled.
+ // Since the policy was notified of the unhandled DOWN event earlier, it will also be notified
+ // of the UP event for consistency. The Dispatcher calls into the policy from its own thread
+ // without holding the lock, because it need to synchronously fetch the fallback key. While in
+ // the policy call, we will now remove the input channel. Once the policy call returns, the
+ // Dispatcher will no longer have a channel to send cancellation events to. Ensure this does
+ // not cause any crashes.
+ mDispatcher->notifyKey(
+ KeyArgsBuilder(ACTION_UP, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build());
+ consumeKey(/*handled=*/true,
+ AllOf(WithKeyAction(ACTION_UP), WithKeyCode(AKEYCODE_A), WithFlags(0)));
+ ASSERT_NO_FATAL_FAILURE(mFakePolicy->assertUnhandledKeyReported(AKEYCODE_A));
+}
+
class InputDispatcherKeyRepeatTest : public InputDispatcherTest {
protected:
static constexpr std::chrono::nanoseconds KEY_REPEAT_TIMEOUT = 40ms;
@@ -7332,6 +7384,94 @@
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 +7794,7 @@
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 +9061,7 @@
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 +9129,7 @@
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 36be684..2aecab9 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -19,12 +19,16 @@
#include <InputReaderBase.h>
#include <gtest/gtest.h>
#include <ui/Rotation.h>
+#include <utils/Timers.h>
+
+#include "NotifyArgs.h"
namespace android {
+using testing::_;
using testing::Return;
-void InputMapperUnitTest::SetUp() {
+void InputMapperUnitTest::SetUpWithBus(int bus) {
mFakePointerController = std::make_shared<FakePointerController>();
mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
mFakePointerController->setPosition(INITIAL_CURSOR_X, INITIAL_CURSOR_Y);
@@ -36,20 +40,30 @@
EXPECT_CALL(mMockInputReaderContext, getPolicy()).WillRepeatedly(Return(mFakePolicy.get()));
EXPECT_CALL(mMockInputReaderContext, getEventHub()).WillRepeatedly(Return(&mMockEventHub));
- InputDeviceIdentifier identifier;
- identifier.name = "device";
- identifier.location = "USB1";
- identifier.bus = 0;
- EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID)).WillRepeatedly(Return(identifier));
+ mIdentifier.name = "device";
+ mIdentifier.location = "USB1";
+ mIdentifier.bus = bus;
+ EXPECT_CALL(mMockEventHub, getDeviceIdentifier(EVENTHUB_ID))
+ .WillRepeatedly(Return(mIdentifier));
+ EXPECT_CALL(mMockEventHub, getConfiguration(EVENTHUB_ID)).WillRepeatedly([&](int32_t) {
+ return mPropertyMap;
+ });
+}
+
+void InputMapperUnitTest::createDevice() {
mDevice = std::make_unique<InputDevice>(&mMockInputReaderContext, DEVICE_ID,
- /*generation=*/2, identifier);
+ /*generation=*/2, mIdentifier);
+ mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
+ 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;
@@ -215,8 +229,8 @@
return generatedArgs;
}
-void InputMapperTest::assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz) {
+void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min,
+ float max, float flat, float fuzz) {
const InputDeviceInfo::MotionRange* range = info.getMotionRange(axis, source);
ASSERT_TRUE(range != nullptr) << "Axis: " << axis << " Source: " << source;
ASSERT_EQ(axis, range->axis) << "Axis: " << axis << " Source: " << source;
@@ -227,11 +241,9 @@
ASSERT_NEAR(fuzz, range->fuzz, EPSILON) << "Axis: " << axis << " Source: " << source;
}
-void InputMapperTest::assertPointerCoords(const PointerCoords& coords, float x, float y,
- float pressure, float size, float touchMajor,
- float touchMinor, float toolMajor, float toolMinor,
- float orientation, float distance,
- float scaledAxisEpsilon) {
+void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size,
+ float touchMajor, float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance, float scaledAxisEpsilon) {
ASSERT_NEAR(x, coords.getAxisValue(AMOTION_EVENT_AXIS_X), scaledAxisEpsilon);
ASSERT_NEAR(y, coords.getAxisValue(AMOTION_EVENT_AXIS_Y), scaledAxisEpsilon);
ASSERT_NEAR(pressure, coords.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), EPSILON);
diff --git a/services/inputflinger/tests/InputMapperTest.h b/services/inputflinger/tests/InputMapperTest.h
index 05b0e97..e176a65 100644
--- a/services/inputflinger/tests/InputMapperTest.h
+++ b/services/inputflinger/tests/InputMapperTest.h
@@ -32,6 +32,7 @@
#include "InterfaceMocks.h"
#include "TestConstants.h"
#include "TestInputListener.h"
+#include "input/PropertyMap.h"
namespace android {
@@ -41,7 +42,15 @@
static constexpr int32_t DEVICE_ID = END_RESERVED_ID + 1000;
static constexpr float INITIAL_CURSOR_X = 400;
static constexpr float INITIAL_CURSOR_Y = 240;
- virtual void SetUp() override;
+ virtual void SetUp() override { SetUpWithBus(0); }
+ virtual void SetUpWithBus(int bus);
+
+ /**
+ * Initializes mDevice and mDeviceContext. When this happens, mDevice takes a copy of
+ * mPropertyMap, so tests that need to set configuration properties should do so before calling
+ * this. Others will most likely want to call it in their SetUp method.
+ */
+ void createDevice();
void setupAxis(int axis, bool valid, int32_t min, int32_t max, int32_t resolution);
@@ -54,6 +63,7 @@
std::list<NotifyArgs> process(int32_t type, int32_t code, int32_t value);
std::list<NotifyArgs> process(nsecs_t when, int32_t type, int32_t code, int32_t value);
+ InputDeviceIdentifier mIdentifier;
MockEventHubInterface mMockEventHub;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakePointerController> mFakePointerController;
@@ -64,6 +74,7 @@
InputReaderConfiguration mReaderConfiguration;
// The mapper should be created by the subclasses.
std::unique_ptr<InputMapper> mMapper;
+ PropertyMap mPropertyMap;
};
/**
@@ -130,13 +141,13 @@
void resetMapper(InputMapper& mapper, nsecs_t when);
std::list<NotifyArgs> handleTimeout(InputMapper& mapper, nsecs_t when);
-
- static void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source,
- float min, float max, float flat, float fuzz);
- static void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure,
- float size, float touchMajor, float touchMinor, float toolMajor,
- float toolMinor, float orientation, float distance,
- float scaledAxisEpsilon = 1.f);
};
+void assertMotionRange(const InputDeviceInfo& info, int32_t axis, uint32_t source, float min,
+ float max, float flat, float fuzz);
+
+void assertPointerCoords(const PointerCoords& coords, float x, float y, float pressure, float size,
+ float touchMajor, float touchMinor, float toolMajor, float toolMinor,
+ float orientation, float distance, float scaledAxisEpsilon = 1.f);
+
} // namespace android
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c6536de..460a7b1 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -97,8 +97,6 @@
// Minimum timestamp separation between subsequent input events from a Bluetooth device.
static constexpr nsecs_t MIN_BLUETOOTH_TIMESTAMP_DELTA = ms2ns(4);
-// Maximum smoothing time delta so that we don't generate events too far into the future.
-constexpr static nsecs_t MAX_BLUETOOTH_SMOOTHING_DELTA = ms2ns(32);
namespace input_flags = com::android::input::flags;
@@ -1352,6 +1350,9 @@
std::shared_ptr<FakePointerController> mFakePointerController;
+ constexpr static auto EVENT_HAPPENED_TIMEOUT = 2000ms;
+ constexpr static auto EVENT_DID_NOT_HAPPEN_TIMEOUT = 30ms;
+
void SetUp() override {
#if !defined(__ANDROID__)
GTEST_SKIP();
@@ -1383,8 +1384,8 @@
}
void setupInputReader() {
- mTestListener = std::make_unique<TestInputListener>(/*eventHappenedTimeout=*/2000ms,
- /*eventDidNotHappenTimeout=*/30ms);
+ mTestListener = std::make_unique<TestInputListener>(EVENT_HAPPENED_TIMEOUT,
+ EVENT_DID_NOT_HAPPEN_TIMEOUT);
mReader = std::make_unique<InputReader>(std::make_shared<EventHub>(), mFakePolicy,
*mTestListener);
@@ -2415,17 +2416,29 @@
mDevice->sendTrackingId(FIRST_TRACKING_ID);
mDevice->sendToolType(MT_TOOL_FINGER);
mDevice->sendDown(centerPoint);
- auto waitUntil = std::chrono::system_clock::now() +
- std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT));
+ const auto syncTime = std::chrono::system_clock::now();
+ // After 72 ms, the event *will* be generated. If we wait the full 72 ms to check that NO event
+ // is generated in that period, there will be a race condition between the event being generated
+ // and the test's wait timeout expiring. Thus, we wait for a shorter duration in the test, which
+ // will reduce the liklihood of the race condition occurring.
+ const auto waitUntilTimeForNoEvent =
+ syncTime + std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT / 2));
mDevice->sendSync();
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntil));
+ ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasNotCalled(waitUntilTimeForNoEvent));
// Since the external stylus did not report a pressure value within the timeout,
// it shows up as a finger pointer.
- ASSERT_NO_FATAL_FAILURE(mTestListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN),
- WithSource(AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_STYLUS),
- WithToolType(ToolType::FINGER), WithDeviceId(touchscreenId), WithPressure(1.f))));
+ const auto waitUntilTimeForEvent = syncTime +
+ std::chrono::milliseconds(ns2ms(EXTERNAL_STYLUS_DATA_TIMEOUT)) + EVENT_HAPPENED_TIMEOUT;
+ ASSERT_NO_FATAL_FAILURE(
+ mTestListener->assertNotifyMotionWasCalled(AllOf(WithMotionAction(
+ AMOTION_EVENT_ACTION_DOWN),
+ WithSource(AINPUT_SOURCE_TOUCHSCREEN |
+ AINPUT_SOURCE_STYLUS),
+ WithToolType(ToolType::FINGER),
+ WithDeviceId(touchscreenId),
+ WithPressure(1.f)),
+ waitUntilTimeForEvent));
// Change the pressure on the external stylus. Since the pressure was not present at the start
// of the gesture, it is ignored for now.
@@ -2906,13 +2919,11 @@
mapper.assertProcessWasCalled();
// Simulate a kernel buffer overflow, which generates a SYN_DROPPED event.
- // This should reset the mapper.
event.type = EV_SYN;
event.code = SYN_DROPPED;
event.value = 0;
unused = mDevice->process(&event, /*count=*/1);
mapper.assertProcessWasNotCalled();
- mapper.assertResetWasCalled();
// All events until the next SYN_REPORT should be dropped.
event.type = EV_KEY;
@@ -2922,11 +2933,13 @@
mapper.assertProcessWasNotCalled();
// We get the SYN_REPORT event now, which is not forwarded to mappers.
+ // This should reset the mapper.
event.type = EV_SYN;
event.code = SYN_REPORT;
event.value = 0;
unused = mDevice->process(&event, /*count=*/1);
mapper.assertProcessWasNotCalled();
+ mapper.assertResetWasCalled();
// The mapper receives events normally now.
event.type = EV_KEY;
@@ -4087,7 +4100,7 @@
void SetUp() override { InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL); }
};
-TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior) {
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_AlphabeticKeyboard) {
// For external devices, keys will trigger wake on key down. Media keys should also trigger
// wake if triggered from external devices.
@@ -4126,6 +4139,36 @@
ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
}
+TEST_F(KeyboardInputMapperTest_ExternalDevice, WakeBehavior_NoneAlphabeticKeyboard) {
+ // For external devices, keys will trigger wake on key down. Media keys should not trigger
+ // wake if triggered from external non-alphaebtic keyboard (e.g. headsets).
+
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+ mFakeEventHub->addKey(EVENTHUB_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE,
+ POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper& mapper =
+ constructAndAddMapper<KeyboardInputMapper>(AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAY, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
TEST_F(KeyboardInputMapperTest_ExternalDevice, DoNotWakeByDefaultBehavior) {
// Tv Remote key's wake behavior is prescribed by the keylayout file.
@@ -4164,1492 +4207,6 @@
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, WhenModeIsPointer_GetSources_ReturnsMouse) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, mapper.getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsNavigation_GetSources_ReturnsTrackball) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, mapper.getSources());
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsPointer_PopulateDeviceInfo_ReturnsRangeFromPointerController) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- InputDeviceInfo info;
- mapper.populateDeviceInfo(info);
-
- // Initially there may not be a valid motion range.
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-
- // When the bounds are set, then there should be a valid motion range.
- mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- InputDeviceInfo info2;
- mapper.populateDeviceInfo(info2);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE,
- 1, 800 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE,
- 2, 480 - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_MOUSE,
- 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, WhenModeIsNavigation_PopulateDeviceInfo_ReturnsScaledRange) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- InputDeviceInfo info;
- mapper.populateDeviceInfo(info);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_TRACKBALL,
- -1.0f, 1.0f, 0.0f, 1.0f / TRACKBALL_MOVEMENT_THRESHOLD));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info,
- AINPUT_MOTION_RANGE_PRESSURE, AINPUT_SOURCE_TRACKBALL,
- 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldSetAllFieldsAndIncludeGlobalMetaState) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mReader->getContext()->setGlobalMetaState(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON);
-
- NotifyMotionArgs args;
-
- // Button press.
- // Mostly testing non x/y behavior here so we don't need to check again elsewhere.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- // Button release. Should have same down time.
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME + 1, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(0, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(ARBITRARY_TIME + 1, args.eventTime);
- ASSERT_EQ(DEVICE_ID, args.deviceId);
- ASSERT_EQ(AINPUT_SOURCE_TRACKBALL, args.source);
- ASSERT_EQ(uint32_t(0), args.policyFlags);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_EQ(0, args.flags);
- ASSERT_EQ(AMETA_SHIFT_LEFT_ON | AMETA_SHIFT_ON, args.metaState);
- ASSERT_EQ(0, args.buttonState);
- ASSERT_EQ(0, args.edgeFlags);
- ASSERT_EQ(uint32_t(1), args.getPointerCount());
- ASSERT_EQ(0, args.pointerProperties[0].id);
- ASSERT_EQ(ToolType::MOUSE, args.pointerProperties[0].toolType);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.xPrecision);
- ASSERT_EQ(TRACKBALL_MOVEMENT_THRESHOLD, args.yPrecision);
- ASSERT_EQ(ARBITRARY_TIME, args.downTime);
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentXYUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Motion in X but not Y.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f,
- 0.0f));
-
- // Motion in Y but not X.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
- 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], 0.0f,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleIndependentButtonUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Button press.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- // Button release.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTest, Process_ShouldHandleCombinedXYAndButtonUpdates) {
- addConfigurationProperty("cursor.mode", "navigation");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyMotionArgs args;
-
- // Combined X, Y and Button.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, -2);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0],
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- -2.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- // Move X, Y a bit while pressed.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 2);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 1);
- 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],
- 2.0f / TRACKBALL_MOVEMENT_THRESHOLD,
- 1.0f / TRACKBALL_MOVEMENT_THRESHOLD, 1.0f));
-
- // Release Button.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MOUSE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, args.action);
- ASSERT_NO_FATAL_FAILURE(assertCursorPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-}
-
-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, Process_ShouldHandleAllButtons) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- NotifyMotionArgs motionArgs;
- NotifyKeyArgs keyArgs;
-
- // press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- // press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 100.0f, 200.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-}
-
-TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerAround) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- NotifyMotionArgs args;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0],
- 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-/**
- * When Pointer Capture is enabled, we expect to report unprocessed relative movements, so any
- * pointer acceleration or speed processing should not be applied.
- */
-TEST_F(CursorInputMapperTest, PointerCaptureDisablesVelocityProcessing) {
- addConfigurationProperty("cursor.mode", "pointer");
- const VelocityControlParameters testParams(/*scale=*/5.f, /*low threshold=*/0.f,
- /*high threshold=*/100.f, /*acceleration=*/10.f);
- mFakePolicy->setVelocityControlParams(testParams);
- 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);
-
- NotifyMotionArgs args;
-
- // Move and verify scale is applied.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_GT(relX, 10);
- ASSERT_GT(relY, 20);
-
- // Enable Pointer Capture
- mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- NotifyPointerCaptureChangedArgs captureArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
- ASSERT_TRUE(captureArgs.request.enable);
-
- // Move and verify scale is not applied.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(10, args.pointerCoords[0].getX());
- ASSERT_EQ(20, args.pointerCoords[0].getY());
-}
-
-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, PopulateDeviceInfoReturnsRangeFromPolicy) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- InputDeviceInfo info;
- mapper.populateDeviceInfo(info);
-
- // Initially there may not be a valid motion range.
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE));
- ASSERT_EQ(nullptr, info.getMotionRange(AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info, AINPUT_MOTION_RANGE_PRESSURE,
- AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-
- // When the viewport and the default pointer display ID is set, then there should be a valid
- // motion range.
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- InputDeviceInfo info2;
- mapper.populateDeviceInfo(info2);
-
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_X, AINPUT_SOURCE_MOUSE, 0,
- DISPLAY_WIDTH - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_Y, AINPUT_SOURCE_MOUSE, 0,
- DISPLAY_HEIGHT - 1, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(assertMotionRange(info2, AINPUT_MOTION_RANGE_PRESSURE,
- AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- prepareDisplay(ui::ROTATION_0);
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- NotifyMotionArgs motionArgs;
- NotifyKeyArgs keyArgs;
-
- // press BTN_LEFT, release BTN_LEFT
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_LEFT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- // press BTN_RIGHT + BTN_MIDDLE, release BTN_RIGHT, release BTN_MIDDLE
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_DOWN, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_SECONDARY | AMOTION_EVENT_BUTTON_TERTIARY,
- motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_RIGHT, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_TERTIARY, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 1.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_MIDDLE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_UP, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- // press BTN_BACK, release BTN_BACK
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_BACK, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
-
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_SIDE, release BTN_SIDE
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_BACK, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_SIDE, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_BACK, keyArgs.keyCode);
-
- // press BTN_FORWARD, release BTN_FORWARD
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_FORWARD, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- // press BTN_EXTRA, release BTN_EXTRA
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_DOWN, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_PRESS, motionArgs.action);
- ASSERT_EQ(AMOTION_EVENT_BUTTON_FORWARD, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_KEY, BTN_EXTRA, 0);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_BUTTON_RELEASE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, motionArgs.action);
- ASSERT_EQ(0, motionArgs.buttonState);
- ASSERT_NO_FATAL_FAILURE(
- assertCursorPointerCoords(motionArgs.pointerCoords[0], 0.0f, 0.0f, 0.0f));
-
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&keyArgs));
- ASSERT_EQ(AKEY_EVENT_ACTION_UP, keyArgs.action);
- ASSERT_EQ(AKEYCODE_FORWARD, keyArgs.keyCode);
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer, ProcessWhenModeIsPointerShouldKeepZeroCoords) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- prepareDisplay(ui::ROTATION_0);
-
- mFakePointerController->setBounds(0, 0, 800 - 1, 480 - 1);
- mFakePointerController->setPosition(100, 200);
-
- NotifyMotionArgs args;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
- 0.0f, 0.0f, 0.0f, 0.0f, 0.0f));
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer, PointerCaptureDisablesVelocityProcessing) {
- addConfigurationProperty("cursor.mode", "pointer");
- const VelocityControlParameters testParams(/*scale=*/5.f, /*lowThreshold=*/0.f,
- /*highThreshold=*/100.f, /*acceleration=*/10.f);
- mFakePolicy->setVelocityControlParams(testParams);
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- prepareDisplay(ui::ROTATION_0);
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- NotifyMotionArgs args;
-
- // Move and verify scale is applied.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- const float relX = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_GT(relX, 10);
- ASSERT_GT(relY, 20);
-
- // Enable Pointer Capture
- mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- NotifyPointerCaptureChangedArgs captureArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
- ASSERT_TRUE(captureArgs.request.enable);
-
- // Move and verify scale is not applied.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- const float relX2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X);
- const float relY2 = args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y);
- ASSERT_EQ(10, relX2);
- ASSERT_EQ(20, relY2);
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_90);
-
- // Set up the secondary display as the display on which the pointer should be shown.
- // The InputDevice is not associated with any display.
- prepareSecondaryDisplay();
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
- // Ensure input events are generated without display ID and coords,
- // because they will be decided later by PointerChoreographer.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(ADISPLAY_ID_NONE),
- WithCoords(0.0f, 0.0f))));
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_90);
-
- // Set up the secondary display as the display on which the pointer should be shown,
- // and associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
- // Ensure input events are generated with associated display ID but not with coords,
- // because the coords will be decided later by PointerChoreographer.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(0.0f, 0.0f))));
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer,
- ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(ui::ROTATION_90);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
-
- // Associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- // With PointerChoreographer enabled, there could be a PointerController for the associated
- // display even if it is different from the pointer display. So the mapper should generate an
- // event.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(0.0f, 0.0f))));
-}
-
-// --- BluetoothCursorInputMapperTest ---
-
-class BluetoothCursorInputMapperTest : public CursorInputMapperTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(false);
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
-
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
- }
-};
-
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmoothening) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // Process several events that come in quick succession, according to their timestamps.
- for (int i = 0; i < 3; i++) {
- constexpr static nsecs_t delta = ms2ns(1);
- static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
- kernelEventTime += delta;
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
- }
-}
-
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningIsCapped) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // Process several events with the same timestamp from the kernel.
- // Ensure that we do not generate events too far into the future.
- constexpr static int32_t numEvents =
- MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
- for (int i = 0; i < numEvents; i++) {
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
- }
-
- // By processing more events with the same timestamp, we should not generate events with a
- // timestamp that is more than the specified max time delta from the timestamp at its injection.
- const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
- for (int i = 0; i < 3; i++) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(cappedEventTime))));
- }
-}
-
-TEST_F(BluetoothCursorInputMapperTest, TimestampSmootheningNotUsed) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
- // smoothening is not needed, its timestamp is not affected.
- kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
- expectedEventTime = kernelEventTime;
-
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-}
-
-// --- BluetoothCursorInputMapperTestWithChoreographer ---
-
-class BluetoothCursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
- InputMapperTest::SetUp(DEVICE_CLASSES | InputDeviceClass::EXTERNAL, BUS_BLUETOOTH);
-
- mFakePointerController = std::make_shared<FakePointerController>();
- mFakePolicy->setPointerController(mFakePointerController);
- }
-};
-
-TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmoothening) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_0);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // Process several events that come in quick succession, according to their timestamps.
- for (int i = 0; i < 3; i++) {
- constexpr static nsecs_t delta = ms2ns(1);
- static_assert(delta < MIN_BLUETOOTH_TIMESTAMP_DELTA);
- kernelEventTime += delta;
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
- }
-}
-
-TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningIsCapped) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_0);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // Process several events with the same timestamp from the kernel.
- // Ensure that we do not generate events too far into the future.
- constexpr static int32_t numEvents =
- MAX_BLUETOOTH_SMOOTHING_DELTA / MIN_BLUETOOTH_TIMESTAMP_DELTA;
- for (int i = 0; i < numEvents; i++) {
- expectedEventTime += MIN_BLUETOOTH_TIMESTAMP_DELTA;
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
- }
-
- // By processing more events with the same timestamp, we should not generate events with a
- // timestamp that is more than the specified max time delta from the timestamp at its injection.
- const nsecs_t cappedEventTime = ARBITRARY_TIME + MAX_BLUETOOTH_SMOOTHING_DELTA;
- for (int i = 0; i < 3; i++) {
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(cappedEventTime))));
- }
-}
-
-TEST_F(BluetoothCursorInputMapperTestWithChoreographer, TimestampSmootheningNotUsed) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_0);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- nsecs_t kernelEventTime = ARBITRARY_TIME;
- nsecs_t expectedEventTime = ARBITRARY_TIME;
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-
- // If the next event has a timestamp that is sufficiently spaced out so that Bluetooth timestamp
- // smoothening is not needed, its timestamp is not affected.
- kernelEventTime += MAX_BLUETOOTH_SMOOTHING_DELTA + ms2ns(1);
- expectedEventTime = kernelEventTime;
-
- process(mapper, kernelEventTime, READ_TIME, EV_REL, REL_X, 1);
- process(mapper, kernelEventTime, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithEventTime(expectedEventTime))));
-}
-
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
@@ -10971,15 +9528,16 @@
ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+TEST_F(MultiTouchInputMapperTest, Reset_RepopulatesMultiTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
// First finger down.
+ constexpr int32_t x1 = 100, y1 = 200, x2 = 300, y2 = 400;
processId(mapper, FIRST_TRACKING_ID);
- processPosition(mapper, 100, 200);
+ processPosition(mapper, x1, y1);
processPressure(mapper, RAW_PRESSURE_MAX);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
@@ -10988,14 +9546,32 @@
// Second finger down.
processSlot(mapper, SECOND_SLOT);
processId(mapper, SECOND_TRACKING_ID);
- processPosition(mapper, 300, 400);
+ processPosition(mapper, x2, y2);
processPressure(mapper, RAW_PRESSURE_MAX);
processSync(mapper);
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
+ // Set MT Slot state to be repopulated for the required slots
+ std::vector<int32_t> mtSlotValues(RAW_SLOT_MAX + 1, -1);
+ mtSlotValues[0] = FIRST_TRACKING_ID;
+ mtSlotValues[1] = SECOND_TRACKING_ID;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_TRACKING_ID, mtSlotValues);
+
+ mtSlotValues[0] = x1;
+ mtSlotValues[1] = x2;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_X, mtSlotValues);
+
+ mtSlotValues[0] = y1;
+ mtSlotValues[1] = y2;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_POSITION_Y, mtSlotValues);
+
+ mtSlotValues[0] = RAW_PRESSURE_MAX;
+ mtSlotValues[1] = RAW_PRESSURE_MAX;
+ mFakeEventHub->setMtSlotValues(EVENTHUB_ID, ABS_MT_PRESSURE, mtSlotValues);
+
// Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
- // preserved. Resetting should cancel the ongoing gesture.
+ // repopulated. Resetting should cancel the ongoing gesture.
resetMapper(mapper, ARBITRARY_TIME);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
diff --git a/services/inputflinger/tests/InterfaceMocks.h b/services/inputflinger/tests/InterfaceMocks.h
index 7394913..db89168 100644
--- a/services/inputflinger/tests/InterfaceMocks.h
+++ b/services/inputflinger/tests/InterfaceMocks.h
@@ -48,7 +48,7 @@
class MockInputReaderContext : public InputReaderContext {
public:
MOCK_METHOD(void, updateGlobalMetaState, (), (override));
- int32_t getGlobalMetaState() override { return 0; };
+ MOCK_METHOD(int32_t, getGlobalMetaState, (), (override));
MOCK_METHOD(void, disableVirtualKeysUntil, (nsecs_t time), (override));
MOCK_METHOD(bool, shouldDropVirtualKey, (nsecs_t now, int32_t keyCode, int32_t scanCode),
@@ -132,6 +132,9 @@
MOCK_METHOD(status_t, getAbsoluteAxisValue, (int32_t deviceId, int32_t axis, int32_t* outValue),
(const, override));
+ MOCK_METHOD(base::Result<std::vector<int32_t>>, getMtSlotValues,
+ (int32_t deviceId, int32_t axis, size_t slotCount), (const, override));
+
MOCK_METHOD(int32_t, getKeyCodeForKeyLocation, (int32_t deviceId, int32_t locationKeyCode),
(const, override));
MOCK_METHOD(bool, markSupportedKeyCodes,
diff --git a/services/inputflinger/tests/KeyboardInputMapper_test.cpp b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
index 2ef7999..b44529b 100644
--- a/services/inputflinger/tests/KeyboardInputMapper_test.cpp
+++ b/services/inputflinger/tests/KeyboardInputMapper_test.cpp
@@ -55,6 +55,7 @@
void SetUp() override {
InputMapperUnitTest::SetUp();
+ createDevice();
// set key-codes expected in tests
for (const auto& [scanCode, outKeycode] : mKeyCodeMap) {
diff --git a/services/inputflinger/tests/MultiTouchInputMapper_test.cpp b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
new file mode 100644
index 0000000..d726385
--- /dev/null
+++ b/services/inputflinger/tests/MultiTouchInputMapper_test.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#include "MultiTouchInputMapper.h"
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+#include <list>
+#include <optional>
+
+#include "InputMapperTest.h"
+#include "InterfaceMocks.h"
+#include "TestEventMatchers.h"
+
+#define TAG "MultiTouchpadInputMapperUnit_test"
+
+namespace android {
+
+using testing::_;
+using testing::IsEmpty;
+using testing::Return;
+using testing::SetArgPointee;
+using testing::VariantWith;
+
+static constexpr int32_t DISPLAY_ID = 0;
+static constexpr int32_t DISPLAY_WIDTH = 480;
+static constexpr int32_t DISPLAY_HEIGHT = 800;
+static constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
+static constexpr int32_t SLOT_COUNT = 5;
+
+static constexpr int32_t ACTION_POINTER_0_UP =
+ AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+static constexpr int32_t ACTION_POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+
+/**
+ * Unit tests for MultiTouchInputMapper.
+ */
+class MultiTouchInputMapperUnitTest : public InputMapperUnitTest {
+protected:
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+
+ // Present scan codes
+ expectScanCodes(/*present=*/true,
+ {BTN_TOUCH, BTN_TOOL_FINGER, BTN_TOOL_DOUBLETAP, BTN_TOOL_TRIPLETAP,
+ BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
+
+ // Missing scan codes that the mapper checks for.
+ expectScanCodes(/*present=*/false,
+ {BTN_TOOL_PEN, BTN_TOOL_RUBBER, BTN_TOOL_BRUSH, BTN_TOOL_PENCIL,
+ BTN_TOOL_AIRBRUSH});
+
+ // Current scan code state - all keys are UP by default
+ setScanCodeState(KeyState::UP, {BTN_LEFT, BTN_RIGHT, BTN_MIDDLE,
+ BTN_BACK, BTN_SIDE, BTN_FORWARD,
+ BTN_EXTRA, BTN_TASK, BTN_TOUCH,
+ BTN_STYLUS, BTN_STYLUS2, BTN_0,
+ BTN_TOOL_FINGER, BTN_TOOL_PEN, BTN_TOOL_RUBBER,
+ BTN_TOOL_BRUSH, BTN_TOOL_PENCIL, BTN_TOOL_AIRBRUSH,
+ BTN_TOOL_MOUSE, BTN_TOOL_LENS, BTN_TOOL_DOUBLETAP,
+ BTN_TOOL_TRIPLETAP, BTN_TOOL_QUADTAP, BTN_TOOL_QUINTTAP});
+
+ setKeyCodeState(KeyState::UP,
+ {AKEYCODE_STYLUS_BUTTON_PRIMARY, AKEYCODE_STYLUS_BUTTON_SECONDARY});
+
+ // Input properties - only INPUT_PROP_DIRECT for touchscreen
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, _)).WillRepeatedly(Return(false));
+ EXPECT_CALL(mMockEventHub, hasInputProperty(EVENTHUB_ID, INPUT_PROP_DIRECT))
+ .WillRepeatedly(Return(true));
+
+ // Axes that the device has
+ setupAxis(ABS_MT_SLOT, /*valid=*/true, /*min=*/0, /*max=*/SLOT_COUNT - 1, /*resolution=*/0);
+ setupAxis(ABS_MT_TRACKING_ID, /*valid=*/true, /*min*/ 0, /*max=*/255, /*resolution=*/0);
+ setupAxis(ABS_MT_POSITION_X, /*valid=*/true, /*min=*/0, /*max=*/2000, /*resolution=*/24);
+ setupAxis(ABS_MT_POSITION_Y, /*valid=*/true, /*min=*/0, /*max=*/1000, /*resolution=*/24);
+
+ // Axes that the device does not have
+ setupAxis(ABS_MT_PRESSURE, /*valid=*/false, /*min*/ 0, /*max=*/255, /*resolution=*/0);
+ setupAxis(ABS_MT_ORIENTATION, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOUCH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+
+ // reset current slot at the beginning
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
+ .WillRepeatedly([](int32_t, int32_t, int32_t* outValue) {
+ *outValue = 0;
+ return OK;
+ });
+
+ // mark all slots not in use
+ mockSlotValues({});
+
+ mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
+ mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
+ /*isActive=*/true, "local:0", NO_PORT,
+ ViewportType::INTERNAL);
+ createDevice();
+ mMapper = createInputMapper<MultiTouchInputMapper>(*mDeviceContext,
+ mFakePolicy->getReaderConfiguration());
+ }
+
+ // Mocks position and tracking Ids for the provided slots. Remaining slots will be marked
+ // unused.
+ void mockSlotValues(
+ const std::unordered_map<int32_t /*slotIndex*/,
+ std::pair<Point /*position*/, int32_t /*trackingId*/>>&
+ slotValues) {
+ EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, _, SLOT_COUNT))
+ .WillRepeatedly([=](int32_t, int32_t axis,
+ size_t slotCount) -> base::Result<std::vector<int32_t>> {
+ // tracking Id for the unused slots must set to be < 0
+ std::vector<int32_t> outMtSlotValues(slotCount + 1, -1);
+ outMtSlotValues[0] = axis;
+ switch (axis) {
+ case ABS_MT_POSITION_X:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.first.x;
+ }
+ return outMtSlotValues;
+ case ABS_MT_POSITION_Y:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.first.y;
+ }
+ return outMtSlotValues;
+ case ABS_MT_TRACKING_ID:
+ for (const auto& [slotIndex, valuePair] : slotValues) {
+ outMtSlotValues[slotIndex] = valuePair.second;
+ }
+ return outMtSlotValues;
+ default:
+ return base::ResultError("Axis not supported", NAME_NOT_FOUND);
+ }
+ });
+ }
+
+ std::list<NotifyArgs> processPosition(int32_t x, int32_t y) {
+ std::list<NotifyArgs> args;
+ args += process(EV_ABS, ABS_MT_POSITION_X, x);
+ args += process(EV_ABS, ABS_MT_POSITION_Y, y);
+ return args;
+ }
+
+ std::list<NotifyArgs> processId(int32_t id) { return process(EV_ABS, ABS_MT_TRACKING_ID, id); }
+
+ std::list<NotifyArgs> processKey(int32_t code, int32_t value) {
+ return process(EV_KEY, code, value);
+ }
+
+ std::list<NotifyArgs> processSlot(int32_t slot) { return process(EV_ABS, ABS_MT_SLOT, slot); }
+
+ std::list<NotifyArgs> processSync() { return process(EV_SYN, SYN_REPORT, 0); }
+};
+
+// This test simulates a multi-finger gesture with unexpected reset in between. This might happen
+// due to buffer overflow and device with report a SYN_DROPPED. In this case we expect mapper to be
+// reset, MT slot state to be re-populated and the gesture should be cancelled and restarted.
+TEST_F(MultiTouchInputMapperUnitTest, MultiFingerGestureWithUnexpectedReset) {
+ std::list<NotifyArgs> args;
+
+ // Two fingers down at once.
+ constexpr int32_t FIRST_TRACKING_ID = 1, SECOND_TRACKING_ID = 2;
+ int32_t x1 = 100, y1 = 125, x2 = 200, y2 = 225;
+ processKey(BTN_TOUCH, 1);
+ args += processPosition(x1, y1);
+ args += processId(FIRST_TRACKING_ID);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ args += processId(SECOND_TRACKING_ID);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(ACTION_POINTER_1_DOWN))));
+
+ // Move.
+ x1 += 10;
+ y1 += 15;
+ x2 += 5;
+ y2 -= 10;
+ args = processSlot(0);
+ args += processPosition(x1, y1);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+ const auto pointerCoordsBeforeReset = std::get<NotifyMotionArgs>(args.back()).pointerCoords;
+
+ // On buffer overflow mapper will be reset and MT slots data will be repopulated
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, _))
+ .WillRepeatedly([=](int32_t, int32_t, int32_t* outValue) {
+ *outValue = 1;
+ return OK;
+ });
+
+ mockSlotValues(
+ {{1, {Point{x1, y1}, FIRST_TRACKING_ID}}, {2, {Point{x2, y2}, SECOND_TRACKING_ID}}});
+
+ setScanCodeState(KeyState::DOWN, {BTN_TOUCH});
+
+ args = mMapper->reset(systemTime(SYSTEM_TIME_MONOTONIC));
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL))));
+
+ // SYN_REPORT should restart the gesture again
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)),
+ VariantWith<NotifyMotionArgs>(
+ WithMotionAction(ACTION_POINTER_1_DOWN))));
+ ASSERT_EQ(std::get<NotifyMotionArgs>(args.back()).pointerCoords, pointerCoordsBeforeReset);
+
+ // Move.
+ x1 += 10;
+ y1 += 15;
+ x2 += 5;
+ y2 -= 10;
+ args = processSlot(0);
+ args += processPosition(x1, y1);
+ args += processSlot(1);
+ args += processPosition(x2, y2);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE))));
+
+ // First finger up.
+ args = processSlot(0);
+ args += processId(-1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(WithMotionAction(ACTION_POINTER_0_UP))));
+
+ // Second finger up.
+ processKey(BTN_TOUCH, 0);
+ args = processSlot(1);
+ args += processId(-1);
+ ASSERT_THAT(args, IsEmpty());
+
+ args = processSync();
+ ASSERT_THAT(args,
+ ElementsAre(
+ VariantWith<NotifyMotionArgs>(WithMotionAction(AMOTION_EVENT_ACTION_UP))));
+}
+
+} // namespace android
diff --git a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
index 9fa6cdd..5e67506 100644
--- a/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
+++ b/services/inputflinger/tests/MultiTouchMotionAccumulator_test.cpp
@@ -23,6 +23,11 @@
protected:
static constexpr size_t SLOT_COUNT = 8;
+ void SetUp() override {
+ InputMapperUnitTest::SetUp();
+ createDevice();
+ }
+
MultiTouchMotionAccumulator mMotionAccumulator;
void processMotionEvent(int32_t type, int32_t code, int32_t value) {
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 193b84d..1ac043c 100644
--- a/services/inputflinger/tests/PointerChoreographer_test.cpp
+++ b/services/inputflinger/tests/PointerChoreographer_test.cpp
@@ -145,15 +145,18 @@
};
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 @@
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/TestEventMatchers.h b/services/inputflinger/tests/TestEventMatchers.h
index 8ba497a..a3e8eaf 100644
--- a/services/inputflinger/tests/TestEventMatchers.h
+++ b/services/inputflinger/tests/TestEventMatchers.h
@@ -18,6 +18,7 @@
#include <cmath>
#include <compare>
+#include <ios>
#include <android-base/stringprintf.h>
#include <android/input.h>
@@ -678,6 +679,24 @@
return argPressure == pressure;
}
+MATCHER_P(WithSize, size, "MotionEvent with specified size") {
+ const auto argSize = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_SIZE);
+ *result_listener << "expected size " << size << ", but got " << argSize;
+ return argSize == size;
+}
+
+MATCHER_P(WithOrientation, orientation, "MotionEvent with specified orientation") {
+ const auto argOrientation = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION);
+ *result_listener << "expected orientation " << orientation << ", but got " << argOrientation;
+ return argOrientation == orientation;
+}
+
+MATCHER_P(WithDistance, distance, "MotionEvent with specified distance") {
+ const auto argDistance = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_DISTANCE);
+ *result_listener << "expected distance " << distance << ", but got " << argDistance;
+ return argDistance == distance;
+}
+
MATCHER_P2(WithTouchDimensions, maj, min, "InputEvent with specified touch dimensions") {
const auto argMajor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR);
const auto argMinor = arg.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR);
@@ -721,6 +740,12 @@
return arg.buttonState == buttons;
}
+MATCHER_P(WithMetaState, metaState, "InputEvent with specified meta state") {
+ *result_listener << "expected meta state 0x" << std::hex << metaState << ", but got 0x"
+ << arg.metaState;
+ return arg.metaState == metaState;
+}
+
MATCHER_P(WithActionButton, actionButton, "InputEvent with specified action button") {
*result_listener << "expected action button " << actionButton << ", but got "
<< arg.actionButton;
@@ -743,4 +768,16 @@
return arg.xPrecision == xPrecision && arg.yPrecision == yPrecision;
}
+MATCHER_P(WithPolicyFlags, policyFlags, "InputEvent with specified policy flags") {
+ *result_listener << "expected policy flags 0x" << std::hex << policyFlags << ", but got 0x"
+ << arg.policyFlags;
+ return arg.policyFlags == static_cast<uint32_t>(policyFlags);
+}
+
+MATCHER_P(WithEdgeFlags, edgeFlags, "InputEvent with specified edge flags") {
+ *result_listener << "expected edge flags 0x" << std::hex << edgeFlags << ", but got 0x"
+ << arg.edgeFlags;
+ return arg.edgeFlags == edgeFlags;
+}
+
} // namespace android
diff --git a/services/inputflinger/tests/TouchpadInputMapper_test.cpp b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
index 8cf738c..fbafbad 100644
--- a/services/inputflinger/tests/TouchpadInputMapper_test.cpp
+++ b/services/inputflinger/tests/TouchpadInputMapper_test.cpp
@@ -37,6 +37,8 @@
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;
@@ -101,12 +103,20 @@
setupAxis(ABS_MT_TOUCH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_WIDTH_MAJOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
setupAxis(ABS_MT_WIDTH_MINOR, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TRACKING_ID, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_DISTANCE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
+ setupAxis(ABS_MT_TOOL_TYPE, /*valid=*/false, /*min=*/0, /*max=*/0, /*resolution=*/0);
EXPECT_CALL(mMockEventHub, getAbsoluteAxisValue(EVENTHUB_ID, ABS_MT_SLOT, testing::_))
.WillRepeatedly([](int32_t eventHubId, int32_t, int32_t* outValue) {
*outValue = 0;
return OK;
});
+ EXPECT_CALL(mMockEventHub, getMtSlotValues(EVENTHUB_ID, testing::_, testing::_))
+ .WillRepeatedly([]() -> base::Result<std::vector<int32_t>> {
+ return base::ResultError("Axis not supported", NAME_NOT_FOUND);
+ });
+ createDevice();
mMapper = createInputMapper<TouchpadInputMapper>(*mDeviceContext, mReaderConfiguration);
}
};
@@ -150,12 +160,14 @@
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();
@@ -217,12 +229,14 @@
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/inputflinger/tests/fuzzers/MapperHelpers.h b/services/inputflinger/tests/fuzzers/MapperHelpers.h
index 81c570d..7898126 100644
--- a/services/inputflinger/tests/fuzzers/MapperHelpers.h
+++ b/services/inputflinger/tests/fuzzers/MapperHelpers.h
@@ -201,6 +201,18 @@
int32_t* outValue) const override {
return mFdp->ConsumeIntegral<status_t>();
}
+ base::Result<std::vector<int32_t>> getMtSlotValues(int32_t deviceId, int32_t axis,
+ size_t slotCount) const override {
+ if (mFdp->ConsumeBool()) {
+ std::vector<int32_t> outValues(slotCount + 1);
+ for (size_t i = 0; i < outValues.size(); i++) {
+ outValues.push_back(mFdp->ConsumeIntegral<int32_t>());
+ }
+ return std::move(outValues);
+ } else {
+ return base::ResultError("Fuzzer", UNKNOWN_ERROR);
+ }
+ }
bool markSupportedKeyCodes(int32_t deviceId, const std::vector<int32_t>& keyCodes,
uint8_t* outFlags) const override {
return mFdp->ConsumeBool();
diff --git a/services/sensorservice/Android.bp b/services/sensorservice/Android.bp
index 0dd4dd6..019fefa 100644
--- a/services/sensorservice/Android.bp
+++ b/services/sensorservice/Android.bp
@@ -15,7 +15,7 @@
cc_aconfig_library {
name: "sensorservice_flags_c_lib",
- aconfig_declarations: "dynamic_sensors_flags",
+ aconfig_declarations: "sensorservice_flags",
host_supported: true,
}
@@ -82,6 +82,7 @@
"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 @@
"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 dd83fde..45fab7e 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::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 @@
} 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/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 30ba9e4..a7296f3 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -3370,7 +3370,6 @@
static const mat4 kDefaultColorTransformMat;
static const Region kDebugRegion;
- static const compositionengine::CompositionRefreshArgs kDefaultRefreshArgs;
static const HdrCapabilities kHdrCapabilities;
StrictMock<mock::CompositionEngine> mCompositionEngine;
@@ -3393,7 +3392,6 @@
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputDestinationClip{1013, 1014, 1015, 1016};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5f};
-const compositionengine::CompositionRefreshArgs OutputComposeSurfacesTest::kDefaultRefreshArgs;
const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
const HdrCapabilities OutputComposeSurfacesTest::
diff --git a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
index ad5e42b..bb32afa 100644
--- a/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
+++ b/services/surfaceflinger/FrontEnd/LayerSnapshotBuilder.cpp
@@ -1217,8 +1217,8 @@
Rect inputBoundsInDisplaySpace =
getInputBoundsInDisplaySpace(*cropLayerSnapshot, inputBounds,
displayInfo.transform);
- snapshot->inputInfo.touchableRegion = snapshot->inputInfo.touchableRegion.intersect(
- displayInfo.transform.transform(inputBoundsInDisplaySpace));
+ snapshot->inputInfo.touchableRegion =
+ snapshot->inputInfo.touchableRegion.intersect(inputBoundsInDisplaySpace);
}
// If the layer is a clone, we need to crop the input region to cloned root to prevent
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.cpp b/services/surfaceflinger/Scheduler/LayerInfo.cpp
index 97fca39..9c4f7a5 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.cpp
+++ b/services/surfaceflinger/Scheduler/LayerInfo.cpp
@@ -62,6 +62,10 @@
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 @@
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 e06221a..c3709e5 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateSelector.cpp
@@ -420,6 +420,11 @@
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 @@
int explicitExact = 0;
int explicitGteLayers = 0;
int explicitCategoryVoteLayers = 0;
+ int interactiveLayers = 0;
int seamedFocusedLayers = 0;
int categorySmoothSwitchOnlyLayers = 0;
@@ -534,7 +540,13 @@
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 @@
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 @@
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/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 28e35de..6e12b33 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -313,7 +313,7 @@
? std::max(timePoint, *lastVsyncOpt + minFramePeriod - threshold)
: timePoint;
const auto vsyncTime = snapToVsyncAlignedWithRenderRate(baseTime);
- if (FlagManager::getInstance().vrr_config()) {
+ if (FlagManager::getInstance().vrr_config() && mDisplayModePtr->getVrrConfig()) {
const auto vsyncTimePoint = TimePoint::fromNs(vsyncTime);
const Fps renderRate = mRenderRateOpt ? *mRenderRateOpt : mDisplayModePtr->getPeakFps();
mVsyncTrackerCallback.onVsyncGenerated(vsyncTimePoint, mDisplayModePtr, renderRate);
diff --git a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
index 2806450..84ef89f 100644
--- a/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
+++ b/services/surfaceflinger/Scheduler/include/scheduler/Fps.h
@@ -88,6 +88,7 @@
NoPreference,
Low,
Normal,
+ HighHint,
High,
ftl_last = High
diff --git a/services/surfaceflinger/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index b65a2b3..f097a13 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -98,6 +98,7 @@
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.
@@ -149,3 +150,19 @@
bug: "286084594"
is_fixed_read_only: true
}
+
+flag {
+ name: "vulkan_renderengine"
+ namespace: "core_graphics"
+ description: "Use Vulkan backend in RenderEngine prior to switching to Graphite."
+ bug: "293371537"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "graphite_renderengine"
+ namespace: "core_graphics"
+ description: "Use Skia's Graphite Vulkan backend in RenderEngine."
+ bug: "293371537"
+ is_fixed_read_only: true
+}
diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
index 2c71a2e..99062f0 100644
--- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp
@@ -179,23 +179,35 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 1);
+ ASSERT_EQ(packets.size(), 2);
- const auto& packet = packets[0];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- ASSERT_TRUE(buffer_event.has_duration_ns());
- EXPECT_EQ(buffer_event.duration_ns(), duration);
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
+ ASSERT_TRUE(buffer_event1.has_duration_ns());
+ EXPECT_EQ(buffer_event1.duration_ns(), duration);
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), 0);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
}
@@ -219,7 +231,18 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ const auto& frame_event = packet.graphics_frame_event();
+ ASSERT_TRUE(frame_event.has_buffer_event());
+ const auto& buffer_event = frame_event.buffer_event();
+ ASSERT_TRUE(buffer_event.has_type());
+ EXPECT_EQ(buffer_event.type(),
+ perfetto::protos::GraphicsFrameEvent_BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
{
@@ -235,22 +258,56 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above.
+ ASSERT_EQ(packets.size(), 3);
- const auto& packet = packets[1];
- ASSERT_TRUE(packet.has_timestamp());
- EXPECT_EQ(packet.timestamp(), timestamp);
- ASSERT_TRUE(packet.has_graphics_frame_event());
- const auto& frame_event = packet.graphics_frame_event();
- ASSERT_TRUE(frame_event.has_buffer_event());
- const auto& buffer_event = frame_event.buffer_event();
- ASSERT_TRUE(buffer_event.has_buffer_id());
- EXPECT_EQ(buffer_event.buffer_id(), bufferID);
- ASSERT_TRUE(buffer_event.has_frame_number());
- EXPECT_EQ(buffer_event.frame_number(), frameNumber);
- ASSERT_TRUE(buffer_event.has_type());
- EXPECT_EQ(buffer_event.type(), perfetto::protos::GraphicsFrameEvent_BufferEventType(type));
- EXPECT_FALSE(buffer_event.has_duration_ns());
+ const auto& packet1 = packets[0];
+ ASSERT_TRUE(packet1.has_timestamp());
+ EXPECT_EQ(packet1.timestamp(), timestamp);
+ ASSERT_TRUE(packet1.has_graphics_frame_event());
+ const auto& frame_event1 = packet1.graphics_frame_event();
+ ASSERT_TRUE(frame_event1.has_buffer_event());
+ const auto& buffer_event1 = frame_event1.buffer_event();
+ ASSERT_TRUE(buffer_event1.has_buffer_id());
+ EXPECT_EQ(buffer_event1.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event1.has_frame_number());
+ EXPECT_EQ(buffer_event1.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event1.has_type());
+ EXPECT_EQ(buffer_event1.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event1.has_duration_ns());
+
+ const auto& packet2 = packets[1];
+ ASSERT_TRUE(packet2.has_timestamp());
+ EXPECT_EQ(packet2.timestamp(), timestamp);
+ ASSERT_TRUE(packet2.has_graphics_frame_event());
+ const auto& frame_event2 = packet2.graphics_frame_event();
+ ASSERT_TRUE(frame_event2.has_buffer_event());
+ const auto& buffer_event2 = frame_event2.buffer_event();
+ ASSERT_TRUE(buffer_event2.has_buffer_id());
+ EXPECT_EQ(buffer_event2.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event2.has_frame_number());
+ EXPECT_EQ(buffer_event2.frame_number(), frameNumber);
+ ASSERT_TRUE(buffer_event2.has_type());
+ EXPECT_EQ(buffer_event2.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+ EXPECT_FALSE(buffer_event2.has_duration_ns());
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ const auto& frame_event3 = packet3.graphics_frame_event();
+ ASSERT_TRUE(frame_event3.has_buffer_event());
+ const auto& buffer_event3 = frame_event3.buffer_event();
+ ASSERT_TRUE(buffer_event3.has_buffer_id());
+ EXPECT_EQ(buffer_event3.buffer_id(), bufferID);
+ ASSERT_TRUE(buffer_event3.has_frame_number());
+ EXPECT_EQ(buffer_event3.frame_number(), 0);
+ ASSERT_TRUE(buffer_event3.has_type());
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
+ EXPECT_FALSE(buffer_event3.has_duration_ns());
}
}
@@ -285,7 +342,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -293,6 +350,8 @@
ASSERT_TRUE(packet1.has_graphics_frame_event());
ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet1.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -300,6 +359,17 @@
ASSERT_TRUE(packet2.has_graphics_frame_event());
ASSERT_TRUE(packet2.graphics_frame_event().has_buffer_event());
ASSERT_FALSE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
+ EXPECT_EQ(packet2.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet3.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) {
@@ -322,7 +392,15 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 0);
+ ASSERT_EQ(packets.size(), 1);
+ const auto& packet = packets[0];
+ ASSERT_TRUE(packet.has_timestamp());
+ EXPECT_EQ(packet.timestamp(), 0);
+ ASSERT_TRUE(packet.has_graphics_frame_event());
+ ASSERT_TRUE(packet.graphics_frame_event().has_buffer_event());
+ EXPECT_EQ(packet.graphics_frame_event().buffer_event().type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) {
@@ -357,7 +435,7 @@
tracingSession->StopBlocking();
auto packets = readGraphicsFramePacketsBlocking(tracingSession.get());
- EXPECT_EQ(packets.size(), 2);
+ ASSERT_EQ(packets.size(), 3);
const auto& packet1 = packets[0];
ASSERT_TRUE(packet1.has_timestamp());
@@ -367,6 +445,7 @@
ASSERT_TRUE(packet1.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event1.duration_ns(), duration);
+ EXPECT_EQ(buffer_event1.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
const auto& packet2 = packets[1];
ASSERT_TRUE(packet2.has_timestamp());
@@ -376,6 +455,17 @@
ASSERT_TRUE(packet2.graphics_frame_event().buffer_event().has_duration_ns());
const auto& buffer_event2 = packet2.graphics_frame_event().buffer_event();
EXPECT_EQ(buffer_event2.duration_ns(), duration);
+ EXPECT_EQ(buffer_event2.type(), perfetto::protos::GraphicsFrameEvent::BufferEventType(type));
+
+ const auto& packet3 = packets[2];
+ ASSERT_TRUE(packet3.has_timestamp());
+ EXPECT_EQ(packet3.timestamp(), 0);
+ ASSERT_TRUE(packet3.has_graphics_frame_event());
+ ASSERT_TRUE(packet3.graphics_frame_event().has_buffer_event());
+ const auto& buffer_event3 = packet3.graphics_frame_event().buffer_event();
+ EXPECT_EQ(buffer_event3.type(),
+ perfetto::protos::GraphicsFrameEvent::BufferEventType(
+ FrameTracer::FrameEvent::UNSPECIFIED));
}
} // namespace
diff --git a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index ba32c68..6394d63 100644
--- a/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
@@ -353,6 +353,23 @@
EXPECT_EQ(getSnapshot({.id = 111})->inputInfo.touchableRegion.bounds(), modifiedTouchCrop);
}
+TEST_F(LayerSnapshotTest, CanCropTouchableRegionWithDisplayTransform) {
+ DisplayInfo displayInfo;
+ displayInfo.transform = ui::Transform(ui::Transform::RotationFlags::ROT_90, 1000, 1000);
+ mFrontEndDisplayInfos.emplace_or_replace(ui::LayerStack::fromValue(1), displayInfo);
+
+ Rect touchCrop{300, 300, 400, 500};
+ createRootLayer(3);
+ setCrop(3, touchCrop);
+ setLayerStack(3, 1);
+ Region touch{Rect{0, 0, 1000, 1000}};
+ setTouchableRegionCrop(3, touch, /*touchCropId=*/3, /*replaceTouchableRegionWithCrop=*/false);
+
+ UPDATE_AND_VERIFY(mSnapshotBuilder, {1, 11, 111, 12, 121, 122, 1221, 13, 2, 3});
+ Rect rotatedCrop = {500, 300, 700, 400};
+ EXPECT_EQ(getSnapshot({.id = 3})->inputInfo.touchableRegion.bounds(), rotatedCrop);
+}
+
TEST_F(LayerSnapshotTest, blurUpdatesWhenAlphaChanges) {
int blurRadius = 42;
setBackgroundBlurRadius(1221, static_cast<uint32_t>(blurRadius));
@@ -650,7 +667,7 @@
// │ └── 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 +862,7 @@
// │ │ └── 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 +904,7 @@
// │ │ └── 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 0cacf81..1e526ba 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateSelectorTest.cpp
@@ -1607,6 +1607,82 @@
}
}
+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) {
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index 961ba57..d952b70 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -32,6 +32,7 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <chrono>
+#include <optional>
#include <utility>
#include <com_android_graphics_surfaceflinger_flags.h>
@@ -733,6 +734,27 @@
EXPECT_EQ(6000, vrrTracker.nextAnticipatedVSyncTimeFrom(5000, 5000));
}
+TEST_F(VSyncPredictorTest, absentVrrConfigNoVsyncTrackerCallback) {
+ SET_FLAG_FOR_TEST(flags::vrr_config, true);
+ const auto refreshRate = Fps::fromPeriodNsecs(mPeriod);
+ const std::optional<hal::VrrConfig> vrrConfigOpt = std::nullopt;
+ constexpr int32_t kGroup = 0;
+ constexpr auto kResolution = ui::Size(1920, 1080);
+ const auto mode =
+ ftl::as_non_null(createVrrDisplayMode(DisplayModeId(0), refreshRate, vrrConfigOpt,
+ kGroup, kResolution, DEFAULT_DISPLAY_ID));
+ tracker.setDisplayModePtr(mode);
+
+ auto last = mNow;
+ for (auto i = 0u; i < kMinimumSamplesForPrediction; i++) {
+ EXPECT_CALL(mVsyncTrackerCallback, onVsyncGenerated(_, _, _)).Times(0);
+ EXPECT_THAT(tracker.nextAnticipatedVSyncTimeFrom(mNow), Eq(last + mPeriod));
+ mNow += mPeriod;
+ last = mNow;
+ tracker.addVsyncTimestamp(mNow);
+ }
+}
+
} // namespace android::scheduler
// TODO(b/129481165): remove the #pragma below and fix conversion issues
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
index 5bcce50..e298e7c 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplayMode.h
@@ -45,8 +45,8 @@
}
inline DisplayModePtr createVrrDisplayMode(
- DisplayModeId modeId, Fps displayRefreshRate, hal::VrrConfig vrrConfig, int32_t group = 0,
- ui::Size resolution = ui::Size(1920, 1080),
+ DisplayModeId modeId, Fps displayRefreshRate, std::optional<hal::VrrConfig> vrrConfig,
+ int32_t group = 0, ui::Size resolution = ui::Size(1920, 1080),
PhysicalDisplayId displayId = PhysicalDisplayId::fromPort(0)) {
return createDisplayModeBuilder(modeId, displayRefreshRate, group, resolution, displayId)
.setVrrConfig(std::move(vrrConfig))
diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp
index 0e45d2d..81fd118 100644
--- a/vulkan/libvulkan/driver.cpp
+++ b/vulkan/libvulkan/driver.cpp
@@ -340,8 +340,9 @@
ALOGD("Unload builtin Vulkan driver.");
// Close the opened device
- ALOG_ASSERT(!hal_.dev_->common.close(hal_.dev_->common),
- "hw_device_t::close() failed.");
+ int err = hal_.dev_->common.close(
+ const_cast<struct hw_device_t*>(&hal_.dev_->common));
+ ALOG_ASSERT(!err, "hw_device_t::close() failed.");
// Close the opened shared library in the hw_module_t
android_unload_sphal_library(hal_.dev_->common.module->dso);
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 0df5e77..6b3c379 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1762,6 +1762,8 @@
}
int query_value;
+ // TODO: Now that we are calling into GPDSC2 directly, this query may be redundant
+ // the call to std::max(min_buffer_count, num_images) may be redundant as well
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
&query_value);
if (err != android::OK || query_value < 0) {
@@ -1778,12 +1780,33 @@
// with extra images (which they can't actually use!).
const uint32_t min_buffer_count = min_undequeued_buffers + 1;
- uint32_t num_images;
- if (create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
- num_images = std::max(3u, create_info->minImageCount);
- } else {
- num_images = create_info->minImageCount;
- }
+ // Call into GPDSC2 to get the minimum and maximum allowable buffer count for the surface of
+ // interest. This step is only necessary if the app requests a number of images
+ // (create_info->minImageCount) that is less or more than the surface capabilities.
+ // An app should be calling GPDSC2 and using those values to set create_info, but in the
+ // event that the app has hard-coded image counts an error can occur
+ VkSurfacePresentModeEXT present_mode = {
+ VK_STRUCTURE_TYPE_SURFACE_PRESENT_MODE_EXT,
+ nullptr,
+ create_info->presentMode
+ };
+ VkPhysicalDeviceSurfaceInfo2KHR surface_info2 = {
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
+ &present_mode,
+ create_info->surface
+ };
+ VkSurfaceCapabilities2KHR surface_capabilities2 = {
+ VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
+ nullptr,
+ {},
+ };
+ result = GetPhysicalDeviceSurfaceCapabilities2KHR(GetData(device).driver_physical_device,
+ &surface_info2, &surface_capabilities2);
+
+ uint32_t num_images = create_info->minImageCount;
+ num_images = std::clamp(num_images,
+ surface_capabilities2.surfaceCapabilities.minImageCount,
+ surface_capabilities2.surfaceCapabilities.maxImageCount);
const uint32_t buffer_count = std::max(min_buffer_count, num_images);
err = native_window_set_buffer_count(window, buffer_count);