Merge "Introduce thread safety to InputDeviceMetricsCollector" into main
diff --git a/cmds/lshal/Timeout.h b/cmds/lshal/Timeout.h
index 012a5d5..e8d22d9 100644
--- a/cmds/lshal/Timeout.h
+++ b/cmds/lshal/Timeout.h
@@ -72,14 +72,10 @@
return false;
}
bool success = state.wait(now + delay);
- if (success) {
- pthread_join(thread, nullptr);
- } else {
- // b/311143089: Abandon this background thread. Resources for a detached
- // thread are cleaned up when it is terminated. If the background thread
- // is stalled, it will be terminated when returning from main().
- pthread_detach(thread);
+ if (!success) {
+ pthread_kill(thread, SIGINT);
}
+ pthread_join(thread, nullptr);
return success;
}
diff --git a/cmds/lshal/main.cpp b/cmds/lshal/main.cpp
index a44f467..366c938 100644
--- a/cmds/lshal/main.cpp
+++ b/cmds/lshal/main.cpp
@@ -18,6 +18,5 @@
int main(int argc, char **argv) {
using namespace ::android::lshal;
- // Background pthreads from timeout() are destroyed upon returning from main().
return Lshal{}.main(Arg{argc, argv});
}
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 11e8120..90c9ff8 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -106,6 +106,17 @@
header_libs: [
"libbinder_headers",
],
+
+ cflags: [
+ "-Wextra",
+ "-Wextra-semi",
+ "-Werror",
+ "-Wzero-as-null-pointer-constant",
+ "-Wreorder-init-list",
+ "-Wunused-const-variable",
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
+ "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
+ ],
}
cc_defaults {
@@ -135,15 +146,6 @@
export_aidl_headers: true,
},
- cflags: [
- "-Wextra",
- "-Wextra-semi",
- "-Werror",
- "-Wzero-as-null-pointer-constant",
- "-Wreorder-init-list",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
- "-DANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION",
- ],
product_variables: {
debuggable: {
cflags: [
@@ -220,6 +222,10 @@
cflags: [
"-DBINDER_RPC_SINGLE_THREADED",
+ "-DBINDER_ENABLE_LIBLOG_ASSERT",
+ "-DBINDER_DISABLE_NATIVE_HANDLE",
+ "-DBINDER_DISABLE_BLOB",
+ "-DBINDER_NO_LIBBASE",
// Trusty libbinder uses vendor stability for its binders
"-D__ANDROID_VNDK__",
"-U__ANDROID__",
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 146ddef..c1770b3 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -114,7 +114,7 @@
constexpr size_t kMaxFds = 1024;
// Maximum size of a blob to transfer in-place.
-static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
+[[maybe_unused]] static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;
#if defined(__BIONIC__)
static void FdTag(int fd, const void* old_addr, const void* new_addr) {
@@ -886,6 +886,9 @@
}
#ifdef BINDER_WITH_KERNEL_IPC
+
+#if defined(__ANDROID__)
+
#if defined(__ANDROID_VNDK__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#elif defined(__ANDROID_RECOVERY__)
@@ -893,6 +896,14 @@
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
#endif
+
+#else // ANDROID not defined
+
+// If kernel binder is used in new environments, we need to make sure it's separated
+// out and has a separate header.
+constexpr int32_t kHeader = B_PACK_CHARS('U', 'N', 'K', 'N');
+#endif
+
#endif // BINDER_WITH_KERNEL_IPC
// Write RPC headers. (previously just the interface token)
diff --git a/libs/binder/file.cpp b/libs/binder/file.cpp
index bac667e..6e450b9 100644
--- a/libs/binder/file.cpp
+++ b/libs/binder/file.cpp
@@ -18,6 +18,8 @@
#ifdef BINDER_NO_LIBBASE
+#include "Utils.h"
+
#include <stdint.h>
// clang-format off
diff --git a/libs/binder/include/binder/Delegate.h b/libs/binder/include/binder/Delegate.h
index 8b3fc1c..ad5a6a3 100644
--- a/libs/binder/include/binder/Delegate.h
+++ b/libs/binder/include/binder/Delegate.h
@@ -18,6 +18,11 @@
#include <binder/IBinder.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
#ifndef __BIONIC__
#ifndef __assert
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 4786c89..14edf2b 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -31,6 +31,11 @@
#include <stdint.h>
#include <sys/cdefs.h>
+#if !defined(__BIONIC__) && defined(BINDER_ENABLE_LIBLOG_ASSERT)
+#include <log/log.h>
+#define __assert(file, line, message) LOG_ALWAYS_FATAL(file ":" #line ": " message)
+#endif
+
__BEGIN_DECLS
#ifndef __BIONIC__
diff --git a/libs/binder/tests/binderRpcTestServiceTrusty.cpp b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
index aaca8d0..8346b36 100644
--- a/libs/binder/tests/binderRpcTestServiceTrusty.cpp
+++ b/libs/binder/tests/binderRpcTestServiceTrusty.cpp
@@ -77,16 +77,14 @@
// Message size needs to be large enough to cover all messages sent by the
// tests: SendAndGetResultBackBig sends two large strings.
constexpr size_t max_msg_size = 4096;
- auto serverOrErr =
+ auto server =
RpcServerTrusty::make(hset, serverInfo.port->c_str(),
std::shared_ptr<const RpcServerTrusty::PortAcl>(&port_acl),
max_msg_size);
- if (!serverOrErr.ok()) {
- TLOGE("Failed to create RpcServer (%d)\n", serverOrErr.error());
+ if (server == nullptr) {
return EXIT_FAILURE;
}
- auto server = std::move(*serverOrErr);
serverInfo.server = server;
if (!serverInfo.server->setProtocolVersion(serverVersion)) {
return EXIT_FAILURE;
diff --git a/libs/binder/trusty/RpcServerTrusty.cpp b/libs/binder/trusty/RpcServerTrusty.cpp
index 0872b90..1f857a0 100644
--- a/libs/binder/trusty/RpcServerTrusty.cpp
+++ b/libs/binder/trusty/RpcServerTrusty.cpp
@@ -27,12 +27,11 @@
#include "../RpcState.h"
#include "TrustyStatus.h"
-using android::base::unexpected;
using android::binder::unique_fd;
namespace android {
-android::base::expected<sp<RpcServerTrusty>, int> RpcServerTrusty::make(
+sp<RpcServerTrusty> RpcServerTrusty::make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize, std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory) {
// Default is without TLS.
@@ -40,18 +39,21 @@
rpcTransportCtxFactory = RpcTransportCtxFactoryTipcTrusty::make();
auto ctx = rpcTransportCtxFactory->newServerCtx();
if (ctx == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server context");
+ return nullptr;
}
auto srv = sp<RpcServerTrusty>::make(std::move(ctx), std::move(portName), std::move(portAcl),
msgMaxSize);
if (srv == nullptr) {
- return unexpected(ERR_NO_MEMORY);
+ ALOGE("Failed to create RpcServerTrusty: can't create server object");
+ return nullptr;
}
int rc = tipc_add_service(handleSet, &srv->mTipcPort, 1, 0, &kTipcOps);
if (rc != NO_ERROR) {
- return unexpected(rc);
+ ALOGE("Failed to create RpcServerTrusty: can't add service: %d", rc);
+ return nullptr;
}
return srv;
}
diff --git a/libs/binder/trusty/include/binder/RpcServerTrusty.h b/libs/binder/trusty/include/binder/RpcServerTrusty.h
index 7382a30..f35d6c2 100644
--- a/libs/binder/trusty/include/binder/RpcServerTrusty.h
+++ b/libs/binder/trusty/include/binder/RpcServerTrusty.h
@@ -16,7 +16,6 @@
#pragma once
-#include <android-base/expected.h>
#include <binder/IBinder.h>
#include <binder/RpcServer.h>
#include <binder/RpcSession.h>
@@ -53,7 +52,7 @@
* The caller is responsible for calling tipc_run_event_loop() to start
* the TIPC event loop after creating one or more services here.
*/
- static android::base::expected<sp<RpcServerTrusty>, int> make(
+ static sp<RpcServerTrusty> make(
tipc_hset* handleSet, std::string&& portName, std::shared_ptr<const PortAcl>&& portAcl,
size_t msgMaxSize,
std::unique_ptr<RpcTransportCtxFactory> rpcTransportCtxFactory = nullptr);
diff --git a/libs/binder/trusty/include/log/log.h b/libs/binder/trusty/include/log/log.h
index de84617..bf877a3 100644
--- a/libs/binder/trusty/include/log/log.h
+++ b/libs/binder/trusty/include/log/log.h
@@ -120,9 +120,3 @@
do { \
TLOGE("android_errorWriteLog: tag:%x subTag:%s\n", tag, subTag); \
} while (0)
-
-// Override the definition of __assert from binder_status.h
-#ifndef __BIONIC__
-#undef __assert
-#define __assert(file, line, str) LOG_ALWAYS_FATAL("%s:%d: %s", file, line, str)
-#endif // __BIONIC__
diff --git a/libs/binder/trusty/kernel/rules.mk b/libs/binder/trusty/kernel/rules.mk
index 69737fa..2a13ead 100644
--- a/libs/binder/trusty/kernel/rules.mk
+++ b/libs/binder/trusty/kernel/rules.mk
@@ -18,9 +18,9 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
@@ -35,17 +35,14 @@
$(LIBBINDER_DIR)/Stability.cpp \
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_DEFINES += \
LK_DEBUGLEVEL_NO_ALIASES=1 \
@@ -59,14 +56,17 @@
$(LIBBINDER_DIR)/include \
$(LIBBINDER_DIR)/ndk/include_cpp \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
GLOBAL_COMPILEFLAGS += \
-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION \
-DBINDER_NO_KERNEL_IPC \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
-D__ANDROID_VNDK__ \
MODULE_DEPS += \
diff --git a/libs/binder/trusty/rules.mk b/libs/binder/trusty/rules.mk
index 96c66a8..e2b386d 100644
--- a/libs/binder/trusty/rules.mk
+++ b/libs/binder/trusty/rules.mk
@@ -18,9 +18,9 @@
MODULE := $(LOCAL_DIR)
LIBBINDER_DIR := frameworks/native/libs/binder
+# TODO(b/302723053): remove libbase after aidl prebuilt gets updated to December release
LIBBASE_DIR := system/libbase
-LIBCUTILS_DIR := system/core/libcutils
-LIBUTILS_DIR := system/core/libutils
+LIBUTILS_BINDER_DIR := system/core/libutils/binder
FMTLIB_DIR := external/fmtlib
MODULE_SRCS := \
@@ -43,24 +43,20 @@
$(LIBBINDER_DIR)/Status.cpp \
$(LIBBINDER_DIR)/Utils.cpp \
$(LIBBINDER_DIR)/file.cpp \
- $(LIBBASE_DIR)/hex.cpp \
- $(LIBBASE_DIR)/stringprintf.cpp \
- $(LIBUTILS_DIR)/binder/Errors.cpp \
- $(LIBUTILS_DIR)/binder/RefBase.cpp \
- $(LIBUTILS_DIR)/binder/SharedBuffer.cpp \
- $(LIBUTILS_DIR)/binder/String16.cpp \
- $(LIBUTILS_DIR)/binder/String8.cpp \
- $(LIBUTILS_DIR)/binder/StrongPointer.cpp \
- $(LIBUTILS_DIR)/binder/Unicode.cpp \
- $(LIBUTILS_DIR)/binder/VectorImpl.cpp \
- $(LIBUTILS_DIR)/misc.cpp \
+ $(LIBUTILS_BINDER_DIR)/Errors.cpp \
+ $(LIBUTILS_BINDER_DIR)/RefBase.cpp \
+ $(LIBUTILS_BINDER_DIR)/SharedBuffer.cpp \
+ $(LIBUTILS_BINDER_DIR)/String16.cpp \
+ $(LIBUTILS_BINDER_DIR)/String8.cpp \
+ $(LIBUTILS_BINDER_DIR)/StrongPointer.cpp \
+ $(LIBUTILS_BINDER_DIR)/Unicode.cpp \
+ $(LIBUTILS_BINDER_DIR)/VectorImpl.cpp \
MODULE_EXPORT_INCLUDES += \
$(LOCAL_DIR)/include \
$(LIBBINDER_DIR)/include \
$(LIBBASE_DIR)/include \
- $(LIBCUTILS_DIR)/include \
- $(LIBUTILS_DIR)/include \
+ $(LIBUTILS_BINDER_DIR)/include \
$(FMTLIB_DIR)/include \
# The android/binder_to_string.h header is shared between libbinder and
@@ -70,6 +66,10 @@
MODULE_EXPORT_COMPILEFLAGS += \
-DBINDER_RPC_SINGLE_THREADED \
+ -DBINDER_ENABLE_LIBLOG_ASSERT \
+ -DBINDER_DISABLE_NATIVE_HANDLE \
+ -DBINDER_DISABLE_BLOB \
+ -DBINDER_NO_LIBBASE \
-D__ANDROID_VNDK__ \
# libbinder has some deprecated declarations that we want to produce warnings
diff --git a/libs/input/rust/input_verifier.rs b/libs/input/rust/input_verifier.rs
index b60d7c9..867af79 100644
--- a/libs/input/rust/input_verifier.rs
+++ b/libs/input/rust/input_verifier.rs
@@ -272,6 +272,10 @@
return false;
};
+ if pointers.len() != pointer_properties.len() {
+ return false;
+ }
+
for pointer_property in pointer_properties.iter() {
let pointer_id = pointer_property.id;
if !pointers.contains(&pointer_id) {
@@ -592,4 +596,43 @@
)
.is_ok());
}
+
+ // Send a MOVE event with incorrect number of pointers (one of the pointers is missing).
+ #[test]
+ fn move_with_wrong_number_of_pointers() {
+ let mut verifier = InputVerifier::new("Test", /*should_log*/ false);
+ let pointer_properties = Vec::from([RustPointerProperties { id: 0 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_DOWN,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // POINTER 1 DOWN
+ let two_pointer_properties =
+ Vec::from([RustPointerProperties { id: 0 }, RustPointerProperties { id: 1 }]);
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_POINTER_DOWN
+ | (1 << input_bindgen::AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ &two_pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_ok());
+ // MOVE event with 1 pointer missing (the pointer with id = 1). It should be rejected
+ assert!(verifier
+ .process_movement(
+ DeviceId(1),
+ Source::Touchscreen,
+ input_bindgen::AMOTION_EVENT_ACTION_MOVE,
+ &pointer_properties,
+ MotionFlags::empty(),
+ )
+ .is_err());
+ }
}
diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
index 2dd05f5..5a74a42 100644
--- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp
@@ -35,12 +35,10 @@
MultiTouchInputMapper::~MultiTouchInputMapper() {}
std::list<NotifyArgs> MultiTouchInputMapper::reset(nsecs_t when) {
- // The evdev multi-touch protocol does not allow userspace applications to query the initial or
- // current state of the pointers at any time. This means if we clear our accumulated state when
- // resetting the input mapper, there's no way to rebuild the full initial state of the pointers.
- // We can only wait for updates to all the pointers and axes. Rather than clearing the state and
- // rebuilding the state from scratch, we work around this kernel API limitation by never
- // fully clearing any state specific to the multi-touch protocol.
+ // TODO(b/291626046): Sync the MT state with the kernel using EVIOCGMTSLOTS.
+ mMultiTouchMotionAccumulator.reset(getDeviceContext());
+ mPointerIdBits.clear();
+
return TouchInputMapper::reset(when);
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
index b0fc903..d06514a 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.cpp
@@ -30,23 +30,29 @@
size_t slotCount, bool usingSlotsProtocol) {
mUsingSlotsProtocol = usingSlotsProtocol;
mSlots = std::vector<Slot>(slotCount);
+ reset(deviceContext);
+}
- mCurrentSlot = -1;
- if (mUsingSlotsProtocol) {
- // Query the driver for the current slot index and use it as the initial slot before we
- // start reading events from the device. It is possible that the current slot index will
- // not be the same as it was when the first event was written into the evdev buffer, which
- // means the input mapper could start out of sync with the initial state of the events in
- // the evdev buffer. In the extremely unlikely case that this happens, the data from two
- // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
- // touch point to "jump", but at least there will be no stuck touches.
- int32_t initialSlot;
- if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
- status == OK) {
- mCurrentSlot = initialSlot;
- } else {
- ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
- }
+void MultiTouchMotionAccumulator::reset(const InputDeviceContext& deviceContext) {
+ resetSlots();
+
+ if (!mUsingSlotsProtocol) {
+ return;
+ }
+
+ // Query the driver for the current slot index and use it as the initial slot before we
+ // start reading events from the device. It is possible that the current slot index will
+ // not be the same as it was when the first event was written into the evdev buffer, which
+ // means the input mapper could start out of sync with the initial state of the events in
+ // the evdev buffer. In the extremely unlikely case that this happens, the data from two
+ // slots will be confused until the next ABS_MT_SLOT event is received. This can cause the
+ // touch point to "jump", but at least there will be no stuck touches.
+ int32_t initialSlot;
+ if (const auto status = deviceContext.getAbsoluteAxisValue(ABS_MT_SLOT, &initialSlot);
+ status == OK) {
+ mCurrentSlot = initialSlot;
+ } else {
+ ALOGD("Could not retrieve current multi-touch slot index. status=%d", status);
}
}
diff --git a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
index 0e3e2bb..5b55e3d 100644
--- a/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
+++ b/services/inputflinger/reader/mapper/accumulator/MultiTouchMotionAccumulator.h
@@ -83,6 +83,7 @@
LOG_ALWAYS_FATAL_IF(index < 0 || index >= mSlots.size(), "Invalid index: %zu", index);
return mSlots[index];
}
+ void reset(const InputDeviceContext& deviceContext);
private:
int32_t mCurrentSlot;
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index e8b779a..e0a3e94 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -232,6 +232,11 @@
mResetWasCalled = false;
}
+ void assertResetWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mResetWasCalled) << "Expected reset to not have been called.";
+ }
+
void assertProcessWasCalled(RawEvent* outLastEvent = nullptr) {
std::unique_lock<std::mutex> lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
@@ -248,6 +253,11 @@
mProcessWasCalled = false;
}
+ void assertProcessWasNotCalled() {
+ std::scoped_lock lock(mLock);
+ ASSERT_FALSE(mProcessWasCalled) << "Expected process to not have been called.";
+ }
+
void setKeyCodeState(int32_t keyCode, int32_t state) {
mKeyCodeStates.replaceValueFor(keyCode, state);
}
@@ -2872,6 +2882,60 @@
ASSERT_EQ(DEVICE_BLUETOOTH_ADDRESS, *address);
}
+TEST_F(InputDeviceTest, KernelBufferOverflowResetsMappers) {
+ mFakePolicy->clearViewports();
+ FakeInputMapper& mapper =
+ mDevice->addMapper<FakeInputMapper>(EVENTHUB_ID, mFakePolicy->getReaderConfiguration(),
+ AINPUT_SOURCE_KEYBOARD);
+ std::list<NotifyArgs> unused =
+ mDevice->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(),
+ /*changes=*/{});
+
+ mapper.assertConfigureWasCalled();
+ mapper.assertResetWasNotCalled();
+
+ RawEvent event{.deviceId = EVENTHUB_ID,
+ .readTime = ARBITRARY_TIME,
+ .when = ARBITRARY_TIME,
+ .type = EV_SYN,
+ .code = SYN_REPORT,
+ .value = 0};
+
+ // Events are processed normally.
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+
+ // Simulate a kernel buffer overflow, which generates a SYN_DROPPED event.
+ // This should reset the mapper.
+ event.type = EV_SYN;
+ event.code = SYN_DROPPED;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+ mapper.assertResetWasCalled();
+
+ // All events until the next SYN_REPORT should be dropped.
+ event.type = EV_KEY;
+ event.code = KEY_A;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // We get the SYN_REPORT event now, which is not forwarded to mappers.
+ event.type = EV_SYN;
+ event.code = SYN_REPORT;
+ event.value = 0;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasNotCalled();
+
+ // The mapper receives events normally now.
+ event.type = EV_KEY;
+ event.code = KEY_B;
+ event.value = 1;
+ unused = mDevice->process(&event, /*count=*/1);
+ mapper.assertProcessWasCalled();
+}
+
// --- SwitchInputMapperTest ---
class SwitchInputMapperTest : public InputMapperTest {
@@ -10907,7 +10971,7 @@
ASSERT_EQ(uint32_t(1), motionArgs.getPointerCount());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState) {
+TEST_F(MultiTouchInputMapperTest, ResetClearsTouchState) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
@@ -10930,25 +10994,36 @@
ASSERT_NO_FATAL_FAILURE(
mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(ACTION_POINTER_1_DOWN)));
- // Reset the mapper. When the mapper is reset, we expect the current multi-touch state to be
- // preserved. Resetting should cancel the ongoing gesture.
+ // Reset the mapper. When the mapper is reset, the touch state is also cleared.
resetMapper(mapper, ARBITRARY_TIME);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
- // Send a sync to simulate an empty touch frame where nothing changes. The mapper should use
- // the existing touch state to generate a down event.
+ // Move the second slot pointer, and ensure there are no events, because the touch state was
+ // cleared and no slots should be in use.
processPosition(mapper, 301, 302);
processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Release both fingers.
+ processId(mapper, INVALID_TRACKING_ID);
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Start a new gesture, and ensure we get a DOWN event for it.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 200, 300);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
AllOf(WithMotionAction(AMOTION_EVENT_ACTION_DOWN), WithPressure(1.f))));
- ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
- AllOf(WithMotionAction(ACTION_POINTER_1_DOWN), WithPressure(1.f))));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
}
-TEST_F(MultiTouchInputMapperTest, Reset_PreservesLastTouchState_NoPointersDown) {
+TEST_F(MultiTouchInputMapperTest, ResetClearsTouchStateWithNoPointersDown) {
addConfigurationProperty("touch.deviceType", "touchScreen");
prepareDisplay(ui::ROTATION_0);
prepareAxes(POSITION | ID | SLOT | PRESSURE);
@@ -11076,6 +11151,66 @@
ASSERT_FALSE(fakePointerController->isPointerShown());
}
+TEST_F(MultiTouchInputMapperTest, SimulateKernelBufferOverflow) {
+ addConfigurationProperty("touch.deviceType", "touchScreen");
+ prepareDisplay(ui::ROTATION_0);
+ prepareAxes(POSITION | ID | SLOT | PRESSURE);
+ MultiTouchInputMapper& mapper = constructAndAddMapper<MultiTouchInputMapper>();
+
+ // First finger down.
+ processId(mapper, FIRST_TRACKING_ID);
+ processPosition(mapper, 100, 200);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // Assume the kernel buffer overflows, and we get a SYN_DROPPED event.
+ // This will reset the mapper, and thus also reset the touch state.
+ process(mapper, ARBITRARY_TIME, READ_TIME, EV_SYN, SYN_DROPPED, 0);
+ resetMapper(mapper, ARBITRARY_TIME);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_CANCEL)));
+
+ // Since the touch state was reset, it doesn't know which slots are active, so any movements
+ // are ignored.
+ processPosition(mapper, 101, 201);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+
+ // Second finger goes down. This is the first active finger, so we get a DOWN event.
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, SECOND_TRACKING_ID);
+ processPosition(mapper, 400, 500);
+ processPressure(mapper, RAW_PRESSURE_MAX);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_DOWN)));
+
+ // First slot is still ignored, only the second one is active.
+ processSlot(mapper, FIRST_SLOT);
+ processPosition(mapper, 102, 202);
+ processSlot(mapper, SECOND_SLOT);
+ processPosition(mapper, 401, 501);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(
+ WithMotionAction(AMOTION_EVENT_ACTION_MOVE)));
+
+ // Both slots up, so we get the UP event for the active pointer.
+ processSlot(mapper, FIRST_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSlot(mapper, SECOND_SLOT);
+ processId(mapper, INVALID_TRACKING_ID);
+ processSync(mapper);
+
+ ASSERT_NO_FATAL_FAILURE(
+ mFakeListener->assertNotifyMotionWasCalled(WithMotionAction(AMOTION_EVENT_ACTION_UP)));
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasNotCalled());
+}
+
// --- MultiTouchInputMapperTest_ExternalDevice ---
class MultiTouchInputMapperTest_ExternalDevice : public MultiTouchInputMapperTest {