Merge changes Ie02dbcb3,I8195406c into main
* changes:
Wait till next SYN_REPORT to repopulate device state after SYN_DROPPED
Sync MT slots on reset and buffer overflow
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/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/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 a0e4f7b..864ff50 100644
--- a/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
+++ b/libs/binder/ndk/include_cpp/android/persistable_bundle_aidl.h
@@ -61,7 +61,7 @@
if (__builtin_available(android __ANDROID_API_V__, *)) {
return APersistableBundle_readFromParcel(parcel, &mPBundle);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
@@ -72,7 +72,7 @@
if (__builtin_available(android __ANDROID_API_V__, *)) {
return APersistableBundle_writeToParcel(mPBundle, parcel);
} else {
- return STATUS_FAILED_TRANSACTION;
+ return STATUS_INVALID_OPERATION;
}
}
diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp
index 0ea4a3f..d7f6318 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,14 +217,12 @@
for (int i = 0; i < server_count; i++) {
if (num == i)
continue;
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- workers.push_back(serviceMgr->getService(generateServiceName(i)));
-#pragma clang diagnostic pop
+ workers.push_back(serviceMgr->waitForService(generateServiceName(i)));
}
// Run the benchmark if client
- ProcResults results;
+ ProcResults results(iterations);
+
chrono::time_point<chrono::high_resolution_clock> start, end;
for (int i = 0; (!cs_pair || num >= server_count) && i < iterations; i++) {
Parcel data, reply;
@@ -305,11 +316,10 @@
// Collect all results from the workers.
cout << "collecting results" << endl;
signal_all(pipes);
- ProcResults tot_results;
+ ProcResults tot_results(0), tmp_results(0);
for (int i = 0; i < workers; i++) {
- ProcResults tmp_results;
pipes[i].recv(tmp_results);
- tot_results = ProcResults::combine(tot_results, tmp_results);
+ tot_results.combine_with(tmp_results);
}
// Kill all the workers.
@@ -323,13 +333,11 @@
}
}
if (training_round) {
- // sets max_time_bucket to 2 * m_worst from the training round.
- // Also needs to adjust time_per_bucket accordingly.
- max_time_bucket = 2 * tot_results.m_worst;
- time_per_bucket = max_time_bucket / num_buckets;
- cout << "Max latency during training: " << tot_results.m_worst / 1.0E6 << "ms" << endl;
+ // Sets warn_latency to 2 * worst from the training round.
+ warn_latency = 2 * tot_results.worst();
+ cout << "Max latency during training: " << tot_results.worst() / 1.0E6 << "ms" << endl;
} else {
- tot_results.dump();
+ tot_results.dump();
}
}
@@ -340,8 +348,7 @@
int payload_size = 0;
bool cs_pair = false;
bool training_round = false;
- (void)argc;
- (void)argv;
+ int max_time_us;
// Parse arguments.
for (int i = 1; i < argc; i++) {
@@ -351,46 +358,65 @@
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/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/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/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/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/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 162a7bd..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 ---
@@ -3841,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;
}
@@ -4003,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;
}
@@ -4115,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;
}
@@ -6762,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/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/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h
index c076d33..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; }
@@ -454,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/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/tests/CursorInputMapper_test.cpp b/services/inputflinger/tests/CursorInputMapper_test.cpp
index f3a6f01..66c3256 100644
--- a/services/inputflinger/tests/CursorInputMapper_test.cpp
+++ b/services/inputflinger/tests/CursorInputMapper_test.cpp
@@ -24,12 +24,14 @@
#include <android-base/logging.h>
#include <com_android_input_flags.h>
#include <gtest/gtest.h>
+#include <input/DisplayViewport.h>
#include <linux/input-event-codes.h>
#include <linux/input.h>
#include <utils/Timers.h>
#include "FakePointerController.h"
#include "InputMapperTest.h"
+#include "InputReaderBase.h"
#include "InterfaceMocks.h"
#include "NotifyArgs.h"
#include "TestEventMatchers.h"
@@ -53,10 +55,76 @@
constexpr int32_t SECONDARY_DISPLAY_ID = DISPLAY_ID + 1;
constexpr int32_t DISPLAY_WIDTH = 480;
constexpr int32_t DISPLAY_HEIGHT = 800;
-constexpr std::optional<uint8_t> NO_PORT = std::nullopt; // no physical port is specified
constexpr int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
+namespace {
+
+DisplayViewport createPrimaryViewport(ui::Rotation orientation) {
+ const bool isRotated =
+ orientation == ui::Rotation::Rotation90 || orientation == ui::Rotation::Rotation270;
+ DisplayViewport v;
+ v.displayId = DISPLAY_ID;
+ v.orientation = orientation;
+ v.logicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.logicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.physicalRight = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.physicalBottom = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.deviceWidth = isRotated ? DISPLAY_HEIGHT : DISPLAY_WIDTH;
+ v.deviceHeight = isRotated ? DISPLAY_WIDTH : DISPLAY_HEIGHT;
+ v.isActive = true;
+ v.uniqueId = "local:1";
+ return v;
+}
+
+DisplayViewport createSecondaryViewport() {
+ DisplayViewport v;
+ v.displayId = SECONDARY_DISPLAY_ID;
+ v.orientation = ui::Rotation::Rotation0;
+ v.logicalRight = DISPLAY_HEIGHT;
+ v.logicalBottom = DISPLAY_WIDTH;
+ v.physicalRight = DISPLAY_HEIGHT;
+ v.physicalBottom = DISPLAY_WIDTH;
+ v.deviceWidth = DISPLAY_HEIGHT;
+ v.deviceHeight = DISPLAY_WIDTH;
+ v.isActive = true;
+ v.uniqueId = "local:2";
+ v.type = ViewportType::EXTERNAL;
+ return v;
+}
+
+/**
+ * A fake InputDeviceContext that allows the associated viewport to be specified for the mapper.
+ *
+ * This is currently necessary because InputMapperUnitTest doesn't register the mappers it creates
+ * with the InputDevice object, meaning that InputDevice::isIgnored becomes true, and the input
+ * device doesn't set its associated viewport when it's configured.
+ *
+ * TODO(b/319217713): work out a way to avoid this fake.
+ */
+class ViewportFakingInputDeviceContext : public InputDeviceContext {
+public:
+ ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
+ DisplayViewport viewport)
+ : InputDeviceContext(device, eventHubId), mAssociatedViewport(viewport) {}
+
+ ViewportFakingInputDeviceContext(InputDevice& device, int32_t eventHubId,
+ ui::Rotation orientation)
+ : ViewportFakingInputDeviceContext(device, eventHubId,
+ createPrimaryViewport(orientation)) {}
+
+ std::optional<DisplayViewport> getAssociatedViewport() const override {
+ return mAssociatedViewport;
+ }
+
+ void setViewport(const DisplayViewport& viewport) { mAssociatedViewport = viewport; }
+
+private:
+ DisplayViewport mAssociatedViewport;
+};
+
+} // namespace
+
namespace input_flags = com::android::input::flags;
/**
@@ -84,9 +152,7 @@
.WillRepeatedly(Return(false));
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT,
- ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
}
void createMapper() {
@@ -108,6 +174,19 @@
// Check that generation also got bumped
ASSERT_GT(mDevice->getGeneration(), generation);
}
+
+ void testMotionRotation(int32_t originalX, int32_t originalY, int32_t rotatedX,
+ int32_t rotatedY) {
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, originalX);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, originalY);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_MOVE),
+ WithCoords(float(rotatedX) / TRACKBALL_MOVEMENT_THRESHOLD,
+ float(rotatedY) / TRACKBALL_MOVEMENT_THRESHOLD)))));
+ }
};
class CursorInputMapperUnitTest : public CursorInputMapperUnitTestBase {
@@ -275,8 +354,7 @@
// When the bounds are set, then there should be a valid motion range.
mFakePointerController->setBounds(1, 2, 800 - 1, 480 - 1);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
std::list<NotifyArgs> args =
mMapper->reconfigure(systemTime(), mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -490,6 +568,177 @@
args.clear();
}
+TEST_F(CursorInputMapperUnitTest, ProcessShouldNotRotateMotionsWhenOrientationAware) {
+ // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
+ // need to be rotated.
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ mPropertyMap.addProperty("cursor.orientationAware", "1");
+ createDevice();
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation90);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
+}
+
+TEST_F(CursorInputMapperUnitTest, ProcessShouldRotateMotionsWhenNotOrientationAware) {
+ // Since InputReader works in the un-rotated coordinate space, only devices that are not
+ // orientation-aware are affected by display rotation.
+ mPropertyMap.addProperty("cursor.mode", "navigation");
+ createDevice();
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, ui::Rotation::Rotation0);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, 1));
+
+ deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation90));
+ std::list<NotifyArgs> args =
+ mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, -1, -1));
+
+ deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation180));
+ args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, 1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, -1));
+
+ deviceContext.setViewport(createPrimaryViewport(ui::Rotation::Rotation270));
+ args = mMapper->reconfigure(ARBITRARY_TIME, mReaderConfiguration,
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, 1, 1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 1, 1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, 0, 0, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 1, -1, -1, -1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation( 0, -1, -1, 0));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, -1, -1, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 0, 0, 1));
+ ASSERT_NO_FATAL_FAILURE(testMotionRotation(-1, 1, 1, 1));
+}
+
+TEST_F(CursorInputMapperUnitTest, PointerCaptureDisablesOrientationChanges) {
+ mPropertyMap.addProperty("cursor.mode", "pointer");
+ DisplayViewport viewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ mFakePointerController->setDisplayViewport(viewport);
+ mReaderConfiguration.setDisplayViewports({viewport});
+ createMapper();
+
+ // Verify that the coordinates are rotated.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithRelativeMotion(-20.0f, 10.0f)))));
+
+ // Enable Pointer Capture.
+ setPointerCapture(true);
+
+ // Move and verify rotation is not applied.
+ args = process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(ACTION_MOVE),
+ WithSource(AINPUT_SOURCE_MOUSE_RELATIVE),
+ WithCoords(10.0f, 20.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdNoAssociatedViewport) {
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+ // Set up the secondary display as the display on which the pointer should be shown. The
+ // InputDevice is not associated with any display.
+ mFakePointerController->setDisplayViewport(secondaryViewport);
+ mFakePointerController->setPosition(100, 200);
+ createMapper();
+
+ // Ensure input events are generated for the secondary display.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdWithAssociatedViewport) {
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+ // Set up the secondary display as the display on which the pointer should be shown.
+ mFakePointerController->setDisplayViewport(secondaryViewport);
+ mFakePointerController->setPosition(100, 200);
+ createDevice();
+ // Associate the InputDevice with the secondary display.
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ // Ensure input events are generated for the secondary display.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(110.0f, 220.0f)))));
+ ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
+}
+
+TEST_F(CursorInputMapperUnitTest, ConfigureDisplayIdIgnoresEventsForMismatchedPointerDisplay) {
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+ // Set up the primary display as the display on which the pointer should be shown.
+ mFakePointerController->setDisplayViewport(primaryViewport);
+ createDevice();
+ // Associate the InputDevice with the secondary display.
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ // The mapper should not generate any events because it is associated with a display that is
+ // different from the pointer display.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args, testing::IsEmpty());
+}
+
TEST_F(CursorInputMapperUnitTest, ProcessShouldHandleAllButtons) {
mPropertyMap.addProperty("cursor.mode", "pointer");
createMapper();
@@ -728,8 +977,7 @@
// When the viewport and the default pointer display ID is set, then there should be a valid
// motion range.
mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_0,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
std::list<NotifyArgs> args =
mMapper->reconfigure(systemTime(), mReaderConfiguration,
InputReaderConfiguration::Change::DISPLAY_INFO);
@@ -746,6 +994,54 @@
AINPUT_SOURCE_MOUSE, 0.0f, 1.0f, 0.0f, 0.0f));
}
+TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+ // Set up the secondary display as the display on which the pointer should be shown.
+ // The InputDevice is not associated with any display.
+ mFakePointerController->setDisplayViewport(secondaryViewport);
+ mFakePointerController->setPosition(100, 200);
+ createDevice();
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ std::list<NotifyArgs> args;
+ // Ensure input events are generated for the secondary display.
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f)))));
+}
+
+TEST_F(CursorInputMapperUnitTestWithChoreographer,
+ ConfigureDisplayIdShouldGenerateEventForMismatchedPointerDisplay) {
+ DisplayViewport primaryViewport = createPrimaryViewport(ui::Rotation::Rotation90);
+ DisplayViewport secondaryViewport = createSecondaryViewport();
+ mReaderConfiguration.setDisplayViewports({primaryViewport, secondaryViewport});
+ // Set up the primary display as the display on which the pointer should be shown.
+ mFakePointerController->setDisplayViewport(primaryViewport);
+ createDevice();
+ // Associate the InputDevice with the secondary display.
+ ViewportFakingInputDeviceContext deviceContext(*mDevice, EVENTHUB_ID, secondaryViewport);
+ mMapper = createInputMapper<CursorInputMapper>(deviceContext, mReaderConfiguration);
+
+ // With PointerChoreographer enabled, there could be a PointerController for the associated
+ // display even if it is different from the pointer display. So the mapper should generate an
+ // event.
+ std::list<NotifyArgs> args;
+ args += process(ARBITRARY_TIME, EV_REL, REL_X, 10);
+ args += process(ARBITRARY_TIME, EV_REL, REL_Y, 20);
+ args += process(ARBITRARY_TIME, EV_SYN, SYN_REPORT, 0);
+ ASSERT_THAT(args,
+ ElementsAre(VariantWith<NotifyMotionArgs>(
+ AllOf(WithMotionAction(HOVER_MOVE), WithSource(AINPUT_SOURCE_MOUSE),
+ WithDisplayId(SECONDARY_DISPLAY_ID), WithCoords(0.0f, 0.0f)))));
+}
+
TEST_F(CursorInputMapperUnitTestWithChoreographer, ProcessShouldHandleAllButtonsWithZeroCoords) {
mPropertyMap.addProperty("cursor.mode", "pointer");
createMapper();
@@ -959,14 +1255,11 @@
TEST_F(CursorInputMapperUnitTestWithChoreographer, ConfigureDisplayIdNoAssociatedViewport) {
// Set up the default display.
mFakePolicy->clearViewports();
- mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT, ui::ROTATION_90,
- /*isActive=*/true, "local:0", NO_PORT, ViewportType::INTERNAL);
+ mFakePolicy->addDisplayViewport(createPrimaryViewport(ui::Rotation::Rotation0));
// Set up the secondary display as the display on which the pointer should be shown.
// The InputDevice is not associated with any display.
- mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
- ui::ROTATION_0, /*isActive=*/true, "local:1", NO_PORT,
- ViewportType::EXTERNAL);
+ mFakePolicy->addDisplayViewport(createSecondaryViewport());
mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
createMapper();
diff --git a/services/inputflinger/tests/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 5f43bd2..2aecab9 100644
--- a/services/inputflinger/tests/InputMapperTest.cpp
+++ b/services/inputflinger/tests/InputMapperTest.cpp
@@ -21,8 +21,11 @@
#include <ui/Rotation.h>
#include <utils/Timers.h>
+#include "NotifyArgs.h"
+
namespace android {
+using testing::_;
using testing::Return;
void InputMapperUnitTest::SetUpWithBus(int bus) {
@@ -53,13 +56,14 @@
/*generation=*/2, mIdentifier);
mDevice->addEmptyEventHubDevice(EVENTHUB_ID);
mDeviceContext = std::make_unique<InputDeviceContext>(*mDevice, EVENTHUB_ID);
- std::list<NotifyArgs> _ =
+ std::list<NotifyArgs> args =
mDevice->configure(systemTime(), mReaderConfiguration, /*changes=*/{});
+ ASSERT_THAT(args, testing::ElementsAre(testing::VariantWith<NotifyDeviceResetArgs>(_)));
}
void InputMapperUnitTest::setupAxis(int axis, bool valid, int32_t min, int32_t max,
int32_t resolution) {
- EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, testing::_))
+ EXPECT_CALL(mMockEventHub, getAbsoluteAxisInfo(EVENTHUB_ID, axis, _))
.WillRepeatedly([=](int32_t, int32_t, RawAbsoluteAxisInfo* outAxisInfo) {
outAxisInfo->valid = valid;
outAxisInfo->minValue = min;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index 81f57bd..228583a 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1350,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();
@@ -1381,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);
@@ -2413,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.
@@ -4162,319 +4177,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, Process_WhenOrientationAware_ShouldNotRotateMotions) {
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
- addConfigurationProperty("cursor.mode", "navigation");
- // InputReader works in the un-rotated coordinate space, so orientation-aware devices do not
- // need to be rotated.
- addConfigurationProperty("cursor.orientationAware", "1");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
-}
-
-TEST_F(CursorInputMapperTest, Process_WhenNotOrientationAware_ShouldRotateMotions) {
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, DISPLAY_UNIQUE_ID);
- addConfigurationProperty("cursor.mode", "navigation");
- // Since InputReader works in the un-rotated coordinate space, only devices that are not
- // orientation-aware are affected by display rotation.
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- clearViewports();
- prepareDisplay(ui::ROTATION_0);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, 1));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_90);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, -1, -1));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_180);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, 1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, -1));
-
- clearViewports();
- prepareDisplay(ui::ROTATION_270);
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, 1, 1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 1, 1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, 0, 0, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 1, -1, -1, -1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, 0, -1, -1, 0));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, -1, -1, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 0, 0, 1));
- ASSERT_NO_FATAL_FAILURE(testMotionRotation(mapper, -1, 1, 1, 1));
-}
-
-TEST_F(CursorInputMapperTest, PointerCaptureDisablesOrientationChanges) {
- addConfigurationProperty("cursor.mode", "pointer");
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- NotifyDeviceResetArgs resetArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
- ASSERT_EQ(DEVICE_ID, resetArgs.deviceId);
-
- // Ensure the display is rotated.
- prepareDisplay(ui::ROTATION_90);
-
- NotifyMotionArgs args;
-
- // Verify that the coordinates are rotated.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action);
- ASSERT_EQ(-20, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X));
- ASSERT_EQ(10, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y));
-
- // Enable Pointer Capture.
- mFakePolicy->setPointerCapture(true);
- configureDevice(InputReaderConfiguration::Change::POINTER_CAPTURE);
- NotifyPointerCaptureChangedArgs captureArgs;
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyCaptureWasCalled(&captureArgs));
- ASSERT_TRUE(captureArgs.request.enable);
-
- // Move and verify rotation is not applied.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&args));
- ASSERT_EQ(AINPUT_SOURCE_MOUSE_RELATIVE, args.source);
- ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action);
- ASSERT_EQ(10, args.pointerCoords[0].getX());
- ASSERT_EQ(20, args.pointerCoords[0].getY());
-}
-
-TEST_F(CursorInputMapperTest, ConfigureDisplayId_NoAssociatedViewport) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_90);
-
- // Set up the secondary display as the display on which the pointer should be shown.
- // The InputDevice is not associated with any display.
- prepareSecondaryDisplay();
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
- // Ensure input events are generated for the secondary display.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-TEST_F(CursorInputMapperTest, ConfigureDisplayId_WithAssociatedViewport) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_90);
-
- // Set up the secondary display as the display on which the pointer should be shown,
- // and associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(110.0f, 220.0f))));
- ASSERT_NO_FATAL_FAILURE(mFakePointerController->assertPosition(110.0f, 220.0f));
-}
-
-TEST_F(CursorInputMapperTest, ConfigureDisplayId_IgnoresEventsForMismatchedPointerDisplay) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(ui::ROTATION_90);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
-
- // Associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- // The mapper should not generate any events because it is associated with a display that is
- // different from the pointer display.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
-}
-
-// --- CursorInputMapperTestWithChoreographer ---
-
-// TODO(b/311416205): De-duplicate the test cases after the refactoring is complete and the flagging
-// logic can be removed.
-class CursorInputMapperTestWithChoreographer : public CursorInputMapperTestBase {
-protected:
- void SetUp() override {
- input_flags::enable_pointer_choreographer(true);
- CursorInputMapperTestBase::SetUp();
- }
-};
-
-TEST_F(CursorInputMapperTestWithChoreographer, ConfigureDisplayIdWithAssociatedViewport) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display.
- prepareDisplay(ui::ROTATION_90);
-
- // Set up the secondary display as the display on which the pointer should be shown,
- // and associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->setDefaultPointerDisplayId(SECONDARY_DISPLAY_ID);
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- mFakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1);
- mFakePointerController->setPosition(100, 200);
-
- // Ensure input events are generated with associated display ID but not with coords,
- // because the coords will be decided later by PointerChoreographer.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(0.0f, 0.0f))));
-}
-
-TEST_F(CursorInputMapperTestWithChoreographer,
- ConfigureDisplayIdShouldGenerateEventWithMismatchedPointerDisplay) {
- CursorInputMapper& mapper = constructAndAddMapper<CursorInputMapper>();
-
- // Set up the default display as the display on which the pointer should be shown.
- prepareDisplay(ui::ROTATION_90);
- mFakePolicy->setDefaultPointerDisplayId(DISPLAY_ID);
-
- // Associate the InputDevice with the secondary display.
- prepareSecondaryDisplay();
- mFakePolicy->addInputUniqueIdAssociation(DEVICE_LOCATION, SECONDARY_DISPLAY_UNIQUE_ID);
- configureDevice(InputReaderConfiguration::Change::DISPLAY_INFO);
-
- // With PointerChoreographer enabled, there could be a PointerController for the associated
- // display even if it is different from the pointer display. So the mapper should generate an
- // event.
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_X, 10);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_REL, REL_Y, 20);
- process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_REPORT, 0);
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(AMOTION_EVENT_ACTION_HOVER_MOVE),
- WithSource(AINPUT_SOURCE_MOUSE), WithDisplayId(SECONDARY_DISPLAY_ID),
- WithCoords(0.0f, 0.0f))));
-}
-
// --- TouchInputMapperTest ---
class TouchInputMapperTest : public InputMapperTest {
diff --git a/services/inputflinger/tests/PointerChoreographer_test.cpp b/services/inputflinger/tests/PointerChoreographer_test.cpp
index 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/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/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/surfaceflinger_flags.aconfig b/services/surfaceflinger/surfaceflinger_flags.aconfig
index 79379fe..f097a13 100644
--- a/services/surfaceflinger/surfaceflinger_flags.aconfig
+++ b/services/surfaceflinger/surfaceflinger_flags.aconfig
@@ -150,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/LayerSnapshotTest.cpp b/services/surfaceflinger/tests/unittests/LayerSnapshotTest.cpp
index 55b20b3..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));
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);