From d0729e1ec2498d3c1dd780063085d352f3436423 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 5 Dec 2018 16:46:06 -0800 Subject: Remove unused function The function is unused. Remove it. Bug: none Test: presubmit only Change-Id: If5af47bbdb119b106021ea79da0bd4b06ea27548 --- include/input/Input.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index aa42db8ea8..a8c386e47e 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -328,8 +328,6 @@ struct PointerCoords { float getAxisValue(int32_t axis) const; status_t setAxisValue(int32_t axis, float value); - void scale(float globalScale); - // Scale the pointer coordinates according to a global scale and a // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. -- cgit v1.2.3-59-g8ed1b From 98318de954ba00293cfd179266f09f266dc1c82b Mon Sep 17 00:00:00 2001 From: chaviw Date: Wed, 19 May 2021 16:45:23 -0500 Subject: Renamed and moved InputWindow and related files In preparation for the hierarchy listener interface, moved the InputWindow structs into libgui and have libinput dependant on libgui. Also renamed InputWindow to exclude Input since it will be used for more generic purposes. Test: Builds and flashes Bug: 188792659 Change-Id: I24262cbc14d409c00273de0024a672394a959e5f --- include/ftl/Flags.h | 280 ++++++++++++++++++++ include/ftl/NamedEnum.h | 125 +++++++++ include/input/DisplayViewport.h | 3 +- include/input/Flags.h | 283 --------------------- include/input/Input.h | 15 -- include/input/InputApplication.h | 85 ------- include/input/InputWindow.h | 279 -------------------- include/input/NamedEnum.h | 128 ---------- libs/ftl/Android.bp | 10 + libs/ftl/Flags_test.cpp | 227 +++++++++++++++++ libs/ftl/NamedEnum_test.cpp | 101 ++++++++ libs/gui/Android.bp | 56 +++- libs/gui/BLASTBufferQueue.cpp | 6 +- libs/gui/LayerState.cpp | 27 +- libs/gui/SurfaceComposerClient.cpp | 16 +- libs/gui/WindowInfo.cpp | 223 ++++++++++++++++ libs/gui/android/gui/FocusRequest.aidl | 45 ++++ libs/gui/android/gui/InputApplicationInfo.aidl | 23 ++ libs/gui/android/gui/TouchOcclusionMode.aidl | 47 ++++ libs/gui/android/gui/WindowInfo.aidl | 19 ++ libs/gui/include/gui/ISurfaceComposer.h | 2 +- libs/gui/include/gui/InputApplication.h | 79 ++++++ libs/gui/include/gui/LayerState.h | 14 +- libs/gui/include/gui/SurfaceComposerClient.h | 6 +- libs/gui/include/gui/WindowInfo.h | 273 ++++++++++++++++++++ libs/gui/include/gui/constants.h | 37 +++ libs/gui/tests/Android.bp | 1 + libs/gui/tests/EndToEndNativeInputTest.cpp | 27 +- libs/gui/tests/WindowInfo_test.cpp | 133 ++++++++++ libs/input/Android.bp | 14 +- libs/input/Input.cpp | 1 + libs/input/InputDevice.cpp | 2 +- libs/input/InputTransport.cpp | 2 +- libs/input/InputWindow.cpp | 224 ---------------- libs/input/KeyCharacterMap.cpp | 5 +- libs/input/KeyLayoutMap.cpp | 2 +- libs/input/android/FocusRequest.aidl | 45 ---- libs/input/android/InputApplicationInfo.aidl | 23 -- libs/input/android/InputWindowInfo.aidl | 20 -- libs/input/android/os/IInputFlinger.aidl | 6 +- libs/input/android/os/TouchOcclusionMode.aidl | 47 ---- libs/input/tests/Android.bp | 10 +- libs/input/tests/Flags_test.cpp | 227 ----------------- libs/input/tests/InputEvent_test.cpp | 24 +- .../input/tests/InputPublisherAndConsumer_test.cpp | 1 + libs/input/tests/InputWindow_test.cpp | 129 ---------- libs/input/tests/NamedEnum_test.cpp | 101 -------- libs/input/tests/VelocityTracker_test.cpp | 7 +- libs/input/tests/VerifiedInputEvent_test.cpp | 8 +- libs/nativedisplay/AChoreographer.cpp | 1 - services/inputflinger/InputManager.cpp | 16 +- services/inputflinger/InputManager.h | 4 +- services/inputflinger/InputReaderBase.cpp | 2 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 11 +- services/inputflinger/dispatcher/DragState.cpp | 2 - services/inputflinger/dispatcher/DragState.h | 10 +- services/inputflinger/dispatcher/Entry.h | 2 +- services/inputflinger/dispatcher/FocusResolver.cpp | 15 +- services/inputflinger/dispatcher/FocusResolver.h | 18 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 233 ++++++++--------- services/inputflinger/dispatcher/InputDispatcher.h | 90 ++++--- services/inputflinger/dispatcher/InputTarget.h | 3 +- services/inputflinger/dispatcher/TouchState.cpp | 11 +- services/inputflinger/dispatcher/TouchState.h | 12 +- services/inputflinger/dispatcher/TouchedWindow.h | 6 +- .../dispatcher/include/InputDispatcherInterface.h | 11 +- .../include/InputDispatcherPolicyInterface.h | 2 +- services/inputflinger/host/InputFlinger.h | 4 +- services/inputflinger/reader/InputDevice.cpp | 2 +- .../reader/controller/PeripheralController.cpp | 2 +- services/inputflinger/reader/include/EventHub.h | 2 +- services/inputflinger/reader/include/InputDevice.h | 2 +- .../reader/mapper/TouchInputMapper.cpp | 2 +- services/inputflinger/tests/Android.bp | 1 + services/inputflinger/tests/FocusResolver_test.cpp | 19 +- .../inputflinger/tests/IInputFlingerQuery.aidl | 6 +- .../tests/InputClassifierConverter_test.cpp | 2 +- .../inputflinger/tests/InputClassifier_test.cpp | 1 + .../inputflinger/tests/InputDispatcher_test.cpp | 149 +++++------ .../tests/InputFlingerService_test.cpp | 52 ++-- services/inputflinger/tests/InputReader_test.cpp | 1 + services/surfaceflinger/Android.bp | 1 - services/surfaceflinger/BufferLayer.cpp | 4 +- .../compositionengine/impl/planner/LayerState.h | 2 +- services/surfaceflinger/Layer.cpp | 24 +- services/surfaceflinger/Layer.h | 18 +- services/surfaceflinger/LayerProtoHelper.cpp | 7 +- services/surfaceflinger/LayerProtoHelper.h | 4 +- services/surfaceflinger/Scheduler/Scheduler.cpp | 9 +- services/surfaceflinger/SurfaceFlinger.cpp | 7 +- .../tests/RefreshRateOverlay_test.cpp | 4 + .../surfaceflinger/tests/utils/CallbackUtils.h | 4 + 92 files changed, 2158 insertions(+), 2088 deletions(-) create mode 100644 include/ftl/Flags.h create mode 100644 include/ftl/NamedEnum.h delete mode 100644 include/input/Flags.h delete mode 100644 include/input/InputApplication.h delete mode 100644 include/input/InputWindow.h delete mode 100644 include/input/NamedEnum.h create mode 100644 libs/ftl/Flags_test.cpp create mode 100644 libs/ftl/NamedEnum_test.cpp create mode 100644 libs/gui/WindowInfo.cpp create mode 100644 libs/gui/android/gui/FocusRequest.aidl create mode 100644 libs/gui/android/gui/InputApplicationInfo.aidl create mode 100644 libs/gui/android/gui/TouchOcclusionMode.aidl create mode 100644 libs/gui/android/gui/WindowInfo.aidl create mode 100644 libs/gui/include/gui/InputApplication.h create mode 100644 libs/gui/include/gui/WindowInfo.h create mode 100644 libs/gui/include/gui/constants.h create mode 100644 libs/gui/tests/WindowInfo_test.cpp delete mode 100644 libs/input/InputWindow.cpp delete mode 100644 libs/input/android/FocusRequest.aidl delete mode 100644 libs/input/android/InputApplicationInfo.aidl delete mode 100644 libs/input/android/InputWindowInfo.aidl delete mode 100644 libs/input/android/os/TouchOcclusionMode.aidl delete mode 100644 libs/input/tests/Flags_test.cpp delete mode 100644 libs/input/tests/InputWindow_test.cpp delete mode 100644 libs/input/tests/NamedEnum_test.cpp (limited to 'include/input') diff --git a/include/ftl/Flags.h b/include/ftl/Flags.h new file mode 100644 index 0000000000..27c84769cb --- /dev/null +++ b/include/ftl/Flags.h @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include "utils/BitSet.h" + +#pragma once + +namespace android { + +namespace details { + +template +inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; + +template +constexpr auto generate_flag_values(std::integer_sequence seq) { + constexpr size_t count = seq.size(); + + std::array values{}; + for (size_t i = 0, v = 0; v < count; ++i) { + values[v++] = static_cast(T{1} << i); + } + + return values; +} + +template +inline constexpr auto flag_values = generate_flag_values( + std::make_integer_sequence, flag_count>{}); + +template +constexpr auto generate_flag_names(std::index_sequence) noexcept { + return std::array, sizeof...(I)>{ + {enum_value_name[I]>()...}}; +} + +template +inline constexpr auto flag_names = + generate_flag_names(std::make_index_sequence>{}); + +// A trait for determining whether a type is specifically an enum class or not. +template > +struct is_enum_class : std::false_type {}; + +// By definition, an enum class is an enum that is not implicitly convertible to its underlying +// type. +template +struct is_enum_class + : std::bool_constant>> {}; + +template +inline constexpr bool is_enum_class_v = is_enum_class::value; +} // namespace details + +template +constexpr auto flag_name() { + using F = decltype(V); + return details::enum_value_name(); +} + +template +constexpr std::optional flag_name(F flag) { + using U = std::underlying_type_t; + auto idx = static_cast(__builtin_ctzl(static_cast(flag))); + return details::flag_names[idx]; +} + +/* A class for handling flags defined by an enum or enum class in a type-safe way. */ +template +class Flags { + // F must be an enum or its underlying type is undefined. Theoretically we could specialize this + // further to avoid this restriction but in general we want to encourage the use of enums + // anyways. + static_assert(std::is_enum_v, "Flags type must be an enum"); + using U = typename std::underlying_type_t; + +public: + constexpr Flags(F f) : mFlags(static_cast(f)) {} + constexpr Flags() : mFlags(0) {} + constexpr Flags(const Flags& f) : mFlags(f.mFlags) {} + + // Provide a non-explicit construct for non-enum classes since they easily convert to their + // underlying types (e.g. when used with bitwise operators). For enum classes, however, we + // should force them to be explicitly constructed from their underlying types to make full use + // of the type checker. + template + constexpr Flags(T t, typename std::enable_if_t, T>* = nullptr) + : mFlags(t) {} + template + explicit constexpr Flags(T t, + typename std::enable_if_t, T>* = nullptr) + : mFlags(t) {} + + class Iterator { + // The type can't be larger than 64-bits otherwise it won't fit in BitSet64. + static_assert(sizeof(U) <= sizeof(uint64_t)); + + public: + Iterator(Flags flags) : mRemainingFlags(flags.mFlags) { (*this)++; } + Iterator() : mRemainingFlags(0), mCurrFlag(static_cast(0)) {} + + // Pre-fix ++ + Iterator& operator++() { + if (mRemainingFlags.isEmpty()) { + mCurrFlag = static_cast(0); + } else { + uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left + const U flag = 1 << (64 - bit - 1); + mCurrFlag = static_cast(flag); + } + return *this; + } + + // Post-fix ++ + Iterator operator++(int) { + Iterator iter = *this; + ++*this; + return iter; + } + + bool operator==(Iterator other) const { + return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags; + } + + bool operator!=(Iterator other) const { return !(*this == other); } + + F operator*() { return mCurrFlag; } + + // iterator traits + + // In the future we could make this a bidirectional const iterator instead of a forward + // iterator but it doesn't seem worth the added complexity at this point. This could not, + // however, be made a non-const iterator as assigning one flag to another is a non-sensical + // operation. + using iterator_category = std::input_iterator_tag; + using value_type = F; + // Per the C++ spec, because input iterators are not assignable the iterator's reference + // type does not actually need to be a reference. In fact, making it a reference would imply + // that modifying it would change the underlying Flags object, which is obviously wrong for + // the same reason this can't be a non-const iterator. + using reference = F; + using difference_type = void; + using pointer = void; + + private: + BitSet64 mRemainingFlags; + F mCurrFlag; + }; + + /* + * Tests whether the given flag is set. + */ + bool test(F flag) const { + U f = static_cast(flag); + return (f & mFlags) == f; + } + + /* Tests whether any of the given flags are set */ + bool any(Flags f) { return (mFlags & f.mFlags) != 0; } + + /* Tests whether all of the given flags are set */ + bool all(Flags f) { return (mFlags & f.mFlags) == f.mFlags; } + + Flags operator|(Flags rhs) const { return static_cast(mFlags | rhs.mFlags); } + Flags& operator|=(Flags rhs) { + mFlags = mFlags | rhs.mFlags; + return *this; + } + + Flags operator&(Flags rhs) const { return static_cast(mFlags & rhs.mFlags); } + Flags& operator&=(Flags rhs) { + mFlags = mFlags & rhs.mFlags; + return *this; + } + + Flags operator^(Flags rhs) const { return static_cast(mFlags ^ rhs.mFlags); } + Flags& operator^=(Flags rhs) { + mFlags = mFlags ^ rhs.mFlags; + return *this; + } + + Flags operator~() { return static_cast(~mFlags); } + + bool operator==(Flags rhs) const { return mFlags == rhs.mFlags; } + bool operator!=(Flags rhs) const { return !operator==(rhs); } + + Flags& operator=(const Flags& rhs) { + mFlags = rhs.mFlags; + return *this; + } + + Iterator begin() const { return Iterator(*this); } + + Iterator end() const { return Iterator(); } + + /* + * Returns the stored set of flags. + * + * Note that this returns the underlying type rather than the base enum class. This is because + * the value is no longer necessarily a strict member of the enum since the returned value could + * be multiple enum variants OR'd together. + */ + U get() const { return mFlags; } + + std::string string() const { + std::string result; + bool first = true; + U unstringified = 0; + for (const F f : *this) { + std::optional flagString = flag_name(f); + if (flagString) { + appendFlag(result, flagString.value(), first); + } else { + unstringified |= static_cast(f); + } + } + + if (unstringified != 0) { + appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); + } + + if (first) { + result += "0x0"; + } + + return result; + } + +private: + U mFlags; + + static void appendFlag(std::string& str, const std::string_view& flag, bool& first) { + if (first) { + first = false; + } else { + str += " | "; + } + str += flag; + } +}; + +// This namespace provides operator overloads for enum classes to make it easier to work with them +// as flags. In order to use these, add them via a `using namespace` declaration. +namespace flag_operators { + +template >> +inline Flags operator~(F f) { + using U = typename std::underlying_type_t; + return static_cast(~static_cast(f)); +} +template >> +Flags operator|(F lhs, F rhs) { + using U = typename std::underlying_type_t; + return static_cast(static_cast(lhs) | static_cast(rhs)); +} + +} // namespace flag_operators +} // namespace android diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h new file mode 100644 index 0000000000..f50ff46fe2 --- /dev/null +++ b/include/ftl/NamedEnum.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#pragma once + +namespace android { + +namespace details { +template +constexpr std::optional enum_value_name() { + // Should look something like (but all on one line): + // std::optional + // android::details::enum_value_name() + // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] + std::string_view view = __PRETTY_FUNCTION__; + size_t templateStart = view.rfind("["); + size_t templateEnd = view.rfind("]"); + if (templateStart == std::string::npos || templateEnd == std::string::npos) { + return std::nullopt; + } + + // Extract the template parameters without the enclosing braces. + // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE + view = view.substr(templateStart + 1, templateEnd - templateStart - 1); + size_t valStart = view.rfind("V = "); + if (valStart == std::string::npos) { + return std::nullopt; + } + + // Example (cont'd): V = android::test::TestEnums::ONE + view = view.substr(valStart); + size_t nameStart = view.rfind("::"); + if (nameStart == std::string::npos) { + return std::nullopt; + } + + // Chop off the initial "::" + nameStart += 2; + return view.substr(nameStart); +} + +template +constexpr auto generate_enum_values(std::integer_sequence seq) { + constexpr size_t count = seq.size(); + + std::array values{}; + for (size_t i = 0, v = 0; v < count; ++i) { + values[v++] = static_cast(T{0} + i); + } + + return values; +} + +template +inline constexpr auto enum_values = + generate_enum_values(std::make_integer_sequence, N>{}); + +template +constexpr auto generate_enum_names(std::index_sequence) noexcept { + return std::array, sizeof...(I)>{ + {enum_value_name[I]>()...}}; +} + +template +inline constexpr auto enum_names = generate_enum_names(std::make_index_sequence{}); + +} // namespace details + +class NamedEnum { +public: + // By default allowed enum value range is 0 ~ 7. + template + static constexpr size_t max = 8; + + template + static constexpr auto enum_name() { + using E = decltype(V); + return details::enum_value_name(); + } + + template + static constexpr std::optional enum_name(E val) { + auto idx = static_cast(val); + return idx < max ? details::enum_names>[idx] : std::nullopt; + } + + // Helper function for parsing enum value to string. + // Example : enum class TestEnums { ZERO = 0x0 }; + // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". + // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, + // it should be declared to specialized the maximum enum by below: + // template <> constexpr size_t NamedEnum::max = 16; + // If the enum class definition is sparse and contains enum values starting from a large value, + // Do not specialize it to a large number to avoid performance issues. + // The recommended maximum enum number to specialize is 64. + template + static const std::string string(E val, const char* fallbackFormat = "%02d") { + std::string result; + std::optional enumString = enum_name(val); + result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); + return result; + } +}; + +} // namespace android diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index 5e40ca7ece..a6213f3ddd 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -18,8 +18,9 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include +#include +#include #include -#include #include #include diff --git a/include/input/Flags.h b/include/input/Flags.h deleted file mode 100644 index b12a9ed2c5..0000000000 --- a/include/input/Flags.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include -#include - -#include "NamedEnum.h" -#include "utils/BitSet.h" - -#ifndef __UI_INPUT_FLAGS_H -#define __UI_INPUT_FLAGS_H - -namespace android { - -namespace details { - -template -inline constexpr auto flag_count = sizeof(F) * __CHAR_BIT__; - -template -constexpr auto generate_flag_values(std::integer_sequence seq) { - constexpr size_t count = seq.size(); - - std::array values{}; - for (size_t i = 0, v = 0; v < count; ++i) { - values[v++] = static_cast(T{1} << i); - } - - return values; -} - -template -inline constexpr auto flag_values = generate_flag_values( - std::make_integer_sequence, flag_count>{}); - -template -constexpr auto generate_flag_names(std::index_sequence) noexcept { - return std::array, sizeof...(I)>{ - {enum_value_name[I]>()...}}; -} - -template -inline constexpr auto flag_names = - generate_flag_names(std::make_index_sequence>{}); - -// A trait for determining whether a type is specifically an enum class or not. -template > -struct is_enum_class : std::false_type {}; - -// By definition, an enum class is an enum that is not implicitly convertible to its underlying -// type. -template -struct is_enum_class - : std::bool_constant>> {}; - -template -inline constexpr bool is_enum_class_v = is_enum_class::value; -} // namespace details - -template -constexpr auto flag_name() { - using F = decltype(V); - return details::enum_value_name(); -} - -template -constexpr std::optional flag_name(F flag) { - using U = std::underlying_type_t; - auto idx = static_cast(__builtin_ctzl(static_cast(flag))); - return details::flag_names[idx]; -} - -/* A class for handling flags defined by an enum or enum class in a type-safe way. */ -template -class Flags { - // F must be an enum or its underlying type is undefined. Theoretically we could specialize this - // further to avoid this restriction but in general we want to encourage the use of enums - // anyways. - static_assert(std::is_enum_v, "Flags type must be an enum"); - using U = typename std::underlying_type_t; - -public: - constexpr Flags(F f) : mFlags(static_cast(f)) {} - constexpr Flags() : mFlags(0) {} - constexpr Flags(const Flags& f) : mFlags(f.mFlags) {} - - // Provide a non-explicit construct for non-enum classes since they easily convert to their - // underlying types (e.g. when used with bitwise operators). For enum classes, however, we - // should force them to be explicitly constructed from their underlying types to make full use - // of the type checker. - template - constexpr Flags(T t, typename std::enable_if_t, T>* = nullptr) - : mFlags(t) {} - template - explicit constexpr Flags(T t, - typename std::enable_if_t, T>* = nullptr) - : mFlags(t) {} - - class Iterator { - // The type can't be larger than 64-bits otherwise it won't fit in BitSet64. - static_assert(sizeof(U) <= sizeof(uint64_t)); - - public: - Iterator(Flags flags) : mRemainingFlags(flags.mFlags) { (*this)++; } - Iterator() : mRemainingFlags(0), mCurrFlag(static_cast(0)) {} - - // Pre-fix ++ - Iterator& operator++() { - if (mRemainingFlags.isEmpty()) { - mCurrFlag = static_cast(0); - } else { - uint64_t bit = mRemainingFlags.clearLastMarkedBit(); // counts from left - const U flag = 1 << (64 - bit - 1); - mCurrFlag = static_cast(flag); - } - return *this; - } - - // Post-fix ++ - Iterator operator++(int) { - Iterator iter = *this; - ++*this; - return iter; - } - - bool operator==(Iterator other) const { - return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags; - } - - bool operator!=(Iterator other) const { return !(*this == other); } - - F operator*() { return mCurrFlag; } - - // iterator traits - - // In the future we could make this a bidirectional const iterator instead of a forward - // iterator but it doesn't seem worth the added complexity at this point. This could not, - // however, be made a non-const iterator as assigning one flag to another is a non-sensical - // operation. - using iterator_category = std::input_iterator_tag; - using value_type = F; - // Per the C++ spec, because input iterators are not assignable the iterator's reference - // type does not actually need to be a reference. In fact, making it a reference would imply - // that modifying it would change the underlying Flags object, which is obviously wrong for - // the same reason this can't be a non-const iterator. - using reference = F; - using difference_type = void; - using pointer = void; - - private: - BitSet64 mRemainingFlags; - F mCurrFlag; - }; - - /* - * Tests whether the given flag is set. - */ - bool test(F flag) const { - U f = static_cast(flag); - return (f & mFlags) == f; - } - - /* Tests whether any of the given flags are set */ - bool any(Flags f) { return (mFlags & f.mFlags) != 0; } - - /* Tests whether all of the given flags are set */ - bool all(Flags f) { return (mFlags & f.mFlags) == f.mFlags; } - - Flags operator|(Flags rhs) const { return static_cast(mFlags | rhs.mFlags); } - Flags& operator|=(Flags rhs) { - mFlags = mFlags | rhs.mFlags; - return *this; - } - - Flags operator&(Flags rhs) const { return static_cast(mFlags & rhs.mFlags); } - Flags& operator&=(Flags rhs) { - mFlags = mFlags & rhs.mFlags; - return *this; - } - - Flags operator^(Flags rhs) const { return static_cast(mFlags ^ rhs.mFlags); } - Flags& operator^=(Flags rhs) { - mFlags = mFlags ^ rhs.mFlags; - return *this; - } - - Flags operator~() { return static_cast(~mFlags); } - - bool operator==(Flags rhs) const { return mFlags == rhs.mFlags; } - bool operator!=(Flags rhs) const { return !operator==(rhs); } - - Flags& operator=(const Flags& rhs) { - mFlags = rhs.mFlags; - return *this; - } - - Iterator begin() const { return Iterator(*this); } - - Iterator end() const { return Iterator(); } - - /* - * Returns the stored set of flags. - * - * Note that this returns the underlying type rather than the base enum class. This is because - * the value is no longer necessarily a strict member of the enum since the returned value could - * be multiple enum variants OR'd together. - */ - U get() const { return mFlags; } - - std::string string() const { - std::string result; - bool first = true; - U unstringified = 0; - for (const F f : *this) { - std::optional flagString = flag_name(f); - if (flagString) { - appendFlag(result, flagString.value(), first); - } else { - unstringified |= static_cast(f); - } - } - - if (unstringified != 0) { - appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); - } - - if (first) { - result += "0x0"; - } - - return result; - } - -private: - U mFlags; - - static void appendFlag(std::string& str, const std::string_view& flag, bool& first) { - if (first) { - first = false; - } else { - str += " | "; - } - str += flag; - } -}; - -// This namespace provides operator overloads for enum classes to make it easier to work with them -// as flags. In order to use these, add them via a `using namespace` declaration. -namespace flag_operators { - -template >> -inline Flags operator~(F f) { - using U = typename std::underlying_type_t; - return static_cast(~static_cast(f)); -} -template >> -Flags operator|(F lhs, F rhs) { - using U = typename std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); -} - -} // namespace flag_operators -} // namespace android - -#endif // __UI_INPUT_FLAGS_H diff --git a/include/input/Input.h b/include/input/Input.h index 438121b30e..3bac7636e7 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -105,15 +105,6 @@ constexpr int32_t VERIFIED_MOTION_EVENT_FLAGS = */ constexpr int32_t AMOTION_EVENT_FLAG_CANCELED = 0x20; -enum { - /* Used when a motion event is not associated with any display. - * Typically used for non-pointer events. */ - ADISPLAY_ID_NONE = -1, - - /* The default display id. */ - ADISPLAY_ID_DEFAULT = 0, -}; - enum { /* * Indicates that an input device has switches. @@ -338,12 +329,6 @@ private: */ constexpr float AMOTION_EVENT_INVALID_CURSOR_POSITION = std::numeric_limits::quiet_NaN(); -/** - * Invalid value for display size. Used when display size isn't available for an event or doesn't - * matter. This is just a constant 0 so that it has no effect if unused. - */ -constexpr int32_t AMOTION_EVENT_INVALID_DISPLAY_SIZE = 0; - /* * Pointer coordinate data. */ diff --git a/include/input/InputApplication.h b/include/input/InputApplication.h deleted file mode 100644 index 8e4fe796a5..0000000000 --- a/include/input/InputApplication.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#ifndef _UI_INPUT_APPLICATION_H -#define _UI_INPUT_APPLICATION_H - -#include - -#include - -#include -#include -#include - -#include -#include -#include - -namespace android { -/* - * Handle for an application that can receive input. - * - * Used by the native input dispatcher as a handle for the window manager objects - * that describe an application. - */ -class InputApplicationHandle { -public: - inline const InputApplicationInfo* getInfo() const { - return &mInfo; - } - - inline std::string getName() const { - return !mInfo.name.empty() ? mInfo.name : ""; - } - - inline std::chrono::nanoseconds getDispatchingTimeout( - std::chrono::nanoseconds defaultValue) const { - return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis) - : defaultValue; - } - - inline sp getApplicationToken() const { - return mInfo.token; - } - - bool operator==(const InputApplicationHandle& other) const { - return getName() == other.getName() && getApplicationToken() == other.getApplicationToken(); - } - - bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); } - - /** - * Requests that the state of this object be updated to reflect - * the most current available information about the application. - * - * This method should only be called from within the input dispatcher's - * critical section. - * - * Returns true on success, or false if the handle is no longer valid. - */ - virtual bool updateInfo() = 0; - -protected: - InputApplicationHandle() = default; - virtual ~InputApplicationHandle() = default; - - InputApplicationInfo mInfo; -}; - -} // namespace android - -#endif // _UI_INPUT_APPLICATION_H diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h deleted file mode 100644 index 121be6d963..0000000000 --- a/include/input/InputWindow.h +++ /dev/null @@ -1,279 +0,0 @@ -/* - * Copyright (C) 2011 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. - */ - -#ifndef _UI_INPUT_WINDOW_H -#define _UI_INPUT_WINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "InputApplication.h" - -using android::os::TouchOcclusionMode; - -namespace android { - -/* - * Describes the properties of a window that can receive input. - */ -struct InputWindowInfo : public Parcelable { - InputWindowInfo() = default; - - // Window flags from WindowManager.LayoutParams - enum class Flag : uint32_t { - ALLOW_LOCK_WHILE_SCREEN_ON = 0x00000001, - DIM_BEHIND = 0x00000002, - BLUR_BEHIND = 0x00000004, - NOT_FOCUSABLE = 0x00000008, - NOT_TOUCHABLE = 0x00000010, - NOT_TOUCH_MODAL = 0x00000020, - TOUCHABLE_WHEN_WAKING = 0x00000040, - KEEP_SCREEN_ON = 0x00000080, - LAYOUT_IN_SCREEN = 0x00000100, - LAYOUT_NO_LIMITS = 0x00000200, - FULLSCREEN = 0x00000400, - FORCE_NOT_FULLSCREEN = 0x00000800, - DITHER = 0x00001000, - SECURE = 0x00002000, - SCALED = 0x00004000, - IGNORE_CHEEK_PRESSES = 0x00008000, - LAYOUT_INSET_DECOR = 0x00010000, - ALT_FOCUSABLE_IM = 0x00020000, - WATCH_OUTSIDE_TOUCH = 0x00040000, - SHOW_WHEN_LOCKED = 0x00080000, - SHOW_WALLPAPER = 0x00100000, - TURN_SCREEN_ON = 0x00200000, - DISMISS_KEYGUARD = 0x00400000, - SPLIT_TOUCH = 0x00800000, - HARDWARE_ACCELERATED = 0x01000000, - LAYOUT_IN_OVERSCAN = 0x02000000, - TRANSLUCENT_STATUS = 0x04000000, - TRANSLUCENT_NAVIGATION = 0x08000000, - LOCAL_FOCUS_MODE = 0x10000000, - SLIPPERY = 0x20000000, - LAYOUT_ATTACHED_IN_DECOR = 0x40000000, - DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000, - }; // Window types from WindowManager.LayoutParams - - enum class Type : int32_t { - UNKNOWN = 0, - FIRST_APPLICATION_WINDOW = 1, - BASE_APPLICATION = 1, - APPLICATION = 2, - APPLICATION_STARTING = 3, - LAST_APPLICATION_WINDOW = 99, - FIRST_SUB_WINDOW = 1000, - APPLICATION_PANEL = FIRST_SUB_WINDOW, - APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1, - APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2, - APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, - APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, - LAST_SUB_WINDOW = 1999, - FIRST_SYSTEM_WINDOW = 2000, - STATUS_BAR = FIRST_SYSTEM_WINDOW, - SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, - PHONE = FIRST_SYSTEM_WINDOW + 2, - SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, - KEYGUARD = FIRST_SYSTEM_WINDOW + 4, - TOAST = FIRST_SYSTEM_WINDOW + 5, - SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, - PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, - SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, - KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, - SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, - INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, - INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, - WALLPAPER = FIRST_SYSTEM_WINDOW + 13, - STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, - SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, - DRAG = FIRST_SYSTEM_WINDOW + 16, - STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, - POINTER = FIRST_SYSTEM_WINDOW + 18, - NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, - VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, - BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, - INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, - NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, - MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, - ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, - DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, - ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39, - NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, - LAST_SYSTEM_WINDOW = 2999, - }; - - enum class Feature { - DISABLE_TOUCH_PAD_GESTURES = 0x00000001, - NO_INPUT_CHANNEL = 0x00000002, - DISABLE_USER_ACTIVITY = 0x00000004, - }; - - /* These values are filled in by the WM and passed through SurfaceFlinger - * unless specified otherwise. - */ - // This value should NOT be used to uniquely identify the window. There may be different - // input windows that have the same token. - sp token; - // This uniquely identifies the input window. - int32_t id = -1; - std::string name; - Flags flags; - Type type = Type::UNKNOWN; - std::chrono::nanoseconds dispatchingTimeout = std::chrono::seconds(5); - - /* These values are filled in by SurfaceFlinger. */ - int32_t frameLeft = -1; - int32_t frameTop = -1; - int32_t frameRight = -1; - int32_t frameBottom = -1; - - /* - * SurfaceFlinger consumes this value to shrink the computed frame. This is - * different from shrinking the touchable region in that it DOES shift the coordinate - * space where-as the touchable region does not and is more like "cropping". This - * is used for window shadows. - */ - int32_t surfaceInset = 0; - - // A global scaling factor for all windows. Unlike windowScaleX/Y this results - // in scaling of the TOUCH_MAJOR/TOUCH_MINOR axis. - float globalScaleFactor = 1.0f; - - // The opacity of this window, from 0.0 to 1.0 (inclusive). - // An alpha of 1.0 means fully opaque and 0.0 means fully transparent. - float alpha; - - // Transform applied to individual windows. - ui::Transform transform; - - // Display size in its natural rotation. Used to rotate raw coordinates for compatibility. - int32_t displayWidth = AMOTION_EVENT_INVALID_DISPLAY_SIZE; - int32_t displayHeight = AMOTION_EVENT_INVALID_DISPLAY_SIZE; - - /* - * This is filled in by the WM relative to the frame and then translated - * to absolute coordinates by SurfaceFlinger once the frame is computed. - */ - Region touchableRegion; - bool visible = false; - bool focusable = false; - bool hasWallpaper = false; - bool paused = false; - /* This flag is set when the window is of a trusted type that is allowed to silently - * overlay other windows for the purpose of implementing the secure views feature. - * Trusted overlays, such as IME windows, can partly obscure other windows without causing - * motion events to be delivered to them with AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED. - */ - bool trustedOverlay = false; - TouchOcclusionMode touchOcclusionMode = TouchOcclusionMode::BLOCK_UNTRUSTED; - int32_t ownerPid = -1; - int32_t ownerUid = -1; - std::string packageName; - Flags inputFeatures; - int32_t displayId = ADISPLAY_ID_NONE; - int32_t portalToDisplayId = ADISPLAY_ID_NONE; - InputApplicationInfo applicationInfo; - bool replaceTouchableRegionWithCrop = false; - wp touchableRegionCropHandle; - - void addTouchableRegion(const Rect& region); - - bool touchableRegionContainsPoint(int32_t x, int32_t y) const; - - bool frameContainsPoint(int32_t x, int32_t y) const; - - bool supportsSplitTouch() const; - - bool overlaps(const InputWindowInfo* other) const; - - bool operator==(const InputWindowInfo& inputChannel) const; - - status_t writeToParcel(android::Parcel* parcel) const override; - - status_t readFromParcel(const android::Parcel* parcel) override; -}; - -/* - * Handle for a window that can receive input. - * - * Used by the native input dispatcher to indirectly refer to the window manager objects - * that describe a window. - */ -class InputWindowHandle : public RefBase { -public: - explicit InputWindowHandle(); - InputWindowHandle(const InputWindowHandle& other); - InputWindowHandle(const InputWindowInfo& other); - - inline const InputWindowInfo* getInfo() const { return &mInfo; } - - sp getToken() const; - - int32_t getId() const { return mInfo.id; } - - sp getApplicationToken() { return mInfo.applicationInfo.token; } - - inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : ""; } - - inline std::chrono::nanoseconds getDispatchingTimeout( - std::chrono::nanoseconds defaultValue) const { - return mInfo.token ? std::chrono::nanoseconds(mInfo.dispatchingTimeout) : defaultValue; - } - - /** - * Requests that the state of this object be updated to reflect - * the most current available information about the application. - * As this class is created as RefBase object, no pure virtual function is allowed. - * - * This method should only be called from within the input dispatcher's - * critical section. - * - * Returns true on success, or false if the handle is no longer valid. - */ - virtual bool updateInfo() { return false; } - - /** - * Updates from another input window handle. - */ - void updateFrom(const sp handle); - - /** - * Releases the channel used by the associated information when it is - * no longer needed. - */ - void releaseChannel(); - - // Not override since this class is not derrived from Parcelable. - status_t readFromParcel(const android::Parcel* parcel); - status_t writeToParcel(android::Parcel* parcel) const; - -protected: - virtual ~InputWindowHandle(); - - InputWindowInfo mInfo; -}; -} // namespace android - -#endif // _UI_INPUT_WINDOW_H diff --git a/include/input/NamedEnum.h b/include/input/NamedEnum.h deleted file mode 100644 index 6562348701..0000000000 --- a/include/input/NamedEnum.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include - -#ifndef __UI_INPUT_NAMEDENUM_H -#define __UI_INPUT_NAMEDENUM_H - -namespace android { - -namespace details { -template -constexpr std::optional enum_value_name() { - // Should look something like (but all on one line): - // std::optional - // android::details::enum_value_name() - // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] - std::string_view view = __PRETTY_FUNCTION__; - size_t templateStart = view.rfind("["); - size_t templateEnd = view.rfind("]"); - if (templateStart == std::string::npos || templateEnd == std::string::npos) { - return std::nullopt; - } - - // Extract the template parameters without the enclosing braces. - // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE - view = view.substr(templateStart + 1, templateEnd - templateStart - 1); - size_t valStart = view.rfind("V = "); - if (valStart == std::string::npos) { - return std::nullopt; - } - - // Example (cont'd): V = android::test::TestEnums::ONE - view = view.substr(valStart); - size_t nameStart = view.rfind("::"); - if (nameStart == std::string::npos) { - return std::nullopt; - } - - // Chop off the initial "::" - nameStart += 2; - return view.substr(nameStart); -} - -template -constexpr auto generate_enum_values(std::integer_sequence seq) { - constexpr size_t count = seq.size(); - - std::array values{}; - for (size_t i = 0, v = 0; v < count; ++i) { - values[v++] = static_cast(T{0} + i); - } - - return values; -} - -template -inline constexpr auto enum_values = - generate_enum_values(std::make_integer_sequence, N>{}); - -template -constexpr auto generate_enum_names(std::index_sequence) noexcept { - return std::array, sizeof...(I)>{ - {enum_value_name[I]>()...}}; -} - -template -inline constexpr auto enum_names = generate_enum_names(std::make_index_sequence{}); - -} // namespace details - -class NamedEnum { -public: - // By default allowed enum value range is 0 ~ 7. - template - static constexpr size_t max = 8; - - template - static constexpr auto enum_name() { - using E = decltype(V); - return details::enum_value_name(); - } - - template - static constexpr std::optional enum_name(E val) { - auto idx = static_cast(val); - return idx < max ? details::enum_names>[idx] : std::nullopt; - } - - // Helper function for parsing enum value to string. - // Example : enum class TestEnums { ZERO = 0x0 }; - // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". - // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, - // it should be declared to specialized the maximum enum by below: - // template <> constexpr size_t NamedEnum::max = 16; - // If the enum class definition is sparse and contains enum values starting from a large value, - // Do not specialize it to a large number to avoid performance issues. - // The recommended maximum enum number to specialize is 64. - template - static const std::string string(E val, const char* fallbackFormat = "%02d") { - std::string result; - std::optional enumString = enum_name(val); - result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); - return result; - } -}; - -} // namespace android - -#endif // __UI_INPUT_NAMEDENUM_H \ No newline at end of file diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 436620350a..3026921044 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -15,7 +15,9 @@ cc_test { }, srcs: [ "cast_test.cpp", + "Flags_test.cpp", "future_test.cpp", + "NamedEnum_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", @@ -27,4 +29,12 @@ cc_test { "-Wextra", "-Wpedantic", ], + + header_libs: [ + "libbase_headers", + ], + + shared_libs: [ + "libbase", + ], } diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp new file mode 100644 index 0000000000..8c00b5299b --- /dev/null +++ b/libs/ftl/Flags_test.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +namespace android::test { + +using namespace android::flag_operators; + +enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; + +TEST(Flags, Test) { + Flags flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); + ASSERT_FALSE(flags.test(TestFlags::THREE)); +} + +TEST(Flags, Any) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.any(TestFlags::ONE)); + ASSERT_TRUE(flags.any(TestFlags::TWO)); + ASSERT_FALSE(flags.any(TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO)); + ASSERT_TRUE(flags.any(TestFlags::TWO | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::THREE)); + ASSERT_TRUE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, All) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_TRUE(flags.all(TestFlags::ONE)); + ASSERT_TRUE(flags.all(TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::THREE)); + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO)); + ASSERT_FALSE(flags.all(TestFlags::TWO | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, DefaultConstructor_hasNoFlagsSet) { + Flags flags; + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onEmptyFlagsSetsAllFlags) { + Flags flags; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, NotOperator_onNonEmptyFlagsInvertsFlags) { + Flags flags = TestFlags::TWO; + flags = ~flags; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withNewFlag) { + Flags flags = TestFlags::ONE; + Flags flags2 = flags | TestFlags::TWO; + ASSERT_FALSE(flags2.test(TestFlags::THREE)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrOperator_withExistingFlag) { + Flags flags = TestFlags::ONE | TestFlags::THREE; + Flags flags2 = flags | TestFlags::THREE; + ASSERT_FALSE(flags2.test(TestFlags::TWO)); + ASSERT_TRUE(flags2.all(TestFlags::ONE | TestFlags::THREE)); +} + +TEST(Flags, OrEqualsOperator_withNewFlag) { + Flags flags; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.test(TestFlags::THREE)); + ASSERT_FALSE(flags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, OrEqualsOperator_withExistingFlag) { + Flags flags = TestFlags::ONE | TestFlags::THREE; + flags |= TestFlags::THREE; + ASSERT_TRUE(flags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(flags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withOneSetFlag) { + Flags flags = TestFlags::ONE | TestFlags::THREE; + Flags andFlags = flags & TestFlags::THREE; + ASSERT_TRUE(andFlags.test(TestFlags::THREE)); + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withMultipleSetFlags) { + Flags flags = TestFlags::ONE | TestFlags::THREE; + Flags andFlags = flags & (TestFlags::ONE | TestFlags::THREE); + ASSERT_TRUE(andFlags.all(TestFlags::ONE | TestFlags::THREE)); + ASSERT_FALSE(andFlags.test(TestFlags::TWO)); +} + +TEST(Flags, AndOperator_withNoSetFlags) { + Flags flags = TestFlags::ONE | TestFlags::THREE; + Flags andFlags = flags & TestFlags::TWO; + ASSERT_FALSE(andFlags.any(TestFlags::ONE | TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, Equality) { + Flags flags1 = TestFlags::ONE | TestFlags::TWO; + Flags flags2 = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags1, flags2); +} + +TEST(Flags, Inequality) { + Flags flags1 = TestFlags::ONE | TestFlags::TWO; + Flags flags2 = TestFlags::ONE | TestFlags::THREE; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, EqualsOperator) { + Flags flags; + flags = TestFlags::ONE; + ASSERT_TRUE(flags.test(TestFlags::ONE)); + ASSERT_FALSE(flags.any(TestFlags::TWO | TestFlags::THREE)); +} + +TEST(Flags, EqualsOperator_DontShareState) { + Flags flags1 = TestFlags::ONE | TestFlags::TWO; + Flags flags2 = flags1; + ASSERT_EQ(flags1, flags2); + + flags1 &= TestFlags::TWO; + ASSERT_NE(flags1, flags2); +} + +TEST(Flags, GetValue) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags.get(), 0x3); +} + +TEST(Flags, String_NoFlags) { + Flags flags; + ASSERT_EQ(flags.string(), "0x0"); +} + +TEST(Flags, String_KnownValues) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + ASSERT_EQ(flags.string(), "ONE | TWO"); +} + +TEST(Flags, String_UnknownValues) { + auto flags = Flags(0b1011); + ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008"); +} + +TEST(FlagsIterator, IteratesOverAllFlags) { + Flags flags1 = TestFlags::ONE | TestFlags::TWO; + Flags flags2; + for (TestFlags f : flags1) { + flags2 |= f; + } + ASSERT_EQ(flags2, flags1); +} + +TEST(FlagsIterator, IteratesInExpectedOrder) { + const std::vector flagOrder = {TestFlags::ONE, TestFlags::TWO}; + Flags flags; + for (TestFlags f : flagOrder) { + flags |= f; + } + + size_t idx = 0; + auto iter = flags.begin(); + while (iter != flags.end() && idx < flagOrder.size()) { + // Make sure the order is what we expect + ASSERT_EQ(*iter, flagOrder[idx]); + iter++; + idx++; + } + ASSERT_EQ(iter, flags.end()); +} +TEST(FlagsIterator, PostFixIncrement) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*(iter++), TestFlags::ONE); + ASSERT_EQ(*iter, TestFlags::TWO); + ASSERT_EQ(*(iter++), TestFlags::TWO); + ASSERT_EQ(iter, flags.end()); +} + +TEST(FlagsIterator, PreFixIncrement) { + Flags flags = TestFlags::ONE | TestFlags::TWO; + auto iter = flags.begin(); + ASSERT_EQ(*++iter, TestFlags::TWO); + ASSERT_EQ(++iter, flags.end()); +} + +TEST(FlagNames, RuntimeFlagName) { + TestFlags f = TestFlags::ONE; + ASSERT_EQ(flag_name(f), "ONE"); +} + +TEST(FlagNames, RuntimeUnknownFlagName) { + TestFlags f = static_cast(0x8); + ASSERT_EQ(flag_name(f), std::nullopt); +} + +TEST(FlagNames, CompileTimeFlagName) { + static_assert(flag_name() == "TWO"); +} + +} // namespace android::test \ No newline at end of file diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp new file mode 100644 index 0000000000..dff2b8aaa1 --- /dev/null +++ b/libs/ftl/NamedEnum_test.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace android { + +// Test enum class maximum enum value smaller than default maximum of 8. +enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 }; +// Big enum contains enum values greater than default maximum of 8. +enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF }; + +// Declared to specialize the maximum enum since the enum size exceeds 8 by default. +template <> +constexpr size_t NamedEnum::max = 16; + +namespace test { +using android::TestBigEnums; +using android::TestEnums; + +TEST(NamedEnum, RuntimeNamedEnum) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::enum_name(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::enum_name(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN"); +} + +// Test big enum +TEST(NamedEnum, RuntimeBigNamedEnum) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeNamedEnumAsString) { + TestEnums e = TestEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestEnums::ONE; + ASSERT_EQ(NamedEnum::string(e), "ONE"); + + e = TestEnums::THREE; + ASSERT_EQ(NamedEnum::string(e), "THREE"); + + e = TestEnums::SEVEN; + ASSERT_EQ(NamedEnum::string(e), "SEVEN"); +} + +TEST(NamedEnum, RuntimeBigNamedEnumAsString) { + TestBigEnums e = TestBigEnums::ZERO; + ASSERT_EQ(NamedEnum::string(e), "ZERO"); + + e = TestBigEnums::FIFTEEN; + ASSERT_EQ(NamedEnum::string(e), "FIFTEEN"); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnum) { + TestEnums e = static_cast(0x5); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); + e = static_cast(0x9); + ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); +} + +TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) { + TestEnums e = static_cast(0x5); + ASSERT_EQ(NamedEnum::string(e), "05"); + e = static_cast(0x9); + ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009"); +} + +TEST(NamedEnum, CompileTimeFlagName) { + static_assert(NamedEnum::enum_name() == "TWO"); + static_assert(NamedEnum::enum_name() == "THREE"); +} + +} // namespace test + +} // namespace android diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 64203f78a8..2d8fff91da 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -51,6 +51,51 @@ cc_library_headers { ], } +// AIDL files that should be exposed to java +filegroup { + name: "guiconstants_aidl", + srcs: [ + "android/**/TouchOcclusionMode.aidl", + ], +} + +cc_library_static { + name: "libgui_window_info_static", + vendor_available: true, + host_supported: true, + srcs: [ + ":guiconstants_aidl", + "android/gui/FocusRequest.aidl", + "android/gui/InputApplicationInfo.aidl", + "android/gui/WindowInfo.aidl", + "WindowInfo.cpp", + ], + + shared_libs: [ + "libbinder", + ], + + local_include_dirs: [ + "include", + ], + + export_shared_lib_headers: [ + "libbinder", + ], + + static_libs: [ + "libui-types", + ], + + aidl: { + export_aidl_headers: true + }, + + include_dirs: [ + "frameworks/native/include", + ], +} + filegroup { name: "libgui_aidl", srcs: ["aidl/**/*.aidl"], @@ -77,12 +122,15 @@ cc_library_static { "libbinder", ], + static_libs: [ + "libui-types", + ], + aidl: { export_aidl_headers: true } } - cc_library_shared { name: "libgui", vendor_available: true, @@ -96,9 +144,11 @@ cc_library_shared { static_libs: [ "libgui_aidl_static", + "libgui_window_info_static", ], export_static_lib_headers: [ "libgui_aidl_static", + "libgui_window_info_static", ], srcs: [ @@ -150,13 +200,11 @@ cc_library_shared { "libbinder", "libbufferhub", "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. - "libinput", "libpdx_default_transport", ], export_shared_lib_headers: [ "libbinder", - "libinput", ], export_header_lib_headers: [ @@ -168,7 +216,6 @@ cc_library_shared { vendor: { cflags: [ "-DNO_BUFFERHUB", - "-DNO_INPUT", ], exclude_srcs: [ "BufferHubConsumer.cpp", @@ -178,7 +225,6 @@ cc_library_shared { "android.frameworks.bufferhub@1.0", "libbufferhub", "libbufferhubqueue", - "libinput", "libpdx_default_transport", ], }, diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp index 29701168e7..dbb1cb0c17 100644 --- a/libs/gui/BLASTBufferQueue.cpp +++ b/libs/gui/BLASTBufferQueue.cpp @@ -38,7 +38,7 @@ using namespace std::chrono_literals; namespace { -inline const char* toString(bool b) { +inline const char* boolToString(bool b) { return b ? "true" : "false"; } } // namespace @@ -513,7 +513,7 @@ void BLASTBufferQueue::processNextBufferLocked(bool useNextTransaction) { BQA_LOGV("processNextBufferLocked size=%dx%d mFrameNumber=%" PRIu64 " applyTransaction=%s mTimestamp=%" PRId64 "%s mPendingTransactions.size=%d" " graphicBufferId=%" PRIu64 "%s", - mSize.width, mSize.height, bufferItem.mFrameNumber, toString(applyTransaction), + mSize.width, mSize.height, bufferItem.mFrameNumber, boolToString(applyTransaction), bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "", static_cast(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(), bufferItem.mAutoRefresh ? " mAutoRefresh" : ""); @@ -543,7 +543,7 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) { mNumFrameAvailable + mNumAcquired - mPendingRelease.size()); BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " nextTransactionSet=%s", item.mFrameNumber, - toString(nextTransactionSet)); + boolToString(nextTransactionSet)); processNextBufferLocked(nextTransactionSet /* useNextTransaction */); } diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index d102e07623..001570c391 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -31,6 +31,9 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfoHandle; + layer_state_t::layer_state_t() : what(0), x(0), @@ -94,9 +97,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.writeFloat, color.r); SAFE_PARCEL(output.writeFloat, color.g); SAFE_PARCEL(output.writeFloat, color.b); -#ifndef NO_INPUT - SAFE_PARCEL(inputHandle->writeToParcel, &output); -#endif + SAFE_PARCEL(windowInfoHandle->writeToParcel, &output); SAFE_PARCEL(output.write, transparentRegion); SAFE_PARCEL(output.writeUint32, transform); SAFE_PARCEL(output.writeBool, transformToDisplayInverse); @@ -205,9 +206,7 @@ status_t layer_state_t::read(const Parcel& input) color.g = tmpFloat; SAFE_PARCEL(input.readFloat, &tmpFloat); color.b = tmpFloat; -#ifndef NO_INPUT - SAFE_PARCEL(inputHandle->readFromParcel, &input); -#endif + SAFE_PARCEL(windowInfoHandle->readFromParcel, &input); SAFE_PARCEL(input.read, transparentRegion); SAFE_PARCEL(input.readUint32, &transform); @@ -491,14 +490,10 @@ void layer_state_t::merge(const layer_state_t& other) { if (other.what & eHasListenerCallbacksChanged) { what |= eHasListenerCallbacksChanged; } - -#ifndef NO_INPUT if (other.what & eInputInfoChanged) { what |= eInputInfoChanged; - inputHandle = new InputWindowHandle(*other.inputHandle); + windowInfoHandle = new WindowInfoHandle(*other.windowInfoHandle); } -#endif - if (other.what & eCachedBufferChanged) { what |= eCachedBufferChanged; cachedBuffer = other.cachedBuffer; @@ -589,11 +584,9 @@ status_t layer_state_t::matrix22_t::read(const Parcel& input) { bool InputWindowCommands::merge(const InputWindowCommands& other) { bool changes = false; -#ifndef NO_INPUT changes |= !other.focusRequests.empty(); focusRequests.insert(focusRequests.end(), std::make_move_iterator(other.focusRequests.begin()), std::make_move_iterator(other.focusRequests.end())); -#endif changes |= other.syncInputWindows && !syncInputWindows; syncInputWindows |= other.syncInputWindows; return changes; @@ -601,31 +594,23 @@ bool InputWindowCommands::merge(const InputWindowCommands& other) { bool InputWindowCommands::empty() const { bool empty = true; -#ifndef NO_INPUT empty = focusRequests.empty() && !syncInputWindows; -#endif return empty; } void InputWindowCommands::clear() { -#ifndef NO_INPUT focusRequests.clear(); -#endif syncInputWindows = false; } status_t InputWindowCommands::write(Parcel& output) const { -#ifndef NO_INPUT SAFE_PARCEL(output.writeParcelableVector, focusRequests); -#endif SAFE_PARCEL(output.writeBool, syncInputWindows); return NO_ERROR; } status_t InputWindowCommands::read(const Parcel& input) { -#ifndef NO_INPUT SAFE_PARCEL(input.readParcelableVector, &focusRequests); -#endif SAFE_PARCEL(input.readBool, &syncInputWindows); return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 3118c72276..ec03c2105b 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -39,14 +39,11 @@ #include #include #include +#include #include #include #include -#ifndef NO_INPUT -#include -#endif - #include // This server size should always be smaller than the server cache size @@ -54,6 +51,9 @@ namespace android { +using gui::FocusRequest; +using gui::WindowInfo; +using gui::WindowInfoHandle; using ui::ColorMode; // --------------------------------------------------------------------------- @@ -1491,16 +1491,14 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFrame return *this; } -#ifndef NO_INPUT SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo( - const sp& sc, - const InputWindowInfo& info) { + const sp& sc, const WindowInfo& info) { layer_state_t* s = getLayerState(sc); if (!s) { mStatus = BAD_INDEX; return *this; } - s->inputHandle = new InputWindowHandle(info); + s->windowInfoHandle = new WindowInfoHandle(info); s->what |= layer_state_t::eInputInfoChanged; return *this; } @@ -1516,8 +1514,6 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInpu return *this; } -#endif - SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( const sp& sc, const mat3& matrix, const vec3& translation) { layer_state_t* s = getLayerState(sc); diff --git a/libs/gui/WindowInfo.cpp b/libs/gui/WindowInfo.cpp new file mode 100644 index 0000000000..ff0bb8aa55 --- /dev/null +++ b/libs/gui/WindowInfo.cpp @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#define LOG_TAG "WindowInfo" +#define LOG_NDEBUG 0 + +#include +#include +#include + +#include + +namespace android::gui { + +// --- WindowInfo --- +void WindowInfo::addTouchableRegion(const Rect& region) { + touchableRegion.orSelf(region); +} + +bool WindowInfo::touchableRegionContainsPoint(int32_t x, int32_t y) const { + return touchableRegion.contains(x, y); +} + +bool WindowInfo::frameContainsPoint(int32_t x, int32_t y) const { + return x >= frameLeft && x < frameRight && y >= frameTop && y < frameBottom; +} + +bool WindowInfo::supportsSplitTouch() const { + return flags.test(Flag::SPLIT_TOUCH); +} + +bool WindowInfo::overlaps(const WindowInfo* other) const { + return frameLeft < other->frameRight && frameRight > other->frameLeft && + frameTop < other->frameBottom && frameBottom > other->frameTop; +} + +bool WindowInfo::operator==(const WindowInfo& info) const { + return info.token == token && info.id == id && info.name == name && info.flags == flags && + info.type == type && info.dispatchingTimeout == dispatchingTimeout && + info.frameLeft == frameLeft && info.frameTop == frameTop && + info.frameRight == frameRight && info.frameBottom == frameBottom && + info.surfaceInset == surfaceInset && info.globalScaleFactor == globalScaleFactor && + info.transform == transform && info.displayWidth == displayWidth && + info.displayHeight == displayHeight && + info.touchableRegion.hasSameRects(touchableRegion) && info.visible == visible && + info.trustedOverlay == trustedOverlay && info.focusable == focusable && + info.touchOcclusionMode == touchOcclusionMode && info.hasWallpaper == hasWallpaper && + info.paused == paused && info.ownerPid == ownerPid && info.ownerUid == ownerUid && + info.packageName == packageName && info.inputFeatures == inputFeatures && + info.displayId == displayId && info.portalToDisplayId == portalToDisplayId && + info.replaceTouchableRegionWithCrop == replaceTouchableRegionWithCrop && + info.applicationInfo == applicationInfo; +} + +status_t WindowInfo::writeToParcel(android::Parcel* parcel) const { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + if (name.empty()) { + parcel->writeInt32(0); + return OK; + } + parcel->writeInt32(1); + + // clang-format off + status_t status = parcel->writeStrongBinder(token) ?: + parcel->writeInt64(dispatchingTimeout.count()) ?: + parcel->writeInt32(id) ?: + parcel->writeUtf8AsUtf16(name) ?: + parcel->writeInt32(flags.get()) ?: + parcel->writeInt32(static_cast>(type)) ?: + parcel->writeInt32(frameLeft) ?: + parcel->writeInt32(frameTop) ?: + parcel->writeInt32(frameRight) ?: + parcel->writeInt32(frameBottom) ?: + parcel->writeInt32(surfaceInset) ?: + parcel->writeFloat(globalScaleFactor) ?: + parcel->writeFloat(alpha) ?: + parcel->writeFloat(transform.dsdx()) ?: + parcel->writeFloat(transform.dtdx()) ?: + parcel->writeFloat(transform.tx()) ?: + parcel->writeFloat(transform.dtdy()) ?: + parcel->writeFloat(transform.dsdy()) ?: + parcel->writeFloat(transform.ty()) ?: + parcel->writeInt32(displayWidth) ?: + parcel->writeInt32(displayHeight) ?: + parcel->writeBool(visible) ?: + parcel->writeBool(focusable) ?: + parcel->writeBool(hasWallpaper) ?: + parcel->writeBool(paused) ?: + parcel->writeBool(trustedOverlay) ?: + parcel->writeInt32(static_cast(touchOcclusionMode)) ?: + parcel->writeInt32(ownerPid) ?: + parcel->writeInt32(ownerUid) ?: + parcel->writeUtf8AsUtf16(packageName) ?: + parcel->writeInt32(inputFeatures.get()) ?: + parcel->writeInt32(displayId) ?: + parcel->writeInt32(portalToDisplayId) ?: + applicationInfo.writeToParcel(parcel) ?: + parcel->write(touchableRegion) ?: + parcel->writeBool(replaceTouchableRegionWithCrop) ?: + parcel->writeStrongBinder(touchableRegionCropHandle.promote()); + // clang-format on + return status; +} + +status_t WindowInfo::readFromParcel(const android::Parcel* parcel) { + if (parcel == nullptr) { + ALOGE("%s: Null parcel", __func__); + return BAD_VALUE; + } + if (parcel->readInt32() == 0) { + return OK; + } + + token = parcel->readStrongBinder(); + dispatchingTimeout = static_cast(parcel->readInt64()); + status_t status = parcel->readInt32(&id) ?: parcel->readUtf8FromUtf16(&name); + if (status != OK) { + return status; + } + + flags = Flags(parcel->readInt32()); + type = static_cast(parcel->readInt32()); + float dsdx, dtdx, tx, dtdy, dsdy, ty; + int32_t touchOcclusionModeInt; + // clang-format off + status = parcel->readInt32(&frameLeft) ?: + parcel->readInt32(&frameTop) ?: + parcel->readInt32(&frameRight) ?: + parcel->readInt32(&frameBottom) ?: + parcel->readInt32(&surfaceInset) ?: + parcel->readFloat(&globalScaleFactor) ?: + parcel->readFloat(&alpha) ?: + parcel->readFloat(&dsdx) ?: + parcel->readFloat(&dtdx) ?: + parcel->readFloat(&tx) ?: + parcel->readFloat(&dtdy) ?: + parcel->readFloat(&dsdy) ?: + parcel->readFloat(&ty) ?: + parcel->readInt32(&displayWidth) ?: + parcel->readInt32(&displayHeight) ?: + parcel->readBool(&visible) ?: + parcel->readBool(&focusable) ?: + parcel->readBool(&hasWallpaper) ?: + parcel->readBool(&paused) ?: + parcel->readBool(&trustedOverlay) ?: + parcel->readInt32(&touchOcclusionModeInt) ?: + parcel->readInt32(&ownerPid) ?: + parcel->readInt32(&ownerUid) ?: + parcel->readUtf8FromUtf16(&packageName); + // clang-format on + + if (status != OK) { + return status; + } + + touchOcclusionMode = static_cast(touchOcclusionModeInt); + + inputFeatures = Flags(parcel->readInt32()); + // clang-format off + status = parcel->readInt32(&displayId) ?: + parcel->readInt32(&portalToDisplayId) ?: + applicationInfo.readFromParcel(parcel) ?: + parcel->read(touchableRegion) ?: + parcel->readBool(&replaceTouchableRegionWithCrop); + // clang-format on + + if (status != OK) { + return status; + } + + touchableRegionCropHandle = parcel->readStrongBinder(); + transform.set({dsdx, dtdx, tx, dtdy, dsdy, ty, 0, 0, 1}); + + return OK; +} + +// --- WindowInfoHandle --- + +WindowInfoHandle::WindowInfoHandle() {} + +WindowInfoHandle::~WindowInfoHandle() {} + +WindowInfoHandle::WindowInfoHandle(const WindowInfoHandle& other) : mInfo(other.mInfo) {} + +WindowInfoHandle::WindowInfoHandle(const WindowInfo& other) : mInfo(other) {} + +status_t WindowInfoHandle::writeToParcel(android::Parcel* parcel) const { + return mInfo.writeToParcel(parcel); +} + +status_t WindowInfoHandle::readFromParcel(const android::Parcel* parcel) { + return mInfo.readFromParcel(parcel); +} + +void WindowInfoHandle::releaseChannel() { + mInfo.token.clear(); +} + +sp WindowInfoHandle::getToken() const { + return mInfo.token; +} + +void WindowInfoHandle::updateFrom(sp handle) { + mInfo = handle->mInfo; +} +} // namespace android::gui diff --git a/libs/gui/android/gui/FocusRequest.aidl b/libs/gui/android/gui/FocusRequest.aidl new file mode 100644 index 0000000000..90186351c5 --- /dev/null +++ b/libs/gui/android/gui/FocusRequest.aidl @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2020, 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. + */ + +package android.gui; + +/** @hide */ +parcelable FocusRequest { + /** + * Input channel token used to identify the window that should gain focus. + */ + IBinder token; + @utf8InCpp String windowName; + /** + * The token that the caller expects currently to be focused. If the + * specified token does not match the currently focused window, this request will be dropped. + * If the specified focused token matches the currently focused window, the call will succeed. + * Set this to "null" if this call should succeed no matter what the currently focused token + * is. + */ + @nullable IBinder focusedToken; + @utf8InCpp String focusedWindowName; + /** + * SYSTEM_TIME_MONOTONIC timestamp in nanos set by the client (wm) when requesting the focus + * change. This determines which request gets precedence if there is a focus change request + * from another source such as pointer down. + */ + long timestamp; + /** + * Display id associated with this request. + */ + int displayId; +} diff --git a/libs/gui/android/gui/InputApplicationInfo.aidl b/libs/gui/android/gui/InputApplicationInfo.aidl new file mode 100644 index 0000000000..c0fd666543 --- /dev/null +++ b/libs/gui/android/gui/InputApplicationInfo.aidl @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2020, 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. + */ + +package android.gui; + +parcelable InputApplicationInfo { + @nullable IBinder token; + @utf8InCpp String name; + long dispatchingTimeoutMillis; +} diff --git a/libs/gui/android/gui/TouchOcclusionMode.aidl b/libs/gui/android/gui/TouchOcclusionMode.aidl new file mode 100644 index 0000000000..d91d052135 --- /dev/null +++ b/libs/gui/android/gui/TouchOcclusionMode.aidl @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2020, 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. + */ + +package android.gui; + + +/** + * Touch occlusion modes: These modes represent how windows are taken into + * consideration in order to decide whether to block obscured touches or + * not. + * + * @hide + */ +@Backing(type="int") +enum TouchOcclusionMode { + /** + * Touches that pass through this window will be blocked if they are + * consumed by a different UID and this window is not trusted. + */ + BLOCK_UNTRUSTED, + + /** + * The window's opacity will be taken into consideration for touch + * occlusion rules if the touch passes through it and the window is not + * trusted. + */ + USE_OPACITY, + + /** + * The window won't count for touch occlusion rules if the touch passes + * through it. + */ + ALLOW +} diff --git a/libs/gui/android/gui/WindowInfo.aidl b/libs/gui/android/gui/WindowInfo.aidl new file mode 100644 index 0000000000..2c85d155a8 --- /dev/null +++ b/libs/gui/android/gui/WindowInfo.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2020, 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. +*/ + +package android.gui; + +parcelable WindowInfo cpp_header "gui/WindowInfo.h"; diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 8ec7e6d630..c0a2335885 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -24,9 +24,9 @@ #include #include #include +#include #include #include -#include #include #include #include diff --git a/libs/gui/include/gui/InputApplication.h b/libs/gui/include/gui/InputApplication.h new file mode 100644 index 0000000000..679c2a1754 --- /dev/null +++ b/libs/gui/include/gui/InputApplication.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 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. + */ + +#ifndef _UI_INPUT_APPLICATION_H +#define _UI_INPUT_APPLICATION_H + +#include + +#include + +#include +#include +#include + +#include +#include + +namespace android { + +/* + * Handle for an application that can receive input. + * + * Used by the native input dispatcher as a handle for the window manager objects + * that describe an application. + */ +class InputApplicationHandle { +public: + inline const gui::InputApplicationInfo* getInfo() const { return &mInfo; } + + inline std::string getName() const { return !mInfo.name.empty() ? mInfo.name : ""; } + + inline std::chrono::nanoseconds getDispatchingTimeout( + std::chrono::nanoseconds defaultValue) const { + return mInfo.token ? std::chrono::milliseconds(mInfo.dispatchingTimeoutMillis) + : defaultValue; + } + + inline sp getApplicationToken() const { return mInfo.token; } + + bool operator==(const InputApplicationHandle& other) const { + return getName() == other.getName() && getApplicationToken() == other.getApplicationToken(); + } + + bool operator!=(const InputApplicationHandle& other) const { return !(*this == other); } + + /** + * Requests that the state of this object be updated to reflect + * the most current available information about the application. + * + * This method should only be called from within the input dispatcher's + * critical section. + * + * Returns true on success, or false if the handle is no longer valid. + */ + virtual bool updateInfo() = 0; + +protected: + InputApplicationHandle() = default; + virtual ~InputApplicationHandle() = default; + + gui::InputApplicationInfo mInfo; +}; + +} // namespace android + +#endif // _UI_INPUT_APPLICATION_H diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index 7961f4bd07..92de74a414 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -26,14 +26,12 @@ #include #include -#ifndef NO_INPUT -#include -#include (flag))); - return details::flag_names[idx]; -} - /* A class for handling flags defined by an enum or enum class in a type-safe way. */ template class Flags { @@ -94,7 +37,7 @@ class Flags { // further to avoid this restriction but in general we want to encourage the use of enums // anyways. static_assert(std::is_enum_v, "Flags type must be an enum"); - using U = typename std::underlying_type_t; + using U = std::underlying_type_t; public: constexpr Flags(F f) : mFlags(static_cast(f)) {} @@ -106,11 +49,10 @@ public: // should force them to be explicitly constructed from their underlying types to make full use // of the type checker. template - constexpr Flags(T t, typename std::enable_if_t, T>* = nullptr) - : mFlags(t) {} + constexpr Flags(T t, std::enable_if_t, T>* = nullptr) : mFlags(t) {} + template - explicit constexpr Flags(T t, - typename std::enable_if_t, T>* = nullptr) + explicit constexpr Flags(T t, std::enable_if_t, T>* = nullptr) : mFlags(t) {} class Iterator { @@ -229,16 +171,16 @@ public: bool first = true; U unstringified = 0; for (const F f : *this) { - std::optional flagString = flag_name(f); - if (flagString) { - appendFlag(result, flagString.value(), first); + if (const auto flagName = ftl::flag_name(f)) { + appendFlag(result, flagName.value(), first); } else { unstringified |= static_cast(f); } } if (unstringified != 0) { - appendFlag(result, base::StringPrintf("0x%08x", unstringified), first); + constexpr auto radix = sizeof(U) == 1 ? ftl::Radix::kBin : ftl::Radix::kHex; + appendFlag(result, ftl::to_string(unstringified, radix), first); } if (first) { @@ -265,15 +207,14 @@ private: // as flags. In order to use these, add them via a `using namespace` declaration. namespace flag_operators { -template >> +template >> inline Flags operator~(F f) { - using U = typename std::underlying_type_t; - return static_cast(~static_cast(f)); + return static_cast(~ftl::enum_cast(f)); } -template >> + +template >> Flags operator|(F lhs, F rhs) { - using U = typename std::underlying_type_t; - return static_cast(static_cast(lhs) | static_cast(rhs)); + return static_cast(ftl::enum_cast(lhs) | ftl::enum_cast(rhs)); } } // namespace flag_operators diff --git a/include/ftl/NamedEnum.h b/include/ftl/NamedEnum.h deleted file mode 100644 index 6e98feeb87..0000000000 --- a/include/ftl/NamedEnum.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include -#include -#include -#include - -#pragma once - -namespace android { - -namespace details { -template -constexpr std::optional enum_value_name() { - // Should look something like (but all on one line): - // std::optional - // android::details::enum_value_name() - // [E = android::test::TestEnums, V = android::test::TestEnums::ONE] - std::string_view view = __PRETTY_FUNCTION__; - size_t templateStart = view.rfind("["); - size_t templateEnd = view.rfind("]"); - if (templateStart == std::string::npos || templateEnd == std::string::npos) { - return std::nullopt; - } - - // Extract the template parameters without the enclosing braces. - // Example (cont'd): E = android::test::TestEnums, V = android::test::TestEnums::ONE - view = view.substr(templateStart + 1, templateEnd - templateStart - 1); - size_t valStart = view.rfind("V = "); - if (valStart == std::string::npos) { - return std::nullopt; - } - - // Example (cont'd): V = android::test::TestEnums::ONE - view = view.substr(valStart); - // Check invalid enum values with cast, like V = (android::test::TestEnums)8. - if (view.find('(') != std::string::npos) { - return std::nullopt; - } - size_t nameStart = view.rfind("::"); - if (nameStart == std::string::npos) { - return std::nullopt; - } - - // Chop off the initial "::" - nameStart += 2; - return view.substr(nameStart); -} - -template -constexpr auto generate_enum_values(std::integer_sequence seq) { - constexpr size_t count = seq.size(); - - std::array values{}; - for (size_t i = 0, v = 0; v < count; ++i) { - values[v++] = static_cast(T{0} + i); - } - - return values; -} - -template -inline constexpr auto enum_values = - generate_enum_values(std::make_integer_sequence, N>{}); - -template -constexpr auto generate_enum_names(std::index_sequence) noexcept { - return std::array, sizeof...(I)>{ - {enum_value_name[I]>()...}}; -} - -template -inline constexpr auto enum_names = generate_enum_names(std::make_index_sequence{}); - -} // namespace details - -class NamedEnum { -public: - // By default allowed enum value range is 0 ~ 7. - template - static constexpr size_t max = 8; - - template - static constexpr auto enum_name() { - using E = decltype(V); - return details::enum_value_name(); - } - - template - static constexpr std::optional enum_name(E val) { - auto idx = static_cast(val); - return idx < max ? details::enum_names>[idx] : std::nullopt; - } - - // Helper function for parsing enum value to string. - // Example : enum class TestEnums { ZERO = 0x0 }; - // NamedEnum::string(TestEnums::ZERO) returns string of "ZERO". - // Note the default maximum enum is 8, if the enum ID to be parsed if greater than 8 like 16, - // it should be declared to specialized the maximum enum by below: - // template <> constexpr size_t NamedEnum::max = 16; - // If the enum class definition is sparse and contains enum values starting from a large value, - // Do not specialize it to a large number to avoid performance issues. - // The recommended maximum enum number to specialize is 64. - template - static const std::string string(E val, const char* fallbackFormat = "%02d") { - std::string result; - std::optional enumString = enum_name(val); - result += enumString ? enumString.value() : base::StringPrintf(fallbackFormat, val); - return result; - } -}; - -} // namespace android diff --git a/include/ftl/enum.h b/include/ftl/enum.h new file mode 100644 index 0000000000..dfe3a0976b --- /dev/null +++ b/include/ftl/enum.h @@ -0,0 +1,299 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +// Returns the name of enumerator E::V (i.e. "V") as std::optional by parsing the +// compiler-generated string literal for the signature of this function. The function is defined in +// the global namespace with a short name and inferred return type to reduce bloat in the read-only +// data segment. +template +constexpr auto ftl_enum() { + static_assert(std::is_enum_v); + + using R = std::optional; + using namespace std::literals; + + // The "pretty" signature has the following format: + // + // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue] + // + std::string_view view = __PRETTY_FUNCTION__; + const auto template_begin = view.rfind('['); + const auto template_end = view.rfind(']'); + if (template_begin == view.npos || template_end == view.npos) return R{}; + + // Extract the template parameters without the enclosing brackets. Example (cont'd): + // + // E = android::test::Enum, V = android::test::Enum::kValue + // + view = view.substr(template_begin + 1, template_end - template_begin - 1); + const auto value_begin = view.rfind("V = "sv); + if (value_begin == view.npos) return R{}; + + // Example (cont'd): + // + // V = android::test::Enum::kValue + // + view = view.substr(value_begin); + const auto name_begin = view.rfind("::"sv); + if (name_begin == view.npos) return R{}; + + // Chop off the leading "::". + const auto name = view.substr(name_begin + 2); + + // A value that is not enumerated has the format "Enum)42". + return name.find(')') == view.npos ? R{name} : R{}; +} + +namespace android::ftl { + +// Trait for determining whether a type is specifically a scoped enum or not. By definition, a +// scoped enum is one that is not implicitly convertible to its underlying type. +// +// TODO: Replace with std::is_scoped_enum in C++23. +// +template > +struct is_scoped_enum : std::false_type {}; + +template +struct is_scoped_enum : std::negation>> { +}; + +template +inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; + +// Shorthand for casting an enumerator to its integral value. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_cast(E::B) == 1); +// +template +constexpr auto enum_cast(E v) { + return static_cast>(v); +} + +// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named +// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1 +// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the +// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of- +// range values results in undefined behavior if the underlying type is not fixed. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_begin_v == E::A); +// static_assert(ftl::enum_last_v == E::F); +// static_assert(ftl::enum_size_v == 6); +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// static_assert(ftl::enum_begin_v == F{0}); +// static_assert(ftl::enum_last_v == F{15}); +// static_assert(ftl::enum_size_v == 16); +// +template +struct enum_begin { + static_assert(is_scoped_enum_v, "Missing ftl_first enumerator"); + static constexpr E value{0}; +}; + +template +struct enum_begin> { + static constexpr E value = E::ftl_first; +}; + +template +inline constexpr E enum_begin_v = enum_begin::value; + +template +struct enum_end { + using U = std::underlying_type_t; + static_assert(is_scoped_enum_v && std::is_unsigned_v, "Missing ftl_last enumerator"); + + static constexpr E value{std::numeric_limits::digits}; +}; + +template +struct enum_end> { + static constexpr E value = E{enum_cast(E::ftl_last) + 1}; +}; + +template +inline constexpr E enum_end_v = enum_end::value; + +template +inline constexpr E enum_last_v = E{enum_cast(enum_end_v) - 1}; + +template +struct enum_size { + static constexpr auto kBegin = enum_cast(enum_begin_v); + static constexpr auto kEnd = enum_cast(enum_end_v); + static_assert(kBegin < kEnd, "Invalid range"); + + static constexpr std::size_t value = kEnd - kBegin; + static_assert(value <= 64, "Excessive range size"); +}; + +template +inline constexpr std::size_t enum_size_v = enum_size::value; + +namespace details { + +template +struct Identity { + static constexpr auto value = V; +}; + +template +using make_enum_sequence = std::make_integer_sequence, enum_size_v>; + +template class = Identity, typename = make_enum_sequence> +struct EnumRange; + +template class F, typename T, T... Vs> +struct EnumRange> { + static constexpr auto kBegin = enum_cast(enum_begin_v); + static constexpr auto kSize = enum_size_v; + + using R = decltype(F::value); + const R values[kSize] = {F(Vs + kBegin)>::value...}; + + constexpr const auto* begin() const { return values; } + constexpr const auto* end() const { return values + kSize; } +}; + +template +struct EnumName { + static constexpr auto value = ftl_enum(); +}; + +template +struct FlagName { + using E = decltype(I); + using U = std::underlying_type_t; + + static constexpr E V{U{1} << enum_cast(I)}; + static constexpr auto value = ftl_enum(); +}; + +} // namespace details + +// Returns an iterable over the range of an enum. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// std::string string; +// for (E v : ftl::enum_range()) { +// string += ftl::enum_name(v).value_or("?"); +// } +// +// assert(string == "ABC??F"); +// +template +constexpr auto enum_range() { + return details::EnumRange{}; +} + +// Returns a stringified enumerator at compile time. +// +// enum class E { A, B, C }; +// static_assert(ftl::enum_name() == "B"); +// +template +constexpr std::string_view enum_name() { + constexpr auto kName = ftl_enum(); + static_assert(kName, "Unknown enumerator"); + return *kName; +} + +// Returns a stringified enumerator, possibly at compile time. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// static_assert(ftl::enum_name(E::C).value_or("?") == "C"); +// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); +// +template +constexpr std::optional enum_name(E v) { + const auto value = enum_cast(v); + + constexpr auto kBegin = enum_cast(enum_begin_v); + constexpr auto kLast = enum_cast(enum_last_v); + if (value < kBegin || value > kLast) return {}; + + constexpr auto kRange = details::EnumRange{}; + return kRange.values[value - kBegin]; +} + +// Returns a stringified flag enumerator, possibly at compile time. +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); +// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); +// +template +constexpr std::optional flag_name(E v) { + const auto value = enum_cast(v); + + // TODO: Replace with std::popcount and std::countr_zero in C++20. + if (__builtin_popcountl(value) != 1) return {}; + + constexpr auto kRange = details::EnumRange{}; + return kRange.values[__builtin_ctzl(value)]; +} + +// Returns a stringified enumerator, or its integral value if not named. +// +// enum class E { A, B, C, F = 5, ftl_last = F }; +// +// assert(ftl::enum_string(E::C) == "C"); +// assert(ftl::enum_string(E{3}) == "3"); +// +template +inline std::string enum_string(E v) { + if (const auto name = enum_name(v)) { + return std::string(*name); + } + return to_string(enum_cast(v)); +} + +// Returns a stringified flag enumerator, or its integral value if not named. +// +// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; +// +// assert(ftl::flag_string(F::Z) == "Z"); +// assert(ftl::flag_string(F{7}) == "0b111"); +// +template +inline std::string flag_string(E v) { + if (const auto name = flag_name(v)) { + return std::string(*name); + } + constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; + return to_string(enum_cast(v), radix); +} + +} // namespace android::ftl diff --git a/include/input/DisplayViewport.h b/include/input/DisplayViewport.h index a6213f3ddd..9148fee532 100644 --- a/include/input/DisplayViewport.h +++ b/include/input/DisplayViewport.h @@ -18,7 +18,8 @@ #define _LIBINPUT_DISPLAY_VIEWPORT_H #include -#include +#include +#include #include #include @@ -44,6 +45,8 @@ enum class ViewportType : int32_t { INTERNAL = 1, EXTERNAL = 2, VIRTUAL = 3, + + ftl_last = VIRTUAL }; /* @@ -132,9 +135,8 @@ struct DisplayViewport { "physicalFrame=[%d, %d, %d, %d], " "deviceSize=[%d, %d], " "isActive=[%d]", - NamedEnum::string(type).c_str(), displayId, uniqueId.c_str(), - physicalPort ? StringPrintf("%" PRIu8, *physicalPort).c_str() - : "", + ftl::enum_string(type).c_str(), displayId, uniqueId.c_str(), + physicalPort ? ftl::to_string(*physicalPort).c_str() : "", orientation, logicalLeft, logicalTop, logicalRight, logicalBottom, physicalLeft, physicalTop, physicalRight, physicalBottom, deviceWidth, deviceHeight, isActive); diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 7f0324a4a8..22aae196c6 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -84,6 +84,9 @@ enum class InputDeviceSensorType : int32_t { GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, + + ftl_first = ACCELEROMETER, + ftl_last = SIGNIFICANT_MOTION }; enum class InputDeviceSensorAccuracy : int32_t { @@ -105,6 +108,8 @@ enum class InputDeviceLightType : int32_t { PLAYER_ID = 1, RGB = 2, MULTI_COLOR = 3, + + ftl_last = MULTI_COLOR }; struct InputDeviceSensorInfo { diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 9a150eb0e2..7632b30bd2 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -73,6 +73,8 @@ struct InputMessage { DRAG, TIMELINE, TOUCH_MODE, + + ftl_last = TOUCH_MODE }; struct Header { diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 3026921044..5a80ad067c 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -14,10 +14,10 @@ cc_test { address: true, }, srcs: [ - "cast_test.cpp", "Flags_test.cpp", + "cast_test.cpp", + "enum_test.cpp", "future_test.cpp", - "NamedEnum_test.cpp", "small_map_test.cpp", "small_vector_test.cpp", "static_vector_test.cpp", diff --git a/libs/ftl/Flags_test.cpp b/libs/ftl/Flags_test.cpp index 8c00b5299b..d241fa272a 100644 --- a/libs/ftl/Flags_test.cpp +++ b/libs/ftl/Flags_test.cpp @@ -23,7 +23,7 @@ namespace android::test { using namespace android::flag_operators; -enum class TestFlags { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; +enum class TestFlags : uint8_t { ONE = 0x1, TWO = 0x2, THREE = 0x4 }; TEST(Flags, Test) { Flags flags = TestFlags::ONE; @@ -165,7 +165,7 @@ TEST(Flags, String_KnownValues) { TEST(Flags, String_UnknownValues) { auto flags = Flags(0b1011); - ASSERT_EQ(flags.string(), "ONE | TWO | 0x00000008"); + ASSERT_EQ(flags.string(), "ONE | TWO | 0b1000"); } TEST(FlagsIterator, IteratesOverAllFlags) { @@ -210,18 +210,4 @@ TEST(FlagsIterator, PreFixIncrement) { ASSERT_EQ(++iter, flags.end()); } -TEST(FlagNames, RuntimeFlagName) { - TestFlags f = TestFlags::ONE; - ASSERT_EQ(flag_name(f), "ONE"); -} - -TEST(FlagNames, RuntimeUnknownFlagName) { - TestFlags f = static_cast(0x8); - ASSERT_EQ(flag_name(f), std::nullopt); -} - -TEST(FlagNames, CompileTimeFlagName) { - static_assert(flag_name() == "TWO"); -} - -} // namespace android::test \ No newline at end of file +} // namespace android::test diff --git a/libs/ftl/NamedEnum_test.cpp b/libs/ftl/NamedEnum_test.cpp deleted file mode 100644 index dff2b8aaa1..0000000000 --- a/libs/ftl/NamedEnum_test.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -namespace android { - -// Test enum class maximum enum value smaller than default maximum of 8. -enum class TestEnums { ZERO = 0x0, ONE = 0x1, TWO = 0x2, THREE = 0x3, SEVEN = 0x7 }; -// Big enum contains enum values greater than default maximum of 8. -enum class TestBigEnums { ZERO = 0x0, FIFTEEN = 0xF }; - -// Declared to specialize the maximum enum since the enum size exceeds 8 by default. -template <> -constexpr size_t NamedEnum::max = 16; - -namespace test { -using android::TestBigEnums; -using android::TestEnums; - -TEST(NamedEnum, RuntimeNamedEnum) { - TestEnums e = TestEnums::ZERO; - ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); - - e = TestEnums::ONE; - ASSERT_EQ(NamedEnum::enum_name(e), "ONE"); - - e = TestEnums::THREE; - ASSERT_EQ(NamedEnum::enum_name(e), "THREE"); - - e = TestEnums::SEVEN; - ASSERT_EQ(NamedEnum::enum_name(e), "SEVEN"); -} - -// Test big enum -TEST(NamedEnum, RuntimeBigNamedEnum) { - TestBigEnums e = TestBigEnums::ZERO; - ASSERT_EQ(NamedEnum::enum_name(e), "ZERO"); - - e = TestBigEnums::FIFTEEN; - ASSERT_EQ(NamedEnum::enum_name(e), "FIFTEEN"); -} - -TEST(NamedEnum, RuntimeNamedEnumAsString) { - TestEnums e = TestEnums::ZERO; - ASSERT_EQ(NamedEnum::string(e), "ZERO"); - - e = TestEnums::ONE; - ASSERT_EQ(NamedEnum::string(e), "ONE"); - - e = TestEnums::THREE; - ASSERT_EQ(NamedEnum::string(e), "THREE"); - - e = TestEnums::SEVEN; - ASSERT_EQ(NamedEnum::string(e), "SEVEN"); -} - -TEST(NamedEnum, RuntimeBigNamedEnumAsString) { - TestBigEnums e = TestBigEnums::ZERO; - ASSERT_EQ(NamedEnum::string(e), "ZERO"); - - e = TestBigEnums::FIFTEEN; - ASSERT_EQ(NamedEnum::string(e), "FIFTEEN"); -} - -TEST(NamedEnum, RuntimeUnknownNamedEnum) { - TestEnums e = static_cast(0x5); - ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); - e = static_cast(0x9); - ASSERT_EQ(NamedEnum::enum_name(e), std::nullopt); -} - -TEST(NamedEnum, RuntimeUnknownNamedEnumAsString) { - TestEnums e = static_cast(0x5); - ASSERT_EQ(NamedEnum::string(e), "05"); - e = static_cast(0x9); - ASSERT_EQ(NamedEnum::string(e, "0x%08x"), "0x00000009"); -} - -TEST(NamedEnum, CompileTimeFlagName) { - static_assert(NamedEnum::enum_name() == "TWO"); - static_assert(NamedEnum::enum_name() == "THREE"); -} - -} // namespace test - -} // namespace android diff --git a/libs/ftl/enum_test.cpp b/libs/ftl/enum_test.cpp new file mode 100644 index 0000000000..1fd43abbc6 --- /dev/null +++ b/libs/ftl/enum_test.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace android::test { + +// Keep in sync with example usage in header file. +namespace { + +enum class E { A, B, C, F = 5, ftl_last = F }; + +static_assert(ftl::enum_begin_v == E::A); +static_assert(ftl::enum_last_v == E::F); +static_assert(ftl::enum_size_v == 6); + +static_assert(ftl::enum_name() == "B"); +static_assert(ftl::enum_name() == "F"); +static_assert(ftl::enum_name(E::C).value_or("?") == "C"); +static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); + +enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; + +static_assert(ftl::enum_begin_v == F{0}); +static_assert(ftl::enum_last_v == F{15}); +static_assert(ftl::enum_size_v == 16); + +static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); +static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); + +// If a scoped enum is unsigned, its implicit range corresponds to its bit indices. +enum class Flags : std::uint8_t { + kNone = 0, + kFlag1 = 0b0000'0010, + kFlag4 = 0b0001'0000, + kFlag7 = 0b1000'0000, + kMask = kFlag1 | kFlag4 | kFlag7, + kAll = 0b1111'1111 +}; + +static_assert(ftl::enum_begin_v == Flags{0}); +static_assert(ftl::enum_last_v == Flags{7}); +static_assert(ftl::enum_size_v == 8); + +static_assert(ftl::enum_name() == "kNone"); +static_assert(ftl::enum_name() == "kFlag4"); +static_assert(ftl::enum_name() == "kFlag7"); + +// Though not flags, the enumerators are within the implicit range of bit indices. +enum class Planet : std::uint8_t { + kMercury, + kVenus, + kEarth, + kMars, + kJupiter, + kSaturn, + kUranus, + kNeptune +}; + +constexpr Planet kPluto{ftl::enum_cast(Planet::kNeptune) + 1}; // Honorable mention. + +static_assert(ftl::enum_begin_v == Planet::kMercury); +static_assert(ftl::enum_last_v == Planet::kNeptune); +static_assert(ftl::enum_size_v == 8); + +static_assert(ftl::enum_name() == "kMercury"); +static_assert(ftl::enum_name() == "kSaturn"); + +// Unscoped enum must define explicit range, even if the underlying type is fixed. +enum Temperature : int { + kRoom = 20, + kFridge = 4, + kFreezer = -18, + + ftl_first = kFreezer, + ftl_last = kRoom +}; + +static_assert(ftl::enum_begin_v == kFreezer); +static_assert(ftl::enum_last_v == kRoom); +static_assert(ftl::enum_size_v == 39); + +static_assert(ftl::enum_name() == "kFreezer"); +static_assert(ftl::enum_name() == "kFridge"); +static_assert(ftl::enum_name() == "kRoom"); + +} // namespace + +TEST(Enum, Range) { + std::string string; + for (E v : ftl::enum_range()) { + string += ftl::enum_name(v).value_or("?"); + } + EXPECT_EQ(string, "ABC??F"); +} + +TEST(Enum, Name) { + { + EXPECT_EQ(ftl::flag_name(Flags::kFlag1), "kFlag1"); + EXPECT_EQ(ftl::flag_name(Flags::kFlag7), "kFlag7"); + + EXPECT_EQ(ftl::flag_name(Flags::kNone), std::nullopt); + EXPECT_EQ(ftl::flag_name(Flags::kMask), std::nullopt); + EXPECT_EQ(ftl::flag_name(Flags::kAll), std::nullopt); + } + { + EXPECT_EQ(ftl::enum_name(Planet::kEarth), "kEarth"); + EXPECT_EQ(ftl::enum_name(Planet::kNeptune), "kNeptune"); + + EXPECT_EQ(ftl::enum_name(kPluto), std::nullopt); + } + { + EXPECT_EQ(ftl::enum_name(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_name(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_name(kFreezer), "kFreezer"); + + EXPECT_EQ(ftl::enum_name(static_cast(-30)), std::nullopt); + EXPECT_EQ(ftl::enum_name(static_cast(0)), std::nullopt); + EXPECT_EQ(ftl::enum_name(static_cast(100)), std::nullopt); + } +} + +TEST(Enum, String) { + { + EXPECT_EQ(ftl::flag_string(Flags::kFlag1), "kFlag1"); + EXPECT_EQ(ftl::flag_string(Flags::kFlag7), "kFlag7"); + + EXPECT_EQ(ftl::flag_string(Flags::kNone), "0b0"); + EXPECT_EQ(ftl::flag_string(Flags::kMask), "0b10010010"); + EXPECT_EQ(ftl::flag_string(Flags::kAll), "0b11111111"); + } + { + EXPECT_EQ(ftl::enum_string(Planet::kEarth), "kEarth"); + EXPECT_EQ(ftl::enum_string(Planet::kNeptune), "kNeptune"); + + EXPECT_EQ(ftl::enum_string(kPluto), "8"); + } + { + EXPECT_EQ(ftl::enum_string(kRoom), "kRoom"); + EXPECT_EQ(ftl::enum_string(kFridge), "kFridge"); + EXPECT_EQ(ftl::enum_string(kFreezer), "kFreezer"); + + EXPECT_EQ(ftl::enum_string(static_cast(-30)), "-30"); + EXPECT_EQ(ftl::enum_string(static_cast(0)), "0"); + EXPECT_EQ(ftl::enum_string(static_cast(100)), "100"); + } +} + +} // namespace android::test diff --git a/libs/gui/include/gui/WindowInfo.h b/libs/gui/include/gui/WindowInfo.h index f090c63228..47f6c05bff 100644 --- a/libs/gui/include/gui/WindowInfo.h +++ b/libs/gui/include/gui/WindowInfo.h @@ -71,8 +71,9 @@ struct WindowInfo : public Parcelable { SLIPPERY = 0x20000000, LAYOUT_ATTACHED_IN_DECOR = 0x40000000, DRAWS_SYSTEM_BAR_BACKGROUNDS = 0x80000000, - }; // Window types from WindowManager.LayoutParams + }; + // Window types from WindowManager.LayoutParams enum class Type : int32_t { UNKNOWN = 0, FIRST_APPLICATION_WINDOW = 1, @@ -87,40 +88,50 @@ struct WindowInfo : public Parcelable { APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3, APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4, LAST_SUB_WINDOW = 1999, - FIRST_SYSTEM_WINDOW = 2000, - STATUS_BAR = FIRST_SYSTEM_WINDOW, - SEARCH_BAR = FIRST_SYSTEM_WINDOW + 1, - PHONE = FIRST_SYSTEM_WINDOW + 2, - SYSTEM_ALERT = FIRST_SYSTEM_WINDOW + 3, - KEYGUARD = FIRST_SYSTEM_WINDOW + 4, - TOAST = FIRST_SYSTEM_WINDOW + 5, - SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 6, - PRIORITY_PHONE = FIRST_SYSTEM_WINDOW + 7, - SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW + 8, - KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW + 9, - SYSTEM_ERROR = FIRST_SYSTEM_WINDOW + 10, - INPUT_METHOD = FIRST_SYSTEM_WINDOW + 11, - INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW + 12, - WALLPAPER = FIRST_SYSTEM_WINDOW + 13, - STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW + 14, - SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW + 15, - DRAG = FIRST_SYSTEM_WINDOW + 16, - STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW + 17, - POINTER = FIRST_SYSTEM_WINDOW + 18, - NAVIGATION_BAR = FIRST_SYSTEM_WINDOW + 19, - VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW + 20, - BOOT_PROGRESS = FIRST_SYSTEM_WINDOW + 21, - INPUT_CONSUMER = FIRST_SYSTEM_WINDOW + 22, - NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW + 24, - MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 27, - ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW + 32, - DOCK_DIVIDER = FIRST_SYSTEM_WINDOW + 34, - ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39, - NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40, + +#define FIRST_SYSTEM_WINDOW_ 2000 + + STATUS_BAR = FIRST_SYSTEM_WINDOW_, + SEARCH_BAR = FIRST_SYSTEM_WINDOW_ + 1, + PHONE = FIRST_SYSTEM_WINDOW_ + 2, + SYSTEM_ALERT = FIRST_SYSTEM_WINDOW_ + 3, + KEYGUARD = FIRST_SYSTEM_WINDOW_ + 4, + TOAST = FIRST_SYSTEM_WINDOW_ + 5, + SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 6, + PRIORITY_PHONE = FIRST_SYSTEM_WINDOW_ + 7, + SYSTEM_DIALOG = FIRST_SYSTEM_WINDOW_ + 8, + KEYGUARD_DIALOG = FIRST_SYSTEM_WINDOW_ + 9, + SYSTEM_ERROR = FIRST_SYSTEM_WINDOW_ + 10, + INPUT_METHOD = FIRST_SYSTEM_WINDOW_ + 11, + INPUT_METHOD_DIALOG = FIRST_SYSTEM_WINDOW_ + 12, + WALLPAPER = FIRST_SYSTEM_WINDOW_ + 13, + STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 14, + SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW_ + 15, + DRAG = FIRST_SYSTEM_WINDOW_ + 16, + STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW_ + 17, + POINTER = FIRST_SYSTEM_WINDOW_ + 18, + NAVIGATION_BAR = FIRST_SYSTEM_WINDOW_ + 19, + VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW_ + 20, + BOOT_PROGRESS = FIRST_SYSTEM_WINDOW_ + 21, + INPUT_CONSUMER = FIRST_SYSTEM_WINDOW_ + 22, + NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW_ + 24, + MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 27, + ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW_ + 32, + DOCK_DIVIDER = FIRST_SYSTEM_WINDOW_ + 34, + ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW_ + 39, + NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW_ + 40, + + FIRST_SYSTEM_WINDOW = FIRST_SYSTEM_WINDOW_, LAST_SYSTEM_WINDOW = 2999, + +#undef FIRST_SYSTEM_WINDOW_ + + // Small range to limit LUT size. + ftl_first = FIRST_SYSTEM_WINDOW, + ftl_last = FIRST_SYSTEM_WINDOW + 15 }; - enum class Feature { + enum class Feature : uint32_t { DISABLE_TOUCH_PAD_GESTURES = 0x00000001, NO_INPUT_CHANNEL = 0x00000002, DISABLE_USER_ACTIVITY = 0x00000004, @@ -265,4 +276,4 @@ protected: WindowInfo mInfo; }; -} // namespace android::gui \ No newline at end of file +} // namespace android::gui diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 220c8e1e6e..69ae9a02ec 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -228,7 +228,7 @@ void InputDeviceInfo::addMotionRange(const MotionRange& range) { void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo& info) { if (mSensors.find(info.type) != mSensors.end()) { ALOGW("Sensor type %s already exists, will be replaced by new sensor added.", - NamedEnum::string(info.type).c_str()); + ftl::enum_string(info.type).c_str()); } mSensors.insert_or_assign(info.type, info); } diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 1e93dfb488..91ab008161 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -30,10 +30,10 @@ static constexpr bool DEBUG_TRANSPORT_ACTIONS = false; #include #include #include +#include #include #include -#include #include using android::base::StringPrintf; @@ -714,7 +714,7 @@ android::base::Result InputPublisher::receiveC } ALOGE("channel '%s' publisher ~ Received unexpected %s message from consumer", - mChannel->getName().c_str(), NamedEnum::string(msg.header.type).c_str()); + mChannel->getName().c_str(), ftl::enum_string(msg.header.type).c_str()); return android::base::Error(UNKNOWN_ERROR); } @@ -856,7 +856,7 @@ status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consum case InputMessage::Type::TIMELINE: { LOG_ALWAYS_FATAL("Consumed a %s message, which should never be seen by " "InputConsumer!", - NamedEnum::string(mMsg.header.type).c_str()); + ftl::enum_string(mMsg.header.type).c_str()); break; } @@ -1449,14 +1449,14 @@ std::string InputConsumer::dump() const { out = out + "mChannel = " + mChannel->getName() + "\n"; out = out + "mMsgDeferred: " + toString(mMsgDeferred) + "\n"; if (mMsgDeferred) { - out = out + "mMsg : " + NamedEnum::string(mMsg.header.type) + "\n"; + out = out + "mMsg : " + ftl::enum_string(mMsg.header.type) + "\n"; } out += "Batches:\n"; for (const Batch& batch : mBatches) { out += " Batch:\n"; for (const InputMessage& msg : batch.samples) { out += android::base::StringPrintf(" Message %" PRIu32 ": %s ", msg.header.seq, - NamedEnum::string(msg.header.type).c_str()); + ftl::enum_string(msg.header.type).c_str()); switch (msg.header.type) { case InputMessage::Type::KEY: { out += android::base::StringPrintf("action=%s keycode=%" PRId32, diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index c365ab070e..7c25cda9ac 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -16,10 +16,8 @@ #define LOG_TAG "KeyLayoutMap" -#include - #include -#include +#include #include #include #include @@ -28,6 +26,10 @@ #include #include +#include +#include +#include + // Enables debug output for the parser. #define DEBUG_PARSER 0 @@ -39,37 +41,39 @@ namespace android { +namespace { -static const char* WHITESPACE = " \t\r"; - -#define SENSOR_ENTRY(type) NamedEnum::string(type), type -static const std::unordered_map SENSOR_LIST = - {{SENSOR_ENTRY(InputDeviceSensorType::ACCELEROMETER)}, - {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD)}, - {SENSOR_ENTRY(InputDeviceSensorType::ORIENTATION)}, - {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE)}, - {SENSOR_ENTRY(InputDeviceSensorType::LIGHT)}, - {SENSOR_ENTRY(InputDeviceSensorType::PRESSURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::TEMPERATURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::PROXIMITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::GRAVITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::LINEAR_ACCELERATION)}, - {SENSOR_ENTRY(InputDeviceSensorType::ROTATION_VECTOR)}, - {SENSOR_ENTRY(InputDeviceSensorType::RELATIVE_HUMIDITY)}, - {SENSOR_ENTRY(InputDeviceSensorType::AMBIENT_TEMPERATURE)}, - {SENSOR_ENTRY(InputDeviceSensorType::MAGNETIC_FIELD_UNCALIBRATED)}, - {SENSOR_ENTRY(InputDeviceSensorType::GAME_ROTATION_VECTOR)}, - {SENSOR_ENTRY(InputDeviceSensorType::GYROSCOPE_UNCALIBRATED)}, - {SENSOR_ENTRY(InputDeviceSensorType::SIGNIFICANT_MOTION)}}; - -// --- KeyLayoutMap --- - -KeyLayoutMap::KeyLayoutMap() { -} +constexpr const char* WHITESPACE = " \t\r"; -KeyLayoutMap::~KeyLayoutMap() { +template +constexpr auto sensorPair() { + return std::make_pair(ftl::enum_name(), S); } +static const std::unordered_map SENSOR_LIST = + {sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair(), + sensorPair()}; + +} // namespace + +KeyLayoutMap::KeyLayoutMap() = default; +KeyLayoutMap::~KeyLayoutMap() = default; + base::Result> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { Tokenizer* tokenizer; @@ -160,8 +164,8 @@ base::Result> KeyLayoutMap::mapSensor( const Sensor& sensor = it->second; #if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=0x%0x, sensorDataIndex=0x%x.", absCode, - NamedEnum::string(sensor.sensorType), sensor.sensorDataIndex); + ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); #endif return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } @@ -513,7 +517,7 @@ status_t KeyLayoutMap::Parser::parseLed() { } static std::optional getSensorType(const char* token) { - auto it = SENSOR_LIST.find(std::string(token)); + auto it = SENSOR_LIST.find(token); if (it == SENSOR_LIST.end()) { return std::nullopt; } @@ -581,8 +585,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { int32_t sensorDataIndex = indexOpt.value(); #if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%d, sensorDataIndex=%d.", code, - NamedEnum::string(sensorType).c_str(), sensorDataIndex); + ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + ftl::enum_string(sensorType).c_str(), sensorDataIndex); #endif Sensor sensor; @@ -591,4 +595,5 @@ status_t KeyLayoutMap::Parser::parseSensor() { map.emplace(code, sensor); return NO_ERROR; } -}; + +} // namespace android diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp index 05ef489133..a864cf8202 100644 --- a/services/inputflinger/InputReaderBase.cpp +++ b/services/inputflinger/InputReaderBase.cpp @@ -19,12 +19,12 @@ //#define LOG_NDEBUG 0 #include "InputReaderBase.h" -#include #include "input/DisplayViewport.h" #include "input/Input.h" -#include #include +#include +#include #define INDENT " " #define INDENT2 " " @@ -117,7 +117,7 @@ std::optional InputReaderConfiguration::getDisplayViewportByTyp } if (count > 1) { ALOGW("Found %zu viewports with type %s, but expected 1 at most", count, - NamedEnum::string(type).c_str()); + ftl::enum_string(type).c_str()); } return result; } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 547021ca22..5365a78b0a 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -40,6 +40,8 @@ struct EventEntry { POINTER_CAPTURE_CHANGED, DRAG, TOUCH_MODE_CHANGED, + + ftl_last = TOUCH_MODE_CHANGED }; int32_t id; diff --git a/services/inputflinger/dispatcher/FocusResolver.cpp b/services/inputflinger/dispatcher/FocusResolver.cpp index 4a75773201..600f02ba80 100644 --- a/services/inputflinger/dispatcher/FocusResolver.cpp +++ b/services/inputflinger/dispatcher/FocusResolver.cpp @@ -27,7 +27,7 @@ static constexpr bool DEBUG_FOCUS = false; #include #include -#include +#include #include #include @@ -65,7 +65,7 @@ std::optional FocusResolver::setInputWindows( if (result == Focusability::OK) { return std::nullopt; } - removeFocusReason = NamedEnum::string(result); + removeFocusReason = ftl::enum_string(result); } // We don't have a focused window or the currently focused window is no longer focusable. Check @@ -79,7 +79,7 @@ std::optional FocusResolver::setInputWindows( if (result == Focusability::OK) { return updateFocusedWindow(displayId, "Window became focusable. Previous reason: " + - NamedEnum::string(previousResult), + ftl::enum_string(previousResult), requestedFocus, request->windowName); } } @@ -116,7 +116,7 @@ std::optional FocusResolver::setFocusedWindow( request.token, request.windowName); } ALOGW("setFocusedWindow %s on display %" PRId32 " ignored, reason: %s", - request.windowName.c_str(), displayId, NamedEnum::string(result).c_str()); + request.windowName.c_str(), displayId, ftl::enum_string(result).c_str()); return std::nullopt; } @@ -134,7 +134,7 @@ std::optional FocusResolver::setFocusedWindow( // The requested window is not currently focusable. Wait for the window to become focusable // but remove focus from the current window so that input events can go into a pending queue // and be sent to the window when it becomes focused. - return updateFocusedWindow(displayId, "Waiting for window because " + NamedEnum::string(result), + return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result), nullptr); } @@ -212,7 +212,7 @@ std::string FocusResolver::dump() const { for (const auto& [displayId, request] : mFocusRequestByDisplay) { auto it = mLastFocusResultByDisplay.find(displayId); std::string result = - it != mLastFocusResultByDisplay.end() ? NamedEnum::string(it->second) : ""; + it != mLastFocusResultByDisplay.end() ? ftl::enum_string(it->second) : ""; dump += base::StringPrintf(INDENT2 "displayId=%" PRId32 ", name='%s' result='%s'\n", displayId, request.windowName.c_str(), result.c_str()); } diff --git a/services/inputflinger/dispatcher/FocusResolver.h b/services/inputflinger/dispatcher/FocusResolver.h index 1d6cd9a5fa..6d11a77aad 100644 --- a/services/inputflinger/dispatcher/FocusResolver.h +++ b/services/inputflinger/dispatcher/FocusResolver.h @@ -77,6 +77,8 @@ private: NO_WINDOW, NOT_FOCUSABLE, NOT_VISIBLE, + + ftl_last = NOT_VISIBLE }; // Checks if the window token can be focused on a display. The token can be focused if there is @@ -113,4 +115,4 @@ private: std::optional getFocusRequest(int32_t displayId); }; -} // namespace android::inputdispatcher \ No newline at end of file +} // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 92ba52c3f4..9da71925ce 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -1151,7 +1152,7 @@ void InputDispatcher::dropInboundEventLocked(const EventEntry& entry, DropReason case EventEntry::Type::TOUCH_MODE_CHANGED: case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { - LOG_ALWAYS_FATAL("Should not drop %s events", NamedEnum::string(entry.type).c_str()); + LOG_ALWAYS_FATAL("Should not drop %s events", ftl::enum_string(entry.type).c_str()); break; } } @@ -1573,7 +1574,7 @@ void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, ALOGD("notifySensorEvent eventTime=%" PRId64 ", hwTimestamp=%" PRId64 ", deviceId=%d, " "source=0x%x, sensorType=%s", entry->eventTime, entry->hwTimestamp, entry->deviceId, entry->source, - NamedEnum::string(entry->sensorType).c_str()); + ftl::enum_string(entry->sensorType).c_str()); } auto command = [this, entry]() REQUIRES(mLock) { scoped_unlock unlock(mLock); @@ -1590,7 +1591,7 @@ void InputDispatcher::dispatchSensorLocked(nsecs_t currentTime, bool InputDispatcher::flushSensor(int deviceId, InputDeviceSensorType sensorType) { if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("flushSensor deviceId=%d, sensorType=%s", deviceId, - NamedEnum::string(sensorType).c_str()); + ftl::enum_string(sensorType).c_str()); } { // acquire lock std::scoped_lock _l(mLock); @@ -1811,7 +1812,7 @@ int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) { case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: case EventEntry::Type::DRAG: { - ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str()); + ALOGE("%s events do not have a target display", ftl::enum_string(entry.type).c_str()); return ADISPLAY_ID_NONE; } } @@ -1863,7 +1864,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( if (focusedWindowHandle == nullptr && focusedApplicationHandle == nullptr) { ALOGI("Dropping %s event because there is no focused window or focused application in " "display %" PRId32 ".", - NamedEnum::string(entry.type).c_str(), displayId); + ftl::enum_string(entry.type).c_str(), displayId); return InputEventInjectionResult::FAILED; } @@ -1888,7 +1889,7 @@ InputEventInjectionResult InputDispatcher::findFocusedWindowTargetsLocked( } else if (currentTime > *mNoFocusedWindowTimeoutTime) { // Already raised ANR. Drop the event ALOGE("Dropping %s event because there is no focused window", - NamedEnum::string(entry.type).c_str()); + ftl::enum_string(entry.type).c_str()); return InputEventInjectionResult::FAILED; } else { // Still waiting for the focused window @@ -2676,8 +2677,7 @@ std::string InputDispatcher::dumpWindowForTouchOcclusion(const WindowInfo* info, "frame=[%" PRId32 ",%" PRId32 "][%" PRId32 ",%" PRId32 "], touchableRegion=%s, window={%s}, flags={%s}, inputFeatures={%s}, " "hasToken=%s, applicationInfo.name=%s, applicationInfo.token=%s\n", - (isTouchedWindow) ? "[TOUCHED] " : "", - NamedEnum::string(info->type, "%" PRId32).c_str(), + isTouchedWindow ? "[TOUCHED] " : "", ftl::enum_string(info->type).c_str(), info->packageName.c_str(), info->ownerUid, info->id, toString(info->touchOcclusionMode).c_str(), info->alpha, info->frameLeft, info->frameTop, info->frameRight, info->frameBottom, @@ -2804,7 +2804,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s events are not user activity", - NamedEnum::string(eventEntry.type).c_str()); + ftl::enum_string(eventEntry.type).c_str()); break; } } @@ -2849,7 +2849,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, if (inputTarget.flags & InputTarget::FLAG_SPLIT) { LOG_ALWAYS_FATAL_IF(eventEntry->type != EventEntry::Type::MOTION, "Entry type %s should not have FLAG_SPLIT", - NamedEnum::string(eventEntry->type).c_str()); + ftl::enum_string(eventEntry->type).c_str()); const MotionEntry& originalMotionEntry = static_cast(*eventEntry); if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) { @@ -3037,7 +3037,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp& connectio case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: { LOG_ALWAYS_FATAL("%s events should not go to apps", - NamedEnum::string(newEntry.type).c_str()); + ftl::enum_string(newEntry.type).c_str()); break; } } @@ -3276,7 +3276,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events", - NamedEnum::string(eventEntry.type).c_str()); + ftl::enum_string(eventEntry.type).c_str()); return; } } @@ -3592,14 +3592,14 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( case EventEntry::Type::POINTER_CAPTURE_CHANGED: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("Canceling %s events is not supported", - NamedEnum::string(cancelationEventEntry->type).c_str()); + ftl::enum_string(cancelationEventEntry->type).c_str()); break; } case EventEntry::Type::CONFIGURATION_CHANGED: case EventEntry::Type::DEVICE_RESET: case EventEntry::Type::SENSOR: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - NamedEnum::string(cancelationEventEntry->type).c_str()); + ftl::enum_string(cancelationEventEntry->type).c_str()); break; } } @@ -3659,7 +3659,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( case EventEntry::Type::SENSOR: case EventEntry::Type::DRAG: { LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue", - NamedEnum::string(downEventEntry->type).c_str()); + ftl::enum_string(downEventEntry->type).c_str()); break; } } @@ -4007,7 +4007,7 @@ void InputDispatcher::notifySensor(const NotifySensorArgs* args) { ALOGD("notifySensor - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=0x%x, " " sensorType=%s", args->id, args->eventTime, args->deviceId, args->source, - NamedEnum::string(args->sensorType).c_str()); + ftl::enum_string(args->sensorType).c_str()); } bool needWake; @@ -5115,7 +5115,7 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) { toString(windowInfo->hasWallpaper), toString(windowInfo->visible), windowInfo->alpha, windowInfo->flags.string().c_str(), - NamedEnum::string(windowInfo->type).c_str(), + ftl::enum_string(windowInfo->type).c_str(), windowInfo->frameLeft, windowInfo->frameTop, windowInfo->frameRight, windowInfo->frameBottom, windowInfo->globalScaleFactor, diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index 0f0ad0a102..d10f8b6605 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -263,7 +264,7 @@ static std::vector allFilesInPath(const std::filesystem:: */ static std::vector findSysfsNodes(const std::filesystem::path& sysfsRoot, SysfsClass clazz) { - std::string nodeStr = NamedEnum::string(clazz); + std::string nodeStr = ftl::enum_string(clazz); std::for_each(nodeStr.begin(), nodeStr.end(), [](char& c) { c = std::tolower(static_cast(c)); }); std::vector nodes; diff --git a/services/inputflinger/reader/controller/PeripheralController.cpp b/services/inputflinger/reader/controller/PeripheralController.cpp index 9c8a29a059..a6934960c9 100644 --- a/services/inputflinger/reader/controller/PeripheralController.cpp +++ b/services/inputflinger/reader/controller/PeripheralController.cpp @@ -17,9 +17,9 @@ #include #include -#include "../Macros.h" +#include -#include +#include "../Macros.h" #include "PeripheralController.h" // Log detailed debug messages about input device lights. @@ -286,7 +286,7 @@ void PeripheralController::dump(std::string& dump) { for (const auto& [lightId, light] : mLights) { dump += StringPrintf(INDENT4 "Id: %d", lightId); dump += StringPrintf(INDENT4 "Name: %s", light->name.c_str()); - dump += StringPrintf(INDENT4 "Type: %s", NamedEnum::string(light->type).c_str()); + dump += StringPrintf(INDENT4 "Type: %s", ftl::enum_string(light->type).c_str()); light->dump(dump); } } @@ -487,7 +487,7 @@ bool PeripheralController::setLightColor(int32_t lightId, int32_t color) { auto& light = it->second; if (DEBUG_LIGHT_DETAILS) { ALOGD("setLightColor lightId %d type %s color 0x%x", lightId, - NamedEnum::string(light->type).c_str(), color); + ftl::enum_string(light->type).c_str(), color); } return light->setLightColor(color); } @@ -501,7 +501,7 @@ std::optional PeripheralController::getLightColor(int32_t lightId) { std::optional color = light->getLightColor(); if (DEBUG_LIGHT_DETAILS) { ALOGD("getLightColor lightId %d type %s color 0x%x", lightId, - NamedEnum::string(light->type).c_str(), color.value_or(0)); + ftl::enum_string(light->type).c_str(), color.value_or(0)); } return color; } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index 7a00bace6e..1f96294e96 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -146,6 +146,8 @@ enum class InputDeviceClass : uint32_t { enum class SysfsClass : uint32_t { POWER_SUPPLY = 0, LEDS = 1, + + ftl_last = LEDS }; enum class LightColor : uint32_t { diff --git a/services/inputflinger/reader/mapper/SensorInputMapper.cpp b/services/inputflinger/reader/mapper/SensorInputMapper.cpp index a507632d0e..a1bd548403 100644 --- a/services/inputflinger/reader/mapper/SensorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/SensorInputMapper.cpp @@ -16,8 +16,9 @@ #include -#include "../Macros.h" +#include +#include "../Macros.h" #include "SensorInputMapper.h" // Log detailed debug messages about each sensor event notification to the dispatcher. @@ -93,7 +94,7 @@ void SensorInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT3 " mHasHardwareTimestamp %d\n", mHasHardwareTimestamp); dump += INDENT3 "Sensors:\n"; for (const auto& [sensorType, sensor] : mSensors) { - dump += StringPrintf(INDENT4 "%s\n", NamedEnum::string(sensorType).c_str()); + dump += StringPrintf(INDENT4 "%s\n", ftl::enum_string(sensorType).c_str()); dump += StringPrintf(INDENT5 "enabled: %d\n", sensor.enabled); dump += StringPrintf(INDENT5 "samplingPeriod: %lld\n", sensor.samplingPeriod.count()); dump += StringPrintf(INDENT5 "maxBatchReportLatency: %lld\n", @@ -208,10 +209,10 @@ SensorInputMapper::Sensor SensorInputMapper::createSensor(InputDeviceSensorType axis.max /* maxRange */, axis.scale /* resolution */, 0.0f /* power */, 0 /* minDelay */, 0 /* fifoReservedEventCount */, 0 /* fifoMaxEventCount */, - NamedEnum::string(sensorType), 0 /* maxDelay */, 0 /* flags */, + ftl::enum_string(sensorType), 0 /* maxDelay */, 0 /* flags */, getDeviceId()); - std::string prefix = "sensor." + NamedEnum::string(sensorType); + std::string prefix = "sensor." + ftl::enum_string(sensorType); transform(prefix.begin(), prefix.end(), prefix.begin(), ::tolower); int32_t reportingMode = 0; @@ -335,7 +336,7 @@ bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, std::chrono::microseconds maxBatchReportLatency) { if (DEBUG_SENSOR_EVENT_DETAILS) { ALOGD("Enable Sensor %s samplingPeriod %lld maxBatchReportLatency %lld", - NamedEnum::string(sensorType).c_str(), samplingPeriod.count(), + ftl::enum_string(sensorType).c_str(), samplingPeriod.count(), maxBatchReportLatency.count()); } @@ -359,7 +360,7 @@ bool SensorInputMapper::enableSensor(InputDeviceSensorType sensorType, void SensorInputMapper::disableSensor(InputDeviceSensorType sensorType) { if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Disable Sensor %s", NamedEnum::string(sensorType).c_str()); + ALOGD("Disable Sensor %s", ftl::enum_string(sensorType).c_str()); } if (!setSensorEnabled(sensorType, false /* enabled */)) { @@ -393,13 +394,12 @@ void SensorInputMapper::sync(nsecs_t when, bool force) { nsecs_t timestamp = mHasHardwareTimestamp ? mHardwareTimestamp : when; if (DEBUG_SENSOR_EVENT_DETAILS) { ALOGD("Sensor %s timestamp %" PRIu64 " values [%f %f %f]", - NamedEnum::string(sensorType).c_str(), timestamp, values[0], values[1], - values[2]); + ftl::enum_string(sensorType).c_str(), timestamp, values[0], values[1], values[2]); } if (sensor.lastSampleTimeNs.has_value() && timestamp - sensor.lastSampleTimeNs.value() < sensor.samplingPeriod.count()) { if (DEBUG_SENSOR_EVENT_DETAILS) { - ALOGD("Sensor %s Skip a sample.", NamedEnum::string(sensorType).c_str()); + ALOGD("Sensor %s Skip a sample.", ftl::enum_string(sensorType).c_str()); } } else { // Convert to Android unit diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index ac5f6b652b..419b0d0eed 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -18,9 +18,10 @@ #include "../Macros.h" // clang-format on -#include #include "TouchInputMapper.h" +#include + #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" #include "TouchButtonAccumulator.h" @@ -259,7 +260,7 @@ void TouchInputMapper::populateDeviceInfo(InputDeviceInfo* info) { void TouchInputMapper::dump(std::string& dump) { dump += StringPrintf(INDENT2 "Touch Input Mapper (mode - %s):\n", - NamedEnum::string(mDeviceMode).c_str()); + ftl::enum_string(mDeviceMode).c_str()); dumpParameters(dump); dumpVirtualKeys(dump); dumpRawPointerAxes(dump); @@ -515,9 +516,9 @@ void TouchInputMapper::configureParameters() { void TouchInputMapper::dumpParameters(std::string& dump) { dump += INDENT3 "Parameters:\n"; - dump += INDENT4 "GestureMode: " + NamedEnum::string(mParameters.gestureMode) + "\n"; + dump += INDENT4 "GestureMode: " + ftl::enum_string(mParameters.gestureMode) + "\n"; - dump += INDENT4 "DeviceType: " + NamedEnum::string(mParameters.deviceType) + "\n"; + dump += INDENT4 "DeviceType: " + ftl::enum_string(mParameters.deviceType) + "\n"; dump += StringPrintf(INDENT4 "AssociatedDisplay: hasAssociatedDisplay=%s, isExternal=%s, " "displayId='%s'\n", @@ -525,7 +526,7 @@ void TouchInputMapper::dumpParameters(std::string& dump) { toString(mParameters.associatedDisplayIsExternal), mParameters.uniqueDisplayId.c_str()); dump += StringPrintf(INDENT4 "OrientationAware: %s\n", toString(mParameters.orientationAware)); - dump += INDENT4 "Orientation: " + NamedEnum::string(mParameters.orientation) + "\n"; + dump += INDENT4 "Orientation: " + ftl::enum_string(mParameters.orientation) + "\n"; } void TouchInputMapper::configureRawPointerAxes() { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index e104220e47..a56468f445 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -185,6 +185,8 @@ protected: UNSCALED, // unscaled mapping (touchpad) NAVIGATION, // unscaled mapping with assist gesture (touch navigation) POINTER, // pointer mapping (pointer) + + ftl_last = POINTER }; DeviceMode mDeviceMode; @@ -198,6 +200,8 @@ protected: TOUCH_PAD, TOUCH_NAVIGATION, POINTER, + + ftl_last = POINTER }; DeviceType deviceType; @@ -210,6 +214,8 @@ protected: ORIENTATION_90 = DISPLAY_ORIENTATION_90, ORIENTATION_180 = DISPLAY_ORIENTATION_180, ORIENTATION_270 = DISPLAY_ORIENTATION_270, + + ftl_last = ORIENTATION_270 }; Orientation orientation; @@ -219,6 +225,8 @@ protected: enum class GestureMode { SINGLE_TOUCH, MULTI_TOUCH, + + ftl_last = MULTI_TOUCH }; GestureMode gestureMode; @@ -818,4 +826,4 @@ private: } // namespace android -#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H \ No newline at end of file +#endif // _UI_INPUTREADER_TOUCH_INPUT_MAPPER_H diff --git a/services/surfaceflinger/ClientCache.cpp b/services/surfaceflinger/ClientCache.cpp index f310738423..1ef8f78894 100644 --- a/services/surfaceflinger/ClientCache.cpp +++ b/services/surfaceflinger/ClientCache.cpp @@ -21,12 +21,12 @@ #include +#include + #include "ClientCache.h" namespace android { -using base::StringAppendF; - ANDROID_SINGLETON_STATIC_INSTANCE(ClientCache); ClientCache::ClientCache() : mDeathRecipient(new CacheDeathRecipient) {} @@ -212,16 +212,15 @@ void ClientCache::CacheDeathRecipient::binderDied(const wp& who) { void ClientCache::dump(std::string& result) { std::lock_guard lock(mMutex); - for (auto i : mBuffers) { - const sp& cacheOwner = i.second.first; - StringAppendF(&result," Cache owner: %p\n", cacheOwner.get()); - auto &buffers = i.second.second; - for (auto& [id, clientCacheBuffer] : buffers) { - StringAppendF(&result, "\t ID: %d, Width/Height: %d,%d\n", (int)id, - (int)clientCacheBuffer.buffer->getBuffer()->getWidth(), - (int)clientCacheBuffer.buffer->getBuffer()->getHeight()); + for (const auto& [_, cache] : mBuffers) { + base::StringAppendF(&result, " Cache owner: %p\n", cache.first.get()); + + for (const auto& [id, entry] : cache.second) { + const auto& buffer = entry.buffer->getBuffer(); + base::StringAppendF(&result, "\tID: %" PRIu64 ", size: %ux%u\n", id, buffer->getWidth(), + buffer->getHeight()); } } } -}; // namespace android +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp index 936dba3b29..2532e3df5d 100644 --- a/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/planner/LayerState.cpp @@ -93,11 +93,7 @@ Flags LayerState::getDifferingFields(const LayerState& other) c void LayerState::dump(std::string& result) const { for (const StateInterface* field : getNonUniqueFields()) { - if (auto viewOpt = flag_name(field->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append(":\n"); - } + base::StringAppendF(&result, " %16s: ", ftl::flag_string(field->getField()).c_str()); bool first = true; for (const std::string& line : field->toStrings()) { @@ -126,11 +122,7 @@ std::optional LayerState::compare(const LayerState& other) const { continue; } - if (auto viewOpt = flag_name(thisField->getField()); viewOpt) { - base::StringAppendF(&result, " %16s: ", std::string(*viewOpt).c_str()); - } else { - result.append(":\n"); - } + base::StringAppendF(&result, " %16s: ", ftl::flag_string(thisField->getField()).c_str()); const auto& thisStrings = thisField->toStrings(); const auto& otherStrings = otherField->toStrings(); -- cgit v1.2.3-59-g8ed1b From ca20550407a1707c48f4c3a839ef55725962f19c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 16 Jul 2021 21:31:58 +0000 Subject: Cancel wallpaper touch stream when foreground window is gone When the foreground window is gone, we should also cancel the touch stream for its wallpaper window. We already cancel touch for the window itself, and for global monitor. Without this patch, the wallpaper window simply stops receiving touches, and never gets CANCEL, either. The behaviour is slightly strange for the global monitor in this case. First of all, the global monitor receives CANCEL, which is questionable (things like pointer location stop working). Also, it only gets cancel when a *new* event comes in. That said, it's probably fine, so let's just document this behaviour by adding a test for it. Bug: 192981537 Test: atest inputflinger_tests Change-Id: I8a2ef7cd552acc5cf64b2e13a6df5d5988bd1808 --- include/input/Input.h | 12 +- libs/input/Input.cpp | 4 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 20 +- services/inputflinger/dispatcher/TouchState.cpp | 10 + services/inputflinger/dispatcher/TouchState.h | 1 + .../inputflinger/tests/InputDispatcher_test.cpp | 267 ++++++++++++++++++++- 6 files changed, 302 insertions(+), 12 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index d2d9fd48e8..f170f0fa23 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -524,13 +524,17 @@ public: inline int32_t getAction() const { return mAction; } - inline int32_t getActionMasked() const { return mAction & AMOTION_EVENT_ACTION_MASK; } + static int32_t getActionMasked(int32_t action) { return action & AMOTION_EVENT_ACTION_MASK; } - inline int32_t getActionIndex() const { - return (mAction & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) - >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + inline int32_t getActionMasked() const { return getActionMasked(mAction); } + + static int32_t getActionIndex(int32_t action) { + return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> + AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } + inline int32_t getActionIndex() const { return getActionIndex(mAction); } + inline void setAction(int32_t action) { mAction = action; } inline int32_t getFlags() const { return mFlags; } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 1e8ff945ef..037849eac4 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -834,9 +834,9 @@ std::string MotionEvent::actionToString(int32_t action) { case AMOTION_EVENT_ACTION_OUTSIDE: return "OUTSIDE"; case AMOTION_EVENT_ACTION_POINTER_DOWN: - return "POINTER_DOWN"; + return StringPrintf("POINTER_DOWN(%" PRId32 ")", MotionEvent::getActionIndex(action)); case AMOTION_EVENT_ACTION_POINTER_UP: - return "POINTER_UP"; + return StringPrintf("POINTER_UP(%" PRId32 ")", MotionEvent::getActionIndex(action)); case AMOTION_EVENT_ACTION_HOVER_MOVE: return "HOVER_MOVE"; case AMOTION_EVENT_ACTION_SCROLL: diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 058e099faa..ce383d9f25 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -1706,13 +1706,13 @@ void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionE if (DEBUG_OUTBOUND_EVENT_DETAILS) { ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", policyFlags=0x%x, " - "action=0x%x, actionButton=0x%x, flags=0x%x, " + "action=%s, actionButton=0x%x, flags=0x%x, " "metaState=0x%x, buttonState=0x%x," "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%" PRId64, prefix, entry.eventTime, entry.deviceId, entry.source, entry.displayId, - entry.policyFlags, entry.action, entry.actionButton, entry.flags, entry.metaState, - entry.buttonState, entry.edgeFlags, entry.xPrecision, entry.yPrecision, - entry.downTime); + entry.policyFlags, MotionEvent::actionToString(entry.action).c_str(), + entry.actionButton, entry.flags, entry.metaState, entry.buttonState, entry.edgeFlags, + entry.xPrecision, entry.yPrecision, entry.downTime); for (uint32_t i = 0; i < entry.pointerCount; i++) { ALOGD(" Pointer %d: id=%d, toolType=%d, " @@ -4642,6 +4642,18 @@ void InputDispatcher::setInputWindowsLocked( CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, "touched window was removed"); synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options); + // Since we are about to drop the touch, cancel the events for the wallpaper as + // well. + if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND && + touchedWindow.windowHandle->getInfo()->hasWallpaper) { + sp wallpaper = state.getWallpaperWindow(); + if (wallpaper != nullptr) { + sp wallpaperConnection = + getConnectionLocked(wallpaper->getToken()); + synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, + options); + } + } } state.windows.erase(state.windows.begin() + i); } else { diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp index b7ed658777..4a50a683e9 100644 --- a/services/inputflinger/dispatcher/TouchState.cpp +++ b/services/inputflinger/dispatcher/TouchState.cpp @@ -134,4 +134,14 @@ bool TouchState::isSlippery() const { return haveSlipperyForegroundWindow; } +sp TouchState::getWallpaperWindow() const { + for (size_t i = 0; i < windows.size(); i++) { + const TouchedWindow& window = windows[i]; + if (window.windowHandle->getInfo()->type == WindowInfo::Type::WALLPAPER) { + return window.windowHandle; + } + } + return nullptr; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h index 579b868443..7dcf55d813 100644 --- a/services/inputflinger/dispatcher/TouchState.h +++ b/services/inputflinger/dispatcher/TouchState.h @@ -51,6 +51,7 @@ struct TouchState { void filterNonMonitors(); sp getFirstForegroundWindowHandle() const; bool isSlippery() const; + sp getWallpaperWindow() const; }; } // namespace inputdispatcher diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 0a91bde03b..e223372fe1 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -74,6 +74,12 @@ static KeyEvent getTestKeyEvent() { return event; } +static void assertMotionAction(int32_t expectedAction, int32_t receivedAction) { + ASSERT_EQ(expectedAction, receivedAction) + << "expected " << MotionEvent::actionToString(expectedAction) << ", got " + << MotionEvent::actionToString(receivedAction); +} + // --- FakeInputDispatcherPolicy --- class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface { @@ -801,7 +807,8 @@ public: } case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent& motionEvent = static_cast(*event); - EXPECT_EQ(expectedAction, motionEvent.getAction()); + assertMotionAction(expectedAction, motionEvent.getAction()); + if (expectedFlags.has_value()) { EXPECT_EQ(expectedFlags.value(), motionEvent.getFlags()); } @@ -982,6 +989,10 @@ public: mInfo.addTouchableRegion(frame); } + void setType(WindowInfo::Type type) { mInfo.type = type; } + + void setHasWallpaper(bool hasWallpaper) { mInfo.hasWallpaper = hasWallpaper; } + void addFlags(Flags flags) { mInfo.flags |= flags; } void setFlags(Flags flags) { mInfo.flags = flags; } @@ -1485,6 +1496,197 @@ TEST_F(InputDispatcherTest, SetInputWindow_MultiWindowsTouch) { windowSecond->assertNoEvents(); } +/** + * Two windows: A top window, and a wallpaper behind the window. + * Touch goes to the top window, and then top window disappears. Ensure that wallpaper window + * gets ACTION_CANCEL. + * 1. foregroundWindow <-- has wallpaper (hasWallpaper=true) + * 2. wallpaperWindow <-- is wallpaper (type=InputWindowInfo::Type::WALLPAPER) + */ +TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCanceled) { + std::shared_ptr application = std::make_shared(); + sp foregroundWindow = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + foregroundWindow->setHasWallpaper(true); + sp wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both foreground window and its wallpaper should receive the touch down + foregroundWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + foregroundWindow->consumeMotionMove(); + wallpaperWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Now the foreground window goes away, but the wallpaper stays + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {wallpaperWindow}}}); + foregroundWindow->consumeMotionCancel(); + // Since the "parent" window of the wallpaper is gone, wallpaper should receive cancel, too. + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); +} + +/** + * A single window that receives touch (on top), and a wallpaper window underneath it. + * The top window gets a multitouch gesture. + * Ensure that wallpaper gets the same gesture. + */ +TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { + std::shared_ptr application = std::make_shared(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); + window->setHasWallpaper(true); + + sp wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + + // Touch down on top window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both top window and its wallpaper should receive the touch down + window->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Second finger down on the top window + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionPointerDown(1 /* pointerIndex */); + wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags); + window->assertNoEvents(); + wallpaperWindow->assertNoEvents(); +} + +/** + * Two windows: a window on the left and window on the right. + * A third window, wallpaper, is behind both windows, and spans both top windows. + * The first touch down goes to the left window. A second pointer touches down on the right window. + * The touch is split, so both left and right windows should receive ACTION_DOWN. + * The wallpaper will get the full event, so it should receive ACTION_DOWN followed by + * ACTION_POINTER_DOWN(1). + */ +TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { + std::shared_ptr application = std::make_shared(); + sp leftWindow = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 200, 200)); + leftWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL); + leftWindow->setHasWallpaper(true); + + sp rightWindow = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + rightWindow->setFrame(Rect(200, 0, 400, 200)); + rightWindow->setFlags(WindowInfo::Flag::SPLIT_TOUCH | WindowInfo::Flag::NOT_TOUCH_MODAL); + rightWindow->setHasWallpaper(true); + + sp wallpaperWindow = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaperWindow->setFrame(Rect(0, 0, 400, 200)); + wallpaperWindow->setType(WindowInfo::Type::WALLPAPER); + constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); + + // Touch down on left window + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both foreground window and its wallpaper should receive the touch down + leftWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // Second finger down on the right window + const MotionEvent secondFingerDownEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_POINTER_DOWN | + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), + AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(300) + .y(100)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + leftWindow->consumeMotionMove(); + // Since the touch is split, right window gets ACTION_DOWN + rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, + expectedWallpaperFlags); + + // Now, leftWindow, which received the first finger, disappears. + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {rightWindow, wallpaperWindow}}}); + leftWindow->consumeMotionCancel(); + // Since a "parent" window of the wallpaper is gone, wallpaper should receive cancel, too. + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // The pointer that's still down on the right window moves, and goes to the right window only. + // As far as the dispatcher's concerned though, both pointers are still present. + const MotionEvent secondFingerMoveEvent = + MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(310) + .y(110)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerMoveEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)); + rightWindow->consumeMotionMove(); + + leftWindow->assertNoEvents(); + rightWindow->assertNoEvents(); + wallpaperWindow->assertNoEvents(); +} + TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) { std::shared_ptr application = std::make_shared(); sp windowLeft = @@ -2301,11 +2503,21 @@ public: expectedDisplayId, expectedFlags); } + void consumeMotionMove(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, + expectedDisplayId, expectedFlags); + } + void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) { mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId, expectedFlags); } + void consumeMotionCancel(int32_t expectedDisplayId, int32_t expectedFlags = 0) { + mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, + expectedDisplayId, expectedFlags); + } + MotionEvent* consumeMotion() { InputEvent* event = mInputReceiver->consume(); if (!event) { @@ -2325,6 +2537,57 @@ private: std::unique_ptr mInputReceiver; }; +/** + * Two entities that receive touch: A window, and a global monitor. + * The touch goes to the window, and then the window disappears. + * The monitor does not get cancel right away. But if more events come in, the touch gets canceled + * for the monitor, as well. + * 1. foregroundWindow + * 2. monitor <-- global monitor (doesn't observe z order, receives all events) + */ +TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_GlobalMonitorTouchIsCanceled) { + std::shared_ptr application = std::make_shared(); + sp window = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + + FakeMonitorReceiver monitor = + FakeMonitorReceiver(mDispatcher, "GlobalMonitor", ADISPLAY_ID_DEFAULT, + false /*isGestureMonitor*/); + + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + // Both the foreground window and the global monitor should receive the touch down + window->consumeMotionDown(); + monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {110, 200})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + + window->consumeMotionMove(); + monitor.consumeMotionMove(ADISPLAY_ID_DEFAULT); + + // Now the foreground window goes away + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {}}}); + window->consumeMotionCancel(); + monitor.assertNoEvents(); // Global monitor does not get a cancel yet + + // If more events come in, there will be no more foreground window to send them to. This will + // cause a cancel for the monitor, as well. + ASSERT_EQ(InputEventInjectionResult::FAILED, + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {120, 200})) + << "Injection should fail because the window was removed"; + window->assertNoEvents(); + // Global monitor now gets the cancel + monitor.consumeMotionCancel(ADISPLAY_ID_DEFAULT); +} + // Tests for gesture monitors TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) { std::shared_ptr application = std::make_shared(); @@ -3567,7 +3830,7 @@ protected: << " event, got " << inputEventTypeToString(event->getType()) << " event"; const MotionEvent& motionEvent = static_cast(*event); - EXPECT_EQ(expectedAction, motionEvent.getAction()); + assertMotionAction(expectedAction, motionEvent.getAction()); for (size_t i = 0; i < points.size(); i++) { float expectedX = points[i].x; -- cgit v1.2.3-59-g8ed1b From b9b18509a6d0f5f074e86802b322a4fb01b43cd5 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 26 Aug 2021 12:30:32 -0700 Subject: Add display transform to MotionEvent for raw coords Since input now works in display coordinates, we need to include the display projection with MotionEvent so that the getRaw API can continue to report logical display coordinates. It is also important to highlight the difference in the coordinate systems used by regular input windows and input monitors. MotionEvents coming from input monitors will always report values in unrotated logical display coordinates, because we do not yet have an API to report unrotated values in MotionEvents. Bug: 179274888 Test: presubmit Test: manual, ensure input works Change-Id: Ief3b2b31c6644beaa2f8c4b90302f441f93ab960 --- include/input/Input.h | 12 +- include/input/InputTransport.h | 29 +-- libs/input/Input.cpp | 83 +++----- libs/input/InputTransport.cpp | 40 ++-- libs/input/tests/InputEvent_test.cpp | 216 +++++++++------------ .../input/tests/InputPublisherAndConsumer_test.cpp | 89 ++++----- libs/input/tests/StructLayout_test.cpp | 14 +- libs/input/tests/VelocityTracker_test.cpp | 3 +- libs/input/tests/VerifiedInputEvent_test.cpp | 4 +- .../benchmarks/InputDispatcher_benchmarks.cpp | 4 +- services/inputflinger/dispatcher/Entry.cpp | 7 +- services/inputflinger/dispatcher/Entry.h | 7 +- .../inputflinger/dispatcher/InputDispatcher.cpp | 63 +++--- services/inputflinger/dispatcher/InputDispatcher.h | 3 +- services/inputflinger/dispatcher/InputTarget.h | 7 +- .../inputflinger/tests/InputDispatcher_test.cpp | 47 ++--- 16 files changed, 280 insertions(+), 348 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index f170f0fa23..d397313ab6 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -579,9 +579,7 @@ public: void setCursorPosition(float x, float y); - uint32_t getDisplayOrientation() const { return mDisplayOrientation; } - - int2 getDisplaySize() const { return {mDisplayWidth, mDisplayHeight}; } + ui::Transform getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } @@ -757,8 +755,8 @@ public: int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, - float rawYCursorPosition, uint32_t displayOrientation, int32_t displayWidth, - int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, + float rawYCursorPosition, const ui::Transform& rawTransform, nsecs_t downTime, + nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); void copyFrom(const MotionEvent* other, bool keepHistory); @@ -816,9 +814,7 @@ protected: float mYPrecision; float mRawXCursorPosition; float mRawYCursorPosition; - uint32_t mDisplayOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; + ui::Transform mRawTransform; nsecs_t mDownTime; Vector mPointerProperties; std::vector mSampleEventTimes; diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index 7632b30bd2..d655b28278 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -114,7 +114,7 @@ struct InputMessage { struct Motion { int32_t eventId; - uint32_t empty1; + uint32_t pointerCount; nsecs_t eventTime __attribute__((aligned(8))); int32_t deviceId; int32_t source; @@ -129,20 +129,22 @@ struct InputMessage { uint8_t empty2[3]; // 3 bytes to fill gap created by classification int32_t edgeFlags; nsecs_t downTime __attribute__((aligned(8))); - float dsdx; - float dtdx; - float dtdy; - float dsdy; - float tx; - float ty; + float dsdx; // Begin window transform + float dtdx; // + float dtdy; // + float dsdy; // + float tx; // + float ty; // End window transform float xPrecision; float yPrecision; float xCursorPosition; float yCursorPosition; - uint32_t displayOrientation; - int32_t displayWidth; - int32_t displayHeight; - uint32_t pointerCount; + float dsdxRaw; // Begin raw transform + float dtdxRaw; // + float dtdyRaw; // + float dsdyRaw; // + float txRaw; // + float tyRaw; // End raw transform /** * The "pointers" field must be the last field of the struct InputMessage. * When we send the struct InputMessage across the socket, we are not @@ -367,9 +369,8 @@ public: int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float xCursorPosition, - float yCursorPosition, uint32_t displayOrientation, - int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, - nsecs_t eventTime, uint32_t pointerCount, + float yCursorPosition, const ui::Transform& rawTransform, + nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 037849eac4..30e5d5b0bc 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -76,36 +76,13 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return result; } -// Rotates the given point to the specified orientation. If the display width and height are -// provided, the point is rotated in the screen space. Otherwise, the point is rotated about the -// origin. This helper is used to avoid the extra overhead of creating new Transforms. -vec2 rotatePoint(uint32_t orientation, float x, float y, int32_t displayWidth = 0, - int32_t displayHeight = 0) { - if (orientation == ui::Transform::ROT_0) { - return {x, y}; - } - - vec2 xy(x, y); - if (orientation == ui::Transform::ROT_90) { - xy.x = displayHeight - y; - xy.y = x; - } else if (orientation == ui::Transform::ROT_180) { - xy.x = displayWidth - x; - xy.y = displayHeight - y; - } else if (orientation == ui::Transform::ROT_270) { - xy.x = y; - xy.y = displayWidth - x; - } - return xy; -} - -vec2 applyTransformWithoutTranslation(const ui::Transform& transform, float x, float y) { +vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) { const vec2 transformedXy = transform.transform(x, y); const vec2 transformedOrigin = transform.transform(0, 0); return transformedXy - transformedOrigin; } -bool shouldDisregardWindowTranslation(uint32_t source) { +bool shouldDisregardTranslation(uint32_t source) { // Pointer events are the only type of events that refer to absolute coordinates on the display, // so we should apply the entire window transform. For other types of events, we should make // sure to not apply the window translation/offset. @@ -431,8 +408,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, float yPrecision, float rawXCursorPosition, float rawYCursorPosition, - uint32_t displayOrientation, int32_t displayWidth, - int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime, size_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { InputEvent::initialize(id, deviceId, source, displayId, hmac); @@ -448,9 +424,7 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mYPrecision = yPrecision; mRawXCursorPosition = rawXCursorPosition; mRawYCursorPosition = rawYCursorPosition; - mDisplayOrientation = displayOrientation; - mDisplayWidth = displayWidth; - mDisplayHeight = displayHeight; + mRawTransform = rawTransform; mDownTime = downTime; mPointerProperties.clear(); mPointerProperties.appendArray(pointerProperties, pointerCount); @@ -474,9 +448,7 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mYPrecision = other->mYPrecision; mRawXCursorPosition = other->mRawXCursorPosition; mRawYCursorPosition = other->mRawYCursorPosition; - mDisplayOrientation = other->mDisplayOrientation; - mDisplayWidth = other->mDisplayWidth; - mDisplayHeight = other->mDisplayHeight; + mRawTransform = other->mRawTransform; mDownTime = other->mDownTime; mPointerProperties = other->mPointerProperties; @@ -542,20 +514,19 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, if (!isPerWindowInputRotationEnabled()) return coords->getAxisValue(axis); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - // For compatibility, convert raw coordinates into "oriented screen space". Once app - // developers are educated about getRaw, we can consider removing this. - const vec2 xy = shouldDisregardWindowTranslation(mSource) - ? rotatePoint(mDisplayOrientation, coords->getX(), coords->getY()) - : rotatePoint(mDisplayOrientation, coords->getX(), coords->getY(), mDisplayWidth, - mDisplayHeight); + // For compatibility, convert raw coordinates into logical display space. + const vec2 xy = shouldDisregardTranslation(mSource) + ? transformWithoutTranslation(mRawTransform, coords->getX(), coords->getY()) + : mRawTransform.transform(coords->getX(), coords->getY()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { - // For compatibility, since we convert raw coordinates into "oriented screen space", we + // For compatibility, since we report raw coordinates in logical display space, we // need to convert the relative axes into the same orientation for consistency. - const vec2 relativeXy = rotatePoint(mDisplayOrientation, + const vec2 relativeXy = + transformWithoutTranslation(mRawTransform, coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; @@ -569,8 +540,8 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = shouldDisregardWindowTranslation(mSource) - ? applyTransformWithoutTranslation(mTransform, coords->getX(), coords->getY()) + const vec2 xy = shouldDisregardTranslation(mSource) + ? transformWithoutTranslation(mTransform, coords->getX(), coords->getY()) : mTransform.transform(coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; @@ -578,11 +549,9 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { const vec2 relativeXy = - applyTransformWithoutTranslation(mTransform, - coords->getAxisValue( - AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue( - AMOTION_EVENT_AXIS_RELATIVE_Y)); + transformWithoutTranslation(mTransform, + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; } @@ -607,6 +576,8 @@ void MotionEvent::offsetLocation(float xOffset, float yOffset) { void MotionEvent::scale(float globalScaleFactor) { mTransform.set(mTransform.tx() * globalScaleFactor, mTransform.ty() * globalScaleFactor); + mRawTransform.set(mRawTransform.tx() * globalScaleFactor, + mRawTransform.ty() * globalScaleFactor); mXPrecision *= globalScaleFactor; mYPrecision *= globalScaleFactor; @@ -708,9 +679,11 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mYPrecision = parcel->readFloat(); mRawXCursorPosition = parcel->readFloat(); mRawYCursorPosition = parcel->readFloat(); - mDisplayOrientation = parcel->readUint32(); - mDisplayWidth = parcel->readInt32(); - mDisplayHeight = parcel->readInt32(); + + result = android::readFromParcel(mRawTransform, *parcel); + if (result != OK) { + return result; + } mDownTime = parcel->readInt64(); mPointerProperties.clear(); @@ -770,9 +743,11 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeFloat(mYPrecision); parcel->writeFloat(mRawXCursorPosition); parcel->writeFloat(mRawYCursorPosition); - parcel->writeUint32(mDisplayOrientation); - parcel->writeInt32(mDisplayWidth); - parcel->writeInt32(mDisplayHeight); + + result = android::writeToParcel(mRawTransform, *parcel); + if (result != OK) { + return result; + } parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 91ab008161..02a5a0807b 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -203,6 +203,8 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::MOTION: { // int32_t eventId msg->body.motion.eventId = body.motion.eventId; + // uint32_t pointerCount + msg->body.motion.pointerCount = body.motion.pointerCount; // nsecs_t eventTime msg->body.motion.eventTime = body.motion.eventTime; // int32_t deviceId @@ -245,14 +247,14 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { msg->body.motion.xCursorPosition = body.motion.xCursorPosition; // float yCursorPosition msg->body.motion.yCursorPosition = body.motion.yCursorPosition; - // uint32_t displayOrientation - msg->body.motion.displayOrientation = body.motion.displayOrientation; - // int32_t displayWidth - msg->body.motion.displayWidth = body.motion.displayWidth; - // int32_t displayHeight - msg->body.motion.displayHeight = body.motion.displayHeight; - // uint32_t pointerCount - msg->body.motion.pointerCount = body.motion.pointerCount; + + msg->body.motion.dsdxRaw = body.motion.dsdxRaw; + msg->body.motion.dtdxRaw = body.motion.dtdxRaw; + msg->body.motion.dtdyRaw = body.motion.dtdyRaw; + msg->body.motion.dsdyRaw = body.motion.dsdyRaw; + msg->body.motion.txRaw = body.motion.txRaw; + msg->body.motion.tyRaw = body.motion.tyRaw; + //struct Pointer pointers[MAX_POINTERS] for (size_t i = 0; i < body.motion.pointerCount; i++) { // PointerProperties properties @@ -542,8 +544,8 @@ status_t InputPublisher::publishMotionEvent( std::array hmac, int32_t action, int32_t actionButton, int32_t flags, int32_t edgeFlags, int32_t metaState, int32_t buttonState, MotionClassification classification, const ui::Transform& transform, float xPrecision, - float yPrecision, float xCursorPosition, float yCursorPosition, uint32_t displayOrientation, - int32_t displayWidth, int32_t displayHeight, nsecs_t downTime, nsecs_t eventTime, + float yPrecision, float xCursorPosition, float yCursorPosition, + const ui::Transform& rawTransform, nsecs_t downTime, nsecs_t eventTime, uint32_t pointerCount, const PointerProperties* pointerProperties, const PointerCoords* pointerCoords) { if (ATRACE_ENABLED()) { @@ -603,9 +605,12 @@ status_t InputPublisher::publishMotionEvent( msg.body.motion.yPrecision = yPrecision; msg.body.motion.xCursorPosition = xCursorPosition; msg.body.motion.yCursorPosition = yCursorPosition; - msg.body.motion.displayOrientation = displayOrientation; - msg.body.motion.displayWidth = displayWidth; - msg.body.motion.displayHeight = displayHeight; + msg.body.motion.dsdxRaw = rawTransform.dsdx(); + msg.body.motion.dtdxRaw = rawTransform.dtdx(); + msg.body.motion.dtdyRaw = rawTransform.dtdy(); + msg.body.motion.dsdyRaw = rawTransform.dsdy(); + msg.body.motion.txRaw = rawTransform.tx(); + msg.body.motion.tyRaw = rawTransform.ty(); msg.body.motion.downTime = downTime; msg.body.motion.eventTime = eventTime; msg.body.motion.pointerCount = pointerCount; @@ -1391,6 +1396,10 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage ui::Transform transform; transform.set({msg->body.motion.dsdx, msg->body.motion.dtdx, msg->body.motion.tx, msg->body.motion.dtdy, msg->body.motion.dsdy, msg->body.motion.ty, 0, 0, 1}); + ui::Transform displayTransform; + displayTransform.set({msg->body.motion.dsdxRaw, msg->body.motion.dtdxRaw, + msg->body.motion.txRaw, msg->body.motion.dtdyRaw, + msg->body.motion.dsdyRaw, msg->body.motion.tyRaw, 0, 0, 1}); event->initialize(msg->body.motion.eventId, msg->body.motion.deviceId, msg->body.motion.source, msg->body.motion.displayId, msg->body.motion.hmac, msg->body.motion.action, msg->body.motion.actionButton, msg->body.motion.flags, @@ -1398,9 +1407,8 @@ void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage msg->body.motion.buttonState, msg->body.motion.classification, transform, msg->body.motion.xPrecision, msg->body.motion.yPrecision, msg->body.motion.xCursorPosition, msg->body.motion.yCursorPosition, - msg->body.motion.displayOrientation, msg->body.motion.displayWidth, - msg->body.motion.displayHeight, msg->body.motion.downTime, - msg->body.motion.eventTime, pointerCount, pointerProperties, pointerCoords); + displayTransform, msg->body.motion.downTime, msg->body.motion.eventTime, + pointerCount, pointerProperties, pointerCoords); } void InputConsumer::initializeTouchModeEvent(TouchModeEvent* event, const InputMessage* msg) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index b1ef7534e4..7e7dfd5416 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -226,11 +226,16 @@ protected: static constexpr float Y_SCALE = 3.0; static constexpr float X_OFFSET = 1; static constexpr float Y_OFFSET = 1.1; + static constexpr float RAW_X_SCALE = 4.0; + static constexpr float RAW_Y_SCALE = -5.0; + static constexpr float RAW_X_OFFSET = 12; + static constexpr float RAW_Y_OFFSET = -41.1; static const std::optional INITIAL_PER_WINDOW_INPUT_ROTATION_FLAG_VALUE; int32_t mId; ui::Transform mTransform; + ui::Transform mRawTransform; void SetUp() override; void TearDown() override; @@ -259,6 +264,7 @@ void MotionEventTest::TearDown() { void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { mId = InputEvent::nextId(); mTransform.set({X_SCALE, 0, X_OFFSET, 0, Y_SCALE, Y_OFFSET, 0, 0, 1}); + mRawTransform.set({RAW_X_SCALE, 0, RAW_X_OFFSET, 0, RAW_Y_SCALE, RAW_Y_OFFSET, 0, 0, 1}); PointerProperties pointerProperties[2]; pointerProperties[0].clear(); @@ -294,9 +300,8 @@ void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { AMOTION_EVENT_EDGE_FLAG_TOP, AMETA_ALT_ON, AMOTION_EVENT_BUTTON_PRIMARY, MotionClassification::NONE, mTransform, 2.0f, 2.1f, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, pointerProperties, - pointerCoords); + mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, + pointerProperties, pointerCoords); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 110); pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 111); @@ -373,39 +378,37 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(ARBITRARY_EVENT_TIME + 1, event->getHistoricalEventTime(1)); ASSERT_EQ(ARBITRARY_EVENT_TIME + 2, event->getEventTime()); - ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(211, event->getRawPointerCoords(0)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - ASSERT_EQ(221, event->getRawPointerCoords(1)-> - getAxisValue(AMOTION_EVENT_AXIS_Y)); - - ASSERT_EQ(11, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); - ASSERT_EQ(21, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); - ASSERT_EQ(111, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); - ASSERT_EQ(121, event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); - ASSERT_EQ(211, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); - ASSERT_EQ(221, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); - - ASSERT_EQ(10, event->getHistoricalRawX(0, 0)); - ASSERT_EQ(20, event->getHistoricalRawX(1, 0)); - ASSERT_EQ(110, event->getHistoricalRawX(0, 1)); - ASSERT_EQ(120, event->getHistoricalRawX(1, 1)); - ASSERT_EQ(210, event->getRawX(0)); - ASSERT_EQ(220, event->getRawX(1)); - - ASSERT_EQ(11, event->getHistoricalRawY(0, 0)); - ASSERT_EQ(21, event->getHistoricalRawY(1, 0)); - ASSERT_EQ(111, event->getHistoricalRawY(0, 1)); - ASSERT_EQ(121, event->getHistoricalRawY(1, 1)); - ASSERT_EQ(211, event->getRawY(0)); - ASSERT_EQ(221, event->getRawY(1)); + ASSERT_EQ(11, event->getHistoricalRawPointerCoords(0, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(21, event->getHistoricalRawPointerCoords(1, 0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(111, event->getHistoricalRawPointerCoords(0, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(121, event->getHistoricalRawPointerCoords(1, 1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(211, event->getRawPointerCoords(0)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + ASSERT_EQ(221, event->getRawPointerCoords(1)->getAxisValue(AMOTION_EVENT_AXIS_Y)); + + ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 0, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, + event->getHistoricalRawAxisValue(AMOTION_EVENT_AXIS_Y, 1, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawAxisValue(AMOTION_EVENT_AXIS_Y, 1)); + + ASSERT_EQ(RAW_X_OFFSET + 10 * RAW_X_SCALE, event->getHistoricalRawX(0, 0)); + ASSERT_EQ(RAW_X_OFFSET + 20 * RAW_X_SCALE, event->getHistoricalRawX(1, 0)); + ASSERT_EQ(RAW_X_OFFSET + 110 * RAW_X_SCALE, event->getHistoricalRawX(0, 1)); + ASSERT_EQ(RAW_X_OFFSET + 120 * RAW_X_SCALE, event->getHistoricalRawX(1, 1)); + ASSERT_EQ(RAW_X_OFFSET + 210 * RAW_X_SCALE, event->getRawX(0)); + ASSERT_EQ(RAW_X_OFFSET + 220 * RAW_X_SCALE, event->getRawX(1)); + + ASSERT_EQ(RAW_Y_OFFSET + 11 * RAW_Y_SCALE, event->getHistoricalRawY(0, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 21 * RAW_Y_SCALE, event->getHistoricalRawY(1, 0)); + ASSERT_EQ(RAW_Y_OFFSET + 111 * RAW_Y_SCALE, event->getHistoricalRawY(0, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 121 * RAW_Y_SCALE, event->getHistoricalRawY(1, 1)); + ASSERT_EQ(RAW_Y_OFFSET + 211 * RAW_Y_SCALE, event->getRawY(0)); + ASSERT_EQ(RAW_Y_OFFSET + 221 * RAW_Y_SCALE, event->getRawY(1)); ASSERT_EQ(X_OFFSET + 10 * X_SCALE, event->getHistoricalX(0, 0)); ASSERT_EQ(X_OFFSET + 20 * X_SCALE, event->getHistoricalX(1, 0)); @@ -543,8 +546,8 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(X_OFFSET * 2, event.getXOffset()); ASSERT_EQ(Y_OFFSET * 2, event.getYOffset()); - ASSERT_EQ(210 * 2, event.getRawX(0)); - ASSERT_EQ(211 * 2, event.getRawY(0)); + ASSERT_EQ((RAW_X_OFFSET + 210 * RAW_X_SCALE) * 2, event.getRawX(0)); + ASSERT_EQ((RAW_Y_OFFSET + 211 * RAW_Y_SCALE) * 2, event.getRawY(0)); ASSERT_EQ((X_OFFSET + 210 * X_SCALE) * 2, event.getX(0)); ASSERT_EQ((Y_OFFSET + 211 * Y_SCALE) * 2, event.getY(0)); ASSERT_EQ(212, event.getPressure(0)); @@ -592,10 +595,10 @@ TEST_F(MotionEventTest, Transform) { // The geometrical representation is irrelevant to the test, it's just easy to generate // and check rotation. We set the orientation to the same angle. // Coordinate system: down is increasing Y, right is increasing X. - const float PI_180 = float(M_PI / 180); - const float RADIUS = 10; - const float ARC = 36; - const float ROTATION = ARC * 2; + static constexpr float PI_180 = float(M_PI / 180); + static constexpr float RADIUS = 10; + static constexpr float ARC = 36; + static constexpr float ROTATION = ARC * 2; const size_t pointerCount = 11; PointerProperties pointerProperties[pointerCount]; @@ -616,9 +619,8 @@ TEST_F(MotionEventTest, Transform) { AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, 3 + RADIUS /*xCursorPosition*/, 2 /*yCursorPosition*/, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, - pointerCoords); + identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, + pointerProperties, pointerCoords); float originalRawX = 0 + 3; float originalRawY = -RADIUS + 2; @@ -661,7 +663,7 @@ TEST_F(MotionEventTest, Transform) { MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, const ui::Transform& transform, - uint32_t displayOrientation = ui::Transform::ROT_0) { + const ui::Transform& rawTransform) { std::vector pointerProperties; pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); std::vector pointerCoords; @@ -677,19 +679,18 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, transform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, displayOrientation, - /* displayWidth */ 400, - /* displayHeight */ 800, eventTime, eventTime, pointerCoords.size(), - pointerProperties.data(), pointerCoords.data()); + AMOTION_EVENT_INVALID_CURSOR_POSITION, rawTransform, eventTime, eventTime, + pointerCoords.size(), pointerProperties.data(), pointerCoords.data()); return event; } TEST_F(MotionEventTest, ApplyTransform) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform identity; - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + ui::Transform rawTransform(ui::Transform::ROT_90, 800, 400); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform); ASSERT_EQ(700, event.getRawX(0)); ASSERT_EQ(60, event.getRawY(0)); ASSERT_NE(event.getRawX(0), event.getX(0)); @@ -698,10 +699,10 @@ TEST_F(MotionEventTest, ApplyTransform) { ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity); - const std::array rowMajor{xform[0][0], xform[1][0], xform[2][0], - xform[0][1], xform[1][1], xform[2][1], - xform[0][2], xform[1][2], xform[2][2]}; + MotionEvent changedEvent = createTouchDownEvent(60, 100, 42, 96, identity, identity); + const std::array rowMajor{transform[0][0], transform[1][0], transform[2][0], + transform[0][1], transform[1][1], transform[2][1], + transform[0][2], transform[1][2], transform[2][2]}; changedEvent.applyTransform(rowMajor); // transformContent effectively rotates the raw coordinates, so those should now include @@ -727,9 +728,9 @@ TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { AINPUT_SOURCE_JOYSTICK}; for (uint32_t source : NON_POINTER_SOURCES) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform); event.setSource(source); // Since this event comes from a non-pointer source, it should include rotation but not @@ -741,72 +742,34 @@ TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { } } -TEST_F(MotionEventTest, RawCompatTransform) { - { - // Make sure raw is raw regardless of transform translation. - ui::Transform xform; - xform.set(20, 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform); - ASSERT_EQ(60, event.getRawX(0)); - ASSERT_EQ(100, event.getRawY(0)); - ASSERT_NE(event.getRawX(0), event.getX(0)); - ASSERT_NE(event.getRawY(0), event.getY(0)); - // Relative values should not be modified. - ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } +TEST_F(MotionEventTest, AxesAreCorrectlyTransformed) { + const ui::Transform identity; + ui::Transform transform; + transform.set({1.1, -2.2, 3.3, -4.4, 5.5, -6.6, 0, 0, 1}); + ui::Transform rawTransform; + rawTransform.set({-6.6, 5.5, -4.4, 3.3, -2.2, 1.1, 0, 0, 1}); + auto transformWithoutTranslation = [](const ui::Transform& t, float x, float y) { + auto newPoint = t.transform(x, y); + auto newOrigin = t.transform(0, 0); + return newPoint - newOrigin; + }; - // Next check that getRaw contains rotation (for compatibility) but otherwise is still - // "Screen-space". The following tests check all 3 rotations. - { - // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). - ui::Transform xform(ui::Transform::ROT_90, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); - ASSERT_EQ(700, event.getRawX(0)); - ASSERT_EQ(60, event.getRawY(0)); - ASSERT_NE(event.getRawX(0), event.getX(0)); - ASSERT_NE(event.getRawY(0), event.getY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + const MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, rawTransform); - { - // Same as above, but check rotate-180. - ui::Transform xform(ui::Transform::ROT_180, 400, 800); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_180); - ASSERT_EQ(340, event.getRawX(0)); - ASSERT_EQ(700, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // The x and y axes should have the window transform applied. + const auto newPoint = transform.transform(60, 100); + ASSERT_EQ(newPoint.x, event.getX(0)); + ASSERT_EQ(newPoint.y, event.getY(0)); - { - // Same as above, but check rotate-270. - ui::Transform xform(ui::Transform::ROT_270, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_270); - ASSERT_EQ(100, event.getRawX(0)); - ASSERT_EQ(340, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // The raw values should have the display transform applied. + const auto raw = rawTransform.transform(60, 100); + ASSERT_EQ(raw.x, event.getRawX(0)); + ASSERT_EQ(raw.y, event.getRawY(0)); - { - // Finally, check that raw isn't effected by transform - ui::Transform xform(ui::Transform::ROT_270, 800, 400); - xform.set(xform.tx() + 20, xform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, xform, ui::Transform::ROT_90); - ASSERT_EQ(700, event.getRawX(0)); - ASSERT_EQ(60, event.getRawY(0)); - // Relative values should be rotated but not translated. - ASSERT_EQ(96, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); - ASSERT_EQ(-42, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); - } + // Relative values should have the window transform applied without any translation. + const auto rel = transformWithoutTranslation(transform, 42, 96); + ASSERT_EQ(rel.x, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0)); + ASSERT_EQ(rel.y, event.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0)); } TEST_F(MotionEventTest, Initialize_SetsClassification) { @@ -832,8 +795,7 @@ TEST_F(MotionEventTest, Initialize_SetsClassification) { DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(classification, event.getClassification()); } @@ -854,9 +816,9 @@ TEST_F(MotionEventTest, Initialize_SetsCursorPosition) { event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, 0 /*eventTime*/, - pointerCount, pointerProperties, pointerCoords); + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identityTransform, + 0 /*downTime*/, 0 /*eventTime*/, pointerCount, pointerProperties, + pointerCoords); event.offsetLocation(20, 60); ASSERT_EQ(280, event.getRawXCursorPosition()); ASSERT_EQ(540, event.getRawYCursorPosition()); diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 8db5bf1289..d09f2ac748 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -160,13 +160,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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; - constexpr uint32_t displayOrientation = ui::Transform::ROT_0; - constexpr int32_t displayWidth = 1000; - constexpr int32_t displayHeight = 2000; constexpr nsecs_t downTime = 3; constexpr size_t pointerCount = 3; constexpr nsecs_t eventTime = 4; @@ -192,12 +193,14 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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 = mPublisher->publishMotionEvent(seq, eventId, deviceId, source, displayId, hmac, action, actionButton, flags, edgeFlags, metaState, buttonState, classification, transform, xPrecision, yPrecision, - xCursorPosition, yCursorPosition, displayOrientation, - displayWidth, displayHeight, downTime, eventTime, - pointerCount, pointerProperties, pointerCoords); + xCursorPosition, yCursorPosition, rawTransform, + downTime, eventTime, pointerCount, pointerProperties, + pointerCoords); ASSERT_EQ(OK, status) << "publisher publishMotionEvent should return OK"; @@ -234,9 +237,7 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(yCursorPosition, motionEvent->getRawYCursorPosition()); EXPECT_EQ(xCursorPosition * xScale + xOffset, motionEvent->getXCursorPosition()); EXPECT_EQ(yCursorPosition * yScale + yOffset, motionEvent->getYCursorPosition()); - EXPECT_EQ(displayOrientation, motionEvent->getDisplayOrientation()); - EXPECT_EQ(displayWidth, motionEvent->getDisplaySize().x); - EXPECT_EQ(displayHeight, motionEvent->getDisplaySize().y); + EXPECT_EQ(rawTransform, motionEvent->getRawTransform()); EXPECT_EQ(downTime, motionEvent->getDownTime()); EXPECT_EQ(eventTime, motionEvent->getEventTime()); EXPECT_EQ(pointerCount, motionEvent->getPointerCount()); @@ -247,28 +248,18 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { EXPECT_EQ(pointerProperties[i].id, motionEvent->getPointerId(i)); EXPECT_EQ(pointerProperties[i].toolType, motionEvent->getToolType(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X), - motionEvent->getRawX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y), - motionEvent->getRawY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X) * xScale + xOffset, - motionEvent->getX(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y) * yScale + yOffset, - motionEvent->getY(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE), - motionEvent->getPressure(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE), - motionEvent->getSize(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR), - motionEvent->getTouchMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR), - motionEvent->getTouchMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR), - motionEvent->getToolMajor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR), - motionEvent->getToolMinor(i)); - EXPECT_EQ(pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), - motionEvent->getOrientation(i)); + const auto& pc = pointerCoords[i]; + EXPECT_EQ(pc.getX() * rawXScale + rawXOffset, motionEvent->getRawX(i)); + EXPECT_EQ(pc.getY() * rawYScale + rawYOffset, motionEvent->getRawY(i)); + EXPECT_EQ(pc.getX() * xScale + xOffset, motionEvent->getX(i)); + EXPECT_EQ(pc.getY() * yScale + yOffset, motionEvent->getY(i)); + 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)); + EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(i)); } status = mConsumer->sendFinishedSignal(seq, false); @@ -505,12 +496,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZer } ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(0, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -522,12 +513,12 @@ TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenPointerCountLessTha PointerCoords pointerCoords[pointerCount]; ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } @@ -544,12 +535,12 @@ TEST_F(InputPublisherAndConsumerTest, } ui::Transform identityTransform; - status = mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, - 0, 0, 0, MotionClassification::NONE, identityTransform, - 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, 0, 0, 0, 0, pointerCount, - pointerProperties, pointerCoords); + status = + mPublisher->publishMotionEvent(1, InputEvent::nextId(), 0, 0, 0, INVALID_HMAC, 0, 0, 0, + 0, 0, 0, MotionClassification::NONE, identityTransform, + 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, + 0, 0, pointerCount, pointerProperties, pointerCoords); ASSERT_EQ(BAD_VALUE, status) << "publisher publishMotionEvent should return BAD_VALUE"; } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 18289a5f11..2f88704d63 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -49,7 +49,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Key, downTime, 88); CHECK_OFFSET(InputMessage::Body::Motion, eventId, 0); - CHECK_OFFSET(InputMessage::Body::Motion, empty1, 4); + CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 4); CHECK_OFFSET(InputMessage::Body::Motion, eventTime, 8); CHECK_OFFSET(InputMessage::Body::Motion, deviceId, 16); CHECK_OFFSET(InputMessage::Body::Motion, source, 20); @@ -74,11 +74,13 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 124); CHECK_OFFSET(InputMessage::Body::Motion, xCursorPosition, 128); CHECK_OFFSET(InputMessage::Body::Motion, yCursorPosition, 132); - CHECK_OFFSET(InputMessage::Body::Motion, displayOrientation, 136); - CHECK_OFFSET(InputMessage::Body::Motion, displayWidth, 140); - CHECK_OFFSET(InputMessage::Body::Motion, displayHeight, 144); - CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 148); - CHECK_OFFSET(InputMessage::Body::Motion, pointers, 152); + CHECK_OFFSET(InputMessage::Body::Motion, dsdxRaw, 136); + CHECK_OFFSET(InputMessage::Body::Motion, dtdxRaw, 140); + CHECK_OFFSET(InputMessage::Body::Motion, dtdyRaw, 144); + CHECK_OFFSET(InputMessage::Body::Motion, dsdyRaw, 148); + CHECK_OFFSET(InputMessage::Body::Motion, txRaw, 152); + CHECK_OFFSET(InputMessage::Body::Motion, tyRaw, 156); + CHECK_OFFSET(InputMessage::Body::Motion, pointers, 160); CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 13e2b02ca4..3039362c2b 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -184,8 +184,7 @@ static std::vector createMotionEventStream( AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, identityTransform, 0 /*xPrecision*/, 0 /*yPrecision*/, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 0 /*downTime*/, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, 0 /*downTime*/, entry.eventTime.count(), pointerCount, properties, coords); events.emplace_back(event); diff --git a/libs/input/tests/VerifiedInputEvent_test.cpp b/libs/input/tests/VerifiedInputEvent_test.cpp index b29c9a4877..f2b59ea9ab 100644 --- a/libs/input/tests/VerifiedInputEvent_test.cpp +++ b/libs/input/tests/VerifiedInputEvent_test.cpp @@ -43,12 +43,12 @@ static MotionEvent getMotionEventWithFlags(int32_t flags) { ui::Transform transform; transform.set({2, 0, 4, 0, 3, 5, 0, 0, 1}); + ui::Transform identity; event.initialize(InputEvent::nextId(), 0 /*deviceId*/, AINPUT_SOURCE_MOUSE, ADISPLAY_ID_DEFAULT, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0 /*actionButton*/, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE, transform, 0.1 /*xPrecision*/, 0.2 /*yPrecision*/, - 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, 100 /*downTime*/, + 280 /*xCursorPosition*/, 540 /*yCursorPosition*/, identity, 100 /*downTime*/, 200 /*eventTime*/, pointerCount, pointerProperties, pointerCoords); return event; } diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp index 6ce0313929..68d25f9ec7 100644 --- a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp +++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp @@ -236,8 +236,8 @@ static MotionEvent generateMotionEvent() { /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, currentTime, currentTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, currentTime, + currentTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); return event; } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 571c126658..bcb00719cd 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -309,15 +309,14 @@ std::string SensorEntry::getDescription() const { volatile int32_t DispatchEntry::sNextSeqAtomic; DispatchEntry::DispatchEntry(std::shared_ptr eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, - uint32_t displayOrientation, int2 displaySize) + const ui::Transform& transform, const ui::Transform& rawTransform, + float globalScaleFactor) : seq(nextSeq()), eventEntry(std::move(eventEntry)), targetFlags(targetFlags), transform(transform), + rawTransform(rawTransform), globalScaleFactor(globalScaleFactor), - displayOrientation(displayOrientation), - displaySize(displaySize), deliveryTime(0), resolvedAction(0), resolvedFlags(0) {} diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 5365a78b0a..7a121ce798 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -226,9 +226,8 @@ struct DispatchEntry { std::shared_ptr eventEntry; // the event to dispatch int32_t targetFlags; ui::Transform transform; + ui::Transform rawTransform; float globalScaleFactor; - uint32_t displayOrientation; - int2 displaySize; // Both deliveryTime and timeoutTime are only populated when the entry is sent to the app, // and will be undefined before that. nsecs_t deliveryTime; // time when the event was actually delivered @@ -241,8 +240,8 @@ struct DispatchEntry { int32_t resolvedFlags; DispatchEntry(std::shared_ptr eventEntry, int32_t targetFlags, - ui::Transform transform, float globalScaleFactor, uint32_t displayOrientation, - int2 displaySize); + const ui::Transform& transform, const ui::Transform& rawTransform, + float globalScaleFactor); inline bool hasForegroundTarget() const { return targetFlags & InputTarget::FLAG_FOREGROUND; } diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index da3e237e3e..22d2e9a97e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -346,18 +346,15 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge // Use identity transform for joystick and position-based (touchpad) events because they // don't depend on the window transform. return std::make_unique(eventEntry, inputTargetFlags, identityTransform, - 1.0f /*globalScaleFactor*/, - inputTarget.displayOrientation, - inputTarget.displaySize); + identityTransform, 1.0f /*globalScaleFactor*/); } } if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique(eventEntry, inputTargetFlags, transform, - inputTarget.globalScaleFactor, - inputTarget.displayOrientation, - inputTarget.displaySize); + inputTarget.displayTransform, + inputTarget.globalScaleFactor); } ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION); @@ -408,9 +405,8 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge std::unique_ptr dispatchEntry = std::make_unique(std::move(combinedMotionEntry), inputTargetFlags, - firstPointerTransform, inputTarget.globalScaleFactor, - inputTarget.displayOrientation, - inputTarget.displaySize); + firstPointerTransform, inputTarget.displayTransform, + inputTarget.globalScaleFactor); return dispatchEntry; } @@ -2347,7 +2343,7 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) { addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset, - touchedMonitor.yOffset, inputTargets); + touchedMonitor.yOffset, displayId, inputTargets); } // Drop the outside or hover touch windows since we will not care about them @@ -2526,9 +2522,7 @@ void InputDispatcher::addWindowTargetLocked(const sp& windowHa inputTarget.globalScaleFactor = windowInfo->globalScaleFactor; const auto& displayInfoIt = mDisplayInfos.find(windowInfo->displayId); if (displayInfoIt != mDisplayInfos.end()) { - const auto& displayInfo = displayInfoIt->second; - inputTarget.displayOrientation = displayInfo.transform.getOrientation(); - inputTarget.displaySize = int2(displayInfo.logicalWidth, displayInfo.logicalHeight); + inputTarget.displayTransform = displayInfoIt->second.transform; } else { ALOGI_IF(isPerWindowInputRotationEnabled(), "DisplayInfo not found for window on display: %d", windowInfo->displayId); @@ -2552,19 +2546,38 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector& if (it != mGlobalMonitorsByDisplay.end()) { const std::vector& monitors = it->second; for (const Monitor& monitor : monitors) { - addMonitoringTargetLocked(monitor, xOffset, yOffset, inputTargets); + addMonitoringTargetLocked(monitor, xOffset, yOffset, displayId, inputTargets); } } } void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xOffset, - float yOffset, + float yOffset, int32_t displayId, std::vector& inputTargets) { InputTarget target; target.inputChannel = monitor.inputChannel; target.flags = InputTarget::FLAG_DISPATCH_AS_IS; - ui::Transform t; - t.set(xOffset, yOffset); + ui::Transform t = ui::Transform(xOffset, yOffset); + if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) { + // Input monitors always get un-rotated display coordinates. We undo the display + // rotation that is present in the display transform so that display rotation is not + // applied to these input targets. + const auto& displayInfo = it->second; + int32_t width = displayInfo.logicalWidth; + int32_t height = displayInfo.logicalHeight; + const auto orientation = displayInfo.transform.getOrientation(); + uint32_t inverseOrientation = orientation; + if (orientation == ui::Transform::ROT_90) { + inverseOrientation = ui::Transform::ROT_270; + std::swap(width, height); + } else if (orientation == ui::Transform::ROT_270) { + inverseOrientation = ui::Transform::ROT_90; + std::swap(width, height); + } + target.displayTransform = + ui::Transform(inverseOrientation, width, height) * displayInfo.transform; + t = t * target.displayTransform; + } target.setDefaultPointerTransform(t); inputTargets.push_back(target); } @@ -3250,9 +3263,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, motionEntry.xPrecision, motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, - dispatchEntry->displayOrientation, - dispatchEntry->displaySize.x, - dispatchEntry->displaySize.y, + dispatchEntry->rawTransform, motionEntry.downTime, motionEntry.eventTime, motionEntry.pointerCount, motionEntry.pointerProperties, usingCoords); @@ -3981,14 +3992,14 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { mLock.unlock(); MotionEvent event; - ui::Transform transform; + ui::Transform identityTransform; event.initialize(args->id, args->deviceId, args->source, args->displayId, INVALID_HMAC, args->action, args->actionButton, args->flags, args->edgeFlags, - args->metaState, args->buttonState, args->classification, transform, - args->xPrecision, args->yPrecision, args->xCursorPosition, - args->yCursorPosition, ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, - INVALID_DISPLAY_SIZE, args->downTime, args->eventTime, - args->pointerCount, args->pointerProperties, args->pointerCoords); + args->metaState, args->buttonState, args->classification, + identityTransform, args->xPrecision, args->yPrecision, + args->xCursorPosition, args->yCursorPosition, identityTransform, + args->downTime, args->eventTime, args->pointerCount, + args->pointerProperties, args->pointerCoords); policyFlags |= POLICY_FLAG_FILTERED; if (!mPolicy->filterInputEvent(&event, policyFlags)) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index 4f6d0d2759..e9fe2810d1 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -525,7 +525,8 @@ private: int32_t targetFlags, BitSet32 pointerIds, std::vector& inputTargets) REQUIRES(mLock); void addMonitoringTargetLocked(const Monitor& monitor, float xOffset, float yOffset, - std::vector& inputTargets) REQUIRES(mLock); + int32_t displayId, std::vector& inputTargets) + REQUIRES(mLock); void addGlobalMonitoringTargetsLocked(std::vector& inputTargets, int32_t displayId, float xOffset = 0, float yOffset = 0) REQUIRES(mLock); void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock); diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h index 7c463c8697..5b76eee343 100644 --- a/services/inputflinger/dispatcher/InputTarget.h +++ b/services/inputflinger/dispatcher/InputTarget.h @@ -101,11 +101,8 @@ struct InputTarget { // (ignored for KeyEvents) float globalScaleFactor = 1.0f; - // Current display orientation - uint32_t displayOrientation = ui::Transform::ROT_0; - - // Display-size in its natural rotation. Used for compatibility transform of raw coordinates. - int2 displaySize = {INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE}; + // Current display transform. Used for compatibility for raw coordinates. + ui::Transform displayTransform; // The subset of pointer ids to include in motion events dispatched to this input target // if FLAG_SPLIT is set. diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 903f337eb9..e9d45b22ad 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -542,8 +542,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, /*action*/ -1, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -556,8 +556,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -569,8 +568,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -583,8 +581,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -596,8 +593,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { (~0U << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT), 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION, - ui::Transform::ROT_0, INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, - ARBITRARY_TIME, ARBITRARY_TIME, + identityTransform, ARBITRARY_TIME, ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -608,8 +604,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 0, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -619,8 +615,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ MAX_POINTERS + 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -632,8 +628,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -644,8 +640,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 1, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -658,8 +654,8 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { event.initialize(InputEvent::nextId(), DEVICE_ID, source, DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, edgeFlags, metaState, 0, classification, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - INVALID_DISPLAY_SIZE, INVALID_DISPLAY_SIZE, ARBITRARY_TIME, ARBITRARY_TIME, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, ARBITRARY_TIME, + ARBITRARY_TIME, /*pointerCount*/ 2, pointerProperties, pointerCoords); ASSERT_EQ(InputEventInjectionResult::FAILED, mDispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID, @@ -1296,9 +1292,8 @@ public: mAction, mActionButton, mFlags, /* edgeFlags */ 0, AMETA_NONE, mButtonState, MotionClassification::NONE, identityTransform, /* xPrecision */ 0, /* yPrecision */ 0, mRawXCursorPosition, - mRawYCursorPosition, mDisplayOrientation, mDisplayWidth, mDisplayHeight, - mEventTime, mEventTime, mPointers.size(), pointerProperties.data(), - pointerCoords.data()); + mRawYCursorPosition, identityTransform, mEventTime, mEventTime, + mPointers.size(), pointerProperties.data(), pointerCoords.data()); return event; } @@ -1313,9 +1308,6 @@ private: int32_t mFlags{0}; float mRawXCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; float mRawYCursorPosition{AMOTION_EVENT_INVALID_CURSOR_POSITION}; - uint32_t mDisplayOrientation{ui::Transform::ROT_0}; - int32_t mDisplayWidth{INVALID_DISPLAY_SIZE}; - int32_t mDisplayHeight{INVALID_DISPLAY_SIZE}; std::vector mPointers; }; @@ -3622,8 +3614,7 @@ protected: DISPLAY_ID, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, 0, 0, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0, MotionClassification::NONE, identityTransform, 0, 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, ui::Transform::ROT_0, - 0 /*INVALID_DISPLAY_SIZE*/, 0 /*INVALID_DISPLAY_SIZE*/, eventTime, + AMOTION_EVENT_INVALID_CURSOR_POSITION, identityTransform, eventTime, eventTime, /*pointerCount*/ 1, pointerProperties, pointerCoords); -- cgit v1.2.3-59-g8ed1b From b5cb957254201f19a434898c249d59752e2a88b7 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 24 Sep 2021 06:35:16 -0700 Subject: VerifiedMotionEvent: Sign transformed raw values The values for axes X/Y that are stored in MotionEvent are transformed to values used for its getRaw API based on the source. This is because non-pointer sources should not have translation applied to them. We need to ensure that we use the same raw coordinates when we sign a VerifiedMotionEvent in InputDispatcher that we would get with the MotionEvent#getRaw API. To do this, we re-use the same logic used to transform the raw coordinates in MotionEvent in InputDispatcher. Bug: 179274888 Test: atest inputflinger_tests Test: atest VerifyInputEventTest Change-Id: I552f94064f15573ddda8f7c0b588cd3b984b6a94 --- include/input/Input.h | 2 ++ libs/input/Input.cpp | 29 +++++++++++----------- services/inputflinger/dispatcher/Entry.cpp | 11 ++++---- services/inputflinger/dispatcher/Entry.h | 3 ++- .../inputflinger/dispatcher/InputDispatcher.cpp | 16 ++++++------ .../inputflinger/tests/InputDispatcher_test.cpp | 16 +++++++++--- 6 files changed, 46 insertions(+), 31 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index d397313ab6..5015e68121 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -801,6 +801,8 @@ public: static std::string actionToString(int32_t action); + static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); + protected: int32_t mAction; int32_t mActionButton; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index a1542c8793..1bc244c653 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -65,8 +65,8 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return result; } -vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) { - const vec2 transformedXy = transform.transform(x, y); +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { + const vec2 transformedXy = transform.transform(xy); const vec2 transformedOrigin = transform.transform(0, 0); return transformedXy - transformedOrigin; } @@ -501,21 +501,16 @@ float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - // For compatibility, convert raw coordinates into logical display space. - const vec2 xy = shouldDisregardTranslation(mSource) - ? transformWithoutTranslation(mRawTransform, coords->getX(), coords->getY()) - : mRawTransform.transform(coords->getX(), coords->getY()); + const vec2 xy = calculateTransformedXY(mSource, mRawTransform, coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { - // For compatibility, since we report raw coordinates in logical display space, we - // need to convert the relative axes into the same orientation for consistency. const vec2 relativeXy = transformWithoutTranslation(mRawTransform, - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; } @@ -527,9 +522,7 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = shouldDisregardTranslation(mSource) - ? transformWithoutTranslation(mTransform, coords->getX(), coords->getY()) - : mTransform.transform(coords->getXYValue()); + const vec2 xy = calculateTransformedXY(mSource, mTransform, coords->getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } @@ -537,8 +530,8 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { const vec2 relativeXy = transformWithoutTranslation(mTransform, - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); + {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; } @@ -815,6 +808,12 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, + const vec2& xy) { + return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy) + : transform.transform(xy); +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index bcb00719cd..c03581d8da 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -40,14 +40,15 @@ VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { entry.repeatCount}; } -VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry) { - const float rawX = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X); - const float rawY = entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y); +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, + const ui::Transform& rawTransform) { + const vec2 rawXY = MotionEvent::calculateTransformedXY(entry.source, rawTransform, + entry.pointerCoords[0].getXYValue()); const int actionMasked = entry.action & AMOTION_EVENT_ACTION_MASK; return {{VerifiedInputEvent::Type::MOTION, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, - rawX, - rawY, + rawXY.x, + rawXY.y, actionMasked, entry.downTime, entry.flags & VERIFIED_MOTION_EVENT_FLAGS, diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 7a121ce798..477781a2ad 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -254,7 +254,8 @@ private: }; VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry); -VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry); +VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, + const ui::Transform& rawTransform); } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 695dd17d38..52a88d7f62 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3360,16 +3360,18 @@ std::array InputDispatcher::sign(const VerifiedInputEvent& event) c const std::array InputDispatcher::getSignature( const MotionEntry& motionEntry, const DispatchEntry& dispatchEntry) const { - int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; - if ((actionMasked == AMOTION_EVENT_ACTION_UP) || (actionMasked == AMOTION_EVENT_ACTION_DOWN)) { + const int32_t actionMasked = dispatchEntry.resolvedAction & AMOTION_EVENT_ACTION_MASK; + if (actionMasked != AMOTION_EVENT_ACTION_UP && actionMasked != AMOTION_EVENT_ACTION_DOWN) { // Only sign events up and down events as the purely move events // are tied to their up/down counterparts so signing would be redundant. - VerifiedMotionEvent verifiedEvent = verifiedMotionEventFromMotionEntry(motionEntry); - verifiedEvent.actionMasked = actionMasked; - verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; - return sign(verifiedEvent); + return INVALID_HMAC; } - return INVALID_HMAC; + + VerifiedMotionEvent verifiedEvent = + verifiedMotionEventFromMotionEntry(motionEntry, dispatchEntry.rawTransform); + verifiedEvent.actionMasked = actionMasked; + verifiedEvent.flags = dispatchEntry.resolvedFlags & VERIFIED_MOTION_EVENT_FLAGS; + return sign(verifiedEvent); } const std::array InputDispatcher::getSignature( diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 42d3e58545..ca77d7ac0a 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -2801,7 +2801,14 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); + ui::Transform transform; + transform.set({1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 0, 0, 1}); + + gui::DisplayInfo displayInfo; + displayInfo.displayId = ADISPLAY_ID_DEFAULT; + displayInfo.transform = transform; + + mDispatcher->onWindowInfosChanged({*window->getInfo()}, {displayInfo}); NotifyMotionArgs motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN, @@ -2822,8 +2829,11 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { const VerifiedMotionEvent& verifiedMotion = static_cast(*verified); - EXPECT_EQ(motionArgs.pointerCoords[0].getX(), verifiedMotion.rawX); - EXPECT_EQ(motionArgs.pointerCoords[0].getY(), verifiedMotion.rawY); + const vec2 rawXY = + MotionEvent::calculateTransformedXY(motionArgs.source, transform, + motionArgs.pointerCoords[0].getXYValue()); + EXPECT_EQ(rawXY.x, verifiedMotion.rawX); + EXPECT_EQ(rawXY.y, verifiedMotion.rawY); EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked); EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags); -- cgit v1.2.3-59-g8ed1b From 9eb02c0ae27754f7230b4cfbd1d34bd837223c6f Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 19 Oct 2021 14:02:20 -0700 Subject: MotionEvent: Report transformed orientation values Previously, the AXIS_ORIENTATION value reported by MotionEvent was unaffected by the window transform. This meant that whatever value that was generated by InputReader was the value reported to the window. This meant that if the window was rotated or scaled respective to the logical display and the window received an event, it could be the case that the 0 value no longer corresponds with the "up" direction of the window. Now that the input pipeline works in the display panel's coordinate system, we need the orientation value reported by MotionEvent to be affected by the window transform. This CL also refactors the shared logic by which transformed axis values are calculated into a common function. Bug: 179274888 Test: atest libinput_tests Test: atest inputflinger_tests Test: manual with test app and stylus Change-Id: Ibb6f135de47f7c1cbde3c023931a760dfbe08e45 --- include/input/Input.h | 3 + libs/input/Input.cpp | 71 +++++++++------------- libs/input/tests/InputEvent_test.cpp | 22 ++++--- .../input/tests/InputPublisherAndConsumer_test.cpp | 8 ++- 4 files changed, 53 insertions(+), 51 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 5015e68121..54c71140eb 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -803,6 +803,9 @@ public: static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); + static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&, + const PointerCoords&); + protected: int32_t mAction; int32_t mActionButton; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index b4d9ab623c..d018800c90 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -493,44 +493,14 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - - if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = calculateTransformedXY(mSource, mRawTransform, coords->getXYValue()); - static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); - return xy[axis]; - } - - if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { - const vec2 relativeXy = - transformWithoutTranslation(mRawTransform, - {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); - return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; - } - - return coords->getAxisValue(axis); + const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); + return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { - const PointerCoords* coords = getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - - if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = calculateTransformedXY(mSource, mTransform, coords->getXYValue()); - static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); - return xy[axis]; - } - - if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { - const vec2 relativeXy = - transformWithoutTranslation(mTransform, - {coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - coords->getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); - return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; - } - - return coords->getAxisValue(axis); + const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); + return calculateTransformedAxisValue(axis, mSource, mTransform, coords); } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -569,15 +539,6 @@ void MotionEvent::transform(const std::array& matrix) { ui::Transform newTransform; newTransform.set(matrix); mTransform = newTransform * mTransform; - - // We need to update the AXIS_ORIENTATION value here to maintain the old behavior where the - // orientation angle is not affected by the initial transformation set in the MotionEvent. - std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), - [&newTransform](PointerCoords& c) { - float orientation = c.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - c.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(newTransform, orientation)); - }); } void MotionEvent::applyTransform(const std::array& matrix) { @@ -809,6 +770,30 @@ vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& t : transform.transform(xy); } +float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, + const ui::Transform& transform, + const PointerCoords& coords) { + if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { + const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue()); + static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); + return xy[axis]; + } + + if (axis == AMOTION_EVENT_AXIS_RELATIVE_X || axis == AMOTION_EVENT_AXIS_RELATIVE_Y) { + const vec2 relativeXy = + transformWithoutTranslation(transform, + {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); + return axis == AMOTION_EVENT_AXIS_RELATIVE_X ? relativeXy.x : relativeXy.y; + } + + if (axis == AMOTION_EVENT_AXIS_ORIENTATION) { + return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + } + + return coords.getAxisValue(axis); +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index caf3a611e2..1b594f142e 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -444,12 +444,19 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(217, event->getToolMinor(0)); ASSERT_EQ(227, event->getToolMinor(1)); - ASSERT_EQ(18, event->getHistoricalOrientation(0, 0)); - ASSERT_EQ(28, event->getHistoricalOrientation(1, 0)); - ASSERT_EQ(118, event->getHistoricalOrientation(0, 1)); - ASSERT_EQ(128, event->getHistoricalOrientation(1, 1)); - ASSERT_EQ(218, event->getOrientation(0)); - ASSERT_EQ(228, event->getOrientation(1)); + // Calculate the orientation after scaling, keeping in mind that an orientation of 0 is "up", + // and the positive y direction is "down". + auto toScaledOrientation = [](float angle) { + const float x = sinf(angle) * X_SCALE; + const float y = -cosf(angle) * Y_SCALE; + return atan2f(x, -y); + }; + ASSERT_EQ(toScaledOrientation(18), event->getHistoricalOrientation(0, 0)); + ASSERT_EQ(toScaledOrientation(28), event->getHistoricalOrientation(1, 0)); + ASSERT_EQ(toScaledOrientation(118), event->getHistoricalOrientation(0, 1)); + ASSERT_EQ(toScaledOrientation(128), event->getHistoricalOrientation(1, 1)); + ASSERT_EQ(toScaledOrientation(218), event->getOrientation(0)); + ASSERT_EQ(toScaledOrientation(228), event->getOrientation(1)); } TEST_F(MotionEventTest, Properties) { @@ -518,6 +525,7 @@ TEST_F(MotionEventTest, OffsetLocation) { TEST_F(MotionEventTest, Scale) { MotionEvent event; initializeEventWithHistory(&event); + const float unscaledOrientation = event.getOrientation(0); event.scale(2.0f); @@ -534,7 +542,7 @@ TEST_F(MotionEventTest, Scale) { ASSERT_EQ(215 * 2, event.getTouchMinor(0)); ASSERT_EQ(216 * 2, event.getToolMajor(0)); ASSERT_EQ(217 * 2, event.getToolMinor(0)); - ASSERT_EQ(218, event.getOrientation(0)); + ASSERT_EQ(unscaledOrientation, event.getOrientation(0)); } TEST_F(MotionEventTest, Parcel) { diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index d09f2ac748..973194c8f6 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -259,7 +259,13 @@ void InputPublisherAndConsumerTest::PublishAndConsumeMotionEvent() { 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)); - EXPECT_EQ(pc.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), motionEvent->getOrientation(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); -- cgit v1.2.3-59-g8ed1b From 7e1ee565b3fe4738e6771bceb2e9679562232992 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Tue, 26 Oct 2021 10:19:49 -0700 Subject: Move Joystick and Touchpad event transformation logic to MotionEvent We would like to have all source and axis based transformations calculated in the same place, which will be in the MotionEvent class. This has the added benefit that MotionEvents created by developers through MotionEvent.obtain() in Java will behave like motion events generated from input devices in terms of how they're affected by transformations. Since axes from joysticks and touchpads should never be transfomred, we previously solved this by using identity matrices for them in InputDispatcher. Now, we move that logic to MotionEvent when applying the transform. Bug: 179274888 Test: atest libinput_test Test: atest inputflinger_tests Change-Id: Ic02466e01f2ba3131aca73bd10933ff81cb38cc9 --- include/input/Input.h | 5 +- libs/input/Input.cpp | 36 +++++++++++--- libs/input/tests/InputEvent_test.cpp | 57 ++++++++++++++++------ .../inputflinger/dispatcher/InputDispatcher.cpp | 12 ----- .../inputflinger/tests/InputDispatcher_test.cpp | 53 -------------------- 5 files changed, 77 insertions(+), 86 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 54c71140eb..7cc595a264 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -801,8 +801,11 @@ public: static std::string actionToString(int32_t action); + // MotionEvent will transform various axes in different ways, based on the source. For + // example, the x and y axes will not have any offsets/translations applied if it comes from a + // relative mouse device (since SOURCE_RELATIVE_MOUSE is a non-pointer source). These methods + // are used to apply these transformations for different axes. static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); - static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&, const PointerCoords&); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index d018800c90..24a77209c4 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -66,11 +66,21 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) return transformedXy - transformedOrigin; } -bool shouldDisregardTranslation(uint32_t source) { +bool isFromSource(uint32_t source, uint32_t test) { + return (source & test) == test; +} + +bool shouldDisregardTransformation(uint32_t source) { + // Do not apply any transformations to axes from joysticks or touchpads. + return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || + isFromSource(source, AINPUT_SOURCE_CLASS_POSITION); +} + +bool shouldDisregardOffset(uint32_t source) { // Pointer events are the only type of events that refer to absolute coordinates on the display, // so we should apply the entire window transform. For other types of events, we should make // sure to not apply the window translation/offset. - return (source & AINPUT_SOURCE_CLASS_POINTER) == 0; + return !isFromSource(source, AINPUT_SOURCE_CLASS_POINTER); } } // namespace @@ -707,7 +717,7 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { #endif bool MotionEvent::isTouchEvent(uint32_t source, int32_t action) { - if (source & AINPUT_SOURCE_CLASS_POINTER) { + if (isFromSource(source, AINPUT_SOURCE_CLASS_POINTER)) { // Specifically excludes HOVER_MOVE and SCROLL. switch (action & AMOTION_EVENT_ACTION_MASK) { case AMOTION_EVENT_ACTION_DOWN: @@ -764,17 +774,31 @@ std::string MotionEvent::actionToString(int32_t action) { return android::base::StringPrintf("%" PRId32, action); } +// Apply the given transformation to the point without checking whether the entire transform +// should be disregarded altogether for the provided source. +static inline vec2 calculateTransformedXYUnchecked(uint32_t source, const ui::Transform& transform, + const vec2& xy) { + return shouldDisregardOffset(source) ? transformWithoutTranslation(transform, xy) + : transform.transform(xy); +} + vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& transform, const vec2& xy) { - return shouldDisregardTranslation(source) ? transformWithoutTranslation(transform, xy) - : transform.transform(xy); + if (shouldDisregardTransformation(source)) { + return xy; + } + return calculateTransformedXYUnchecked(source, transform, xy); } float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform& transform, const PointerCoords& coords) { + if (shouldDisregardTransformation(source)) { + return coords.getAxisValue(axis); + } + if (axis == AMOTION_EVENT_AXIS_X || axis == AMOTION_EVENT_AXIS_Y) { - const vec2 xy = calculateTransformedXY(source, transform, coords.getXYValue()); + const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue()); static_assert(AMOTION_EVENT_AXIS_X == 0 && AMOTION_EVENT_AXIS_Y == 1); return xy[axis]; } diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 1b594f142e..a92016ba3b 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -647,9 +647,8 @@ TEST_F(MotionEventTest, Transform) { ASSERT_NEAR(originalRawY, event.getRawY(0), 0.001); } -MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, - const ui::Transform& transform, - const ui::Transform& rawTransform) { +MotionEvent createMotionEvent(int32_t source, uint32_t action, float x, float y, float dx, float dy, + const ui::Transform& transform, const ui::Transform& rawTransform) { std::vector pointerProperties; pointerProperties.push_back(PointerProperties{/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER}); std::vector pointerCoords; @@ -660,8 +659,8 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, pointerCoords.back().setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, dy); nsecs_t eventTime = systemTime(SYSTEM_TIME_MONOTONIC); MotionEvent event; - event.initialize(InputEvent::nextId(), /* deviceId */ 1, AINPUT_SOURCE_TOUCHSCREEN, - /* displayId */ 0, INVALID_HMAC, AMOTION_EVENT_ACTION_DOWN, + event.initialize(InputEvent::nextId(), /* deviceId */ 1, source, + /* displayId */ 0, INVALID_HMAC, action, /* actionButton */ 0, /* flags */ 0, /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE, transform, /* xPrecision */ 0, /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION, @@ -670,6 +669,13 @@ MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, return event; } +MotionEvent createTouchDownEvent(float x, float y, float dx, float dy, + const ui::Transform& transform, + const ui::Transform& rawTransform) { + return createMotionEvent(AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_DOWN, x, y, dx, dy, + transform, rawTransform); +} + TEST_F(MotionEventTest, ApplyTransform) { // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). ui::Transform identity; @@ -708,16 +714,39 @@ TEST_F(MotionEventTest, ApplyTransform) { changedEvent.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0), 0.001); } +TEST_F(MotionEventTest, JoystickAndTouchpadAreNotTransformed) { + constexpr static std::array kNonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, + AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_JOYSTICK, + AMOTION_EVENT_ACTION_MOVE)}; + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + + for (const auto& [source, action] : kNonTransformedSources) { + const MotionEvent event = + createMotionEvent(source, action, 60, 100, 0, 0, transform, transform); + + // These events should not be transformed in any way. + ASSERT_EQ(60, event.getX(0)); + ASSERT_EQ(100, event.getY(0)); + ASSERT_EQ(event.getRawX(0), event.getX(0)); + ASSERT_EQ(event.getRawY(0), event.getY(0)); + } +} + TEST_F(MotionEventTest, NonPointerSourcesAreNotTranslated) { - constexpr static auto NON_POINTER_SOURCES = {AINPUT_SOURCE_TRACKBALL, - AINPUT_SOURCE_MOUSE_RELATIVE, - AINPUT_SOURCE_JOYSTICK}; - for (uint32_t source : NON_POINTER_SOURCES) { - // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). - ui::Transform transform(ui::Transform::ROT_90, 800, 400); - transform.set(transform.tx() + 20, transform.ty() + 40); - MotionEvent event = createTouchDownEvent(60, 100, 42, 96, transform, transform); - event.setSource(source); + constexpr static std::array kNonPointerSources = {std::pair(AINPUT_SOURCE_TRACKBALL, + AMOTION_EVENT_ACTION_DOWN), + std::pair(AINPUT_SOURCE_MOUSE_RELATIVE, + AMOTION_EVENT_ACTION_MOVE)}; + // Create a rotate-90 transform with an offset (like a window which isn't fullscreen). + ui::Transform transform(ui::Transform::ROT_90, 800, 400); + transform.set(transform.tx() + 20, transform.ty() + 40); + + for (const auto& [source, action] : kNonPointerSources) { + const MotionEvent event = + createMotionEvent(source, action, 60, 100, 42, 96, transform, transform); // Since this event comes from a non-pointer source, it should include rotation but not // translation/offset. diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 176cf8943a..1b19311b2f 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -342,18 +342,6 @@ bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) { std::unique_ptr createDispatchEntry(const InputTarget& inputTarget, std::shared_ptr eventEntry, int32_t inputTargetFlags) { - if (eventEntry->type == EventEntry::Type::MOTION) { - const MotionEntry& motionEntry = static_cast(*eventEntry); - if ((motionEntry.source & AINPUT_SOURCE_CLASS_JOYSTICK) || - (motionEntry.source & AINPUT_SOURCE_CLASS_POSITION)) { - const ui::Transform identityTransform; - // Use identity transform for joystick and position-based (touchpad) events because they - // don't depend on the window transform. - return std::make_unique(eventEntry, inputTargetFlags, identityTransform, - identityTransform, 1.0f /*globalScaleFactor*/); - } - } - if (inputTarget.useDefaultPointerTransform()) { const ui::Transform& transform = inputTarget.getDefaultPointerTransform(); return std::make_unique(eventEntry, inputTargetFlags, transform, diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index ba0ce95458..d8fd16c5e6 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -3012,59 +3012,6 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); } -TEST_F(InputDispatcherTest, NonPointerMotionEvent_JoystickAndTouchpadNotTransformed) { - std::shared_ptr application = std::make_shared(); - sp window = - new FakeWindowHandle(application, mDispatcher, "Test window", ADISPLAY_ID_DEFAULT); - const std::string name = window->getName(); - - // Window gets transformed by offset values. - window->setWindowOffset(500.0f, 500.0f); - - mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application); - window->setFocusable(true); - - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window}}}); - - // First, we set focused window so that focusedWindowHandle is not null. - setFocusedWindow(window); - - // Second, we consume focus event if it is right or wrong according to onFocusChangedLocked. - window->consumeFocusEvent(true); - - constexpr const std::array nonTransformedSources = {std::pair(AINPUT_SOURCE_TOUCHPAD, - AMOTION_EVENT_ACTION_DOWN), - std::pair(AINPUT_SOURCE_JOYSTICK, - AMOTION_EVENT_ACTION_MOVE)}; - for (const auto& [source, action] : nonTransformedSources) { - const NotifyMotionArgs motionArgs = generateMotionArgs(action, source, ADISPLAY_ID_DEFAULT); - mDispatcher->notifyMotion(&motionArgs); - - MotionEvent* event = window->consumeMotion(); - ASSERT_NE(event, nullptr); - - const MotionEvent& motionEvent = *event; - EXPECT_EQ(action, motionEvent.getAction()); - EXPECT_EQ(motionArgs.pointerCount, motionEvent.getPointerCount()); - - float expectedX = motionArgs.pointerCoords[0].getX(); - float expectedY = motionArgs.pointerCoords[0].getY(); - - // Ensure the axis values from the final motion event are not transformed. - EXPECT_EQ(expectedX, motionEvent.getX(0)) - << "expected " << expectedX << " for x coord of " << name.c_str() << ", got " - << motionEvent.getX(0); - EXPECT_EQ(expectedY, motionEvent.getY(0)) - << "expected " << expectedY << " for y coord of " << name.c_str() << ", got " - << motionEvent.getY(0); - // Ensure the raw and transformed axis values for the motion event are the same. - EXPECT_EQ(motionEvent.getRawX(0), motionEvent.getX(0)) - << "expected raw and transformed X-axis values to be equal"; - EXPECT_EQ(motionEvent.getRawY(0), motionEvent.getY(0)) - << "expected raw and transformed Y-axis values to be equal"; - } -} - /** * Ensure that separate calls to sign the same data are generating the same key. * We avoid asserting against INVALID_HMAC. Since the key is random, there is a non-zero chance -- cgit v1.2.3-59-g8ed1b From 3cfec7b162d40a53880f3cbaf863b0b39027db3d Mon Sep 17 00:00:00 2001 From: Antonio Kantek Date: Fri, 5 Nov 2021 18:26:17 -0700 Subject: TouchMode (6.2/n) Fully detaching touch mode from focus event (native) Bug: 193718270 Test: atest inputflinger_tests Test: atest libinput_tests Test: atest FrameworksCoreTests Test: atest CtsInputMethodTestCases Test: atest CtsInputTestCases Test: atest CtsSecurityTestCases Test: atest CtsWindowManagerDeviceTestCases Change-Id: I334c63d781ee8e8c13d21cc4a6cf323d885fc985 --- include/input/Input.h | 5 +---- include/input/InputTransport.h | 7 +++---- libs/input/Input.cpp | 4 +--- libs/input/InputTransport.cpp | 19 ++++++------------- libs/input/tests/InputPublisherAndConsumer_test.cpp | 4 +--- libs/input/tests/StructLayout_test.cpp | 3 +-- services/inputflinger/dispatcher/InputDispatcher.cpp | 3 +-- services/inputflinger/tests/InputDispatcher_test.cpp | 1 - 8 files changed, 14 insertions(+), 32 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 7cc595a264..ce9cefed8b 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -840,15 +840,12 @@ public: inline bool getHasFocus() const { return mHasFocus; } - inline bool getInTouchMode() const { return mInTouchMode; } - - void initialize(int32_t id, bool hasFocus, bool inTouchMode); + void initialize(int32_t id, bool hasFocus); void initialize(const FocusEvent& from); protected: bool mHasFocus; - bool mInTouchMode; }; /* diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index d655b28278..edcb615491 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -178,10 +178,9 @@ struct InputMessage { struct Focus { int32_t eventId; - // The following 3 fields take up 4 bytes total + // The following 2 fields take up 4 bytes total bool hasFocus; - bool inTouchMode; - uint8_t empty[2]; + uint8_t empty[3]; inline size_t size() const { return sizeof(Focus); } } focus; @@ -381,7 +380,7 @@ public: * Returns DEAD_OBJECT if the channel's peer has been closed. * Other errors probably indicate that the channel is broken. */ - status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, bool inTouchMode); + status_t publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus); /* Publishes a capture event to the input channel. * diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 24a77209c4..c7f77d42fe 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -820,17 +820,15 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, // --- FocusEvent --- -void FocusEvent::initialize(int32_t id, bool hasFocus, bool inTouchMode) { +void FocusEvent::initialize(int32_t id, bool hasFocus) { InputEvent::initialize(id, ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN, ADISPLAY_ID_NONE, INVALID_HMAC); mHasFocus = hasFocus; - mInTouchMode = inTouchMode; } void FocusEvent::initialize(const FocusEvent& from) { InputEvent::initialize(from); mHasFocus = from.mHasFocus; - mInTouchMode = from.mInTouchMode; } // --- CaptureEvent --- diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 02a5a0807b..a065ce25f7 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -278,7 +278,6 @@ void InputMessage::getSanitizedCopy(InputMessage* msg) const { case InputMessage::Type::FOCUS: { msg->body.focus.eventId = body.focus.eventId; msg->body.focus.hasFocus = body.focus.hasFocus; - msg->body.focus.inTouchMode = body.focus.inTouchMode; break; } case InputMessage::Type::CAPTURE: { @@ -622,13 +621,10 @@ status_t InputPublisher::publishMotionEvent( return mChannel->sendMessage(&msg); } -status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus, - bool inTouchMode) { +status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool hasFocus) { if (ATRACE_ENABLED()) { - std::string message = - StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)", - mChannel->getName().c_str(), toString(hasFocus), - toString(inTouchMode)); + std::string message = StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s)", + mChannel->getName().c_str(), toString(hasFocus)); ATRACE_NAME(message.c_str()); } @@ -637,7 +633,6 @@ status_t InputPublisher::publishFocusEvent(uint32_t seq, int32_t eventId, bool h msg.header.seq = seq; msg.body.focus.eventId = eventId; msg.body.focus.hasFocus = hasFocus; - msg.body.focus.inTouchMode = inTouchMode; return mChannel->sendMessage(&msg); } @@ -1371,8 +1366,7 @@ void InputConsumer::initializeKeyEvent(KeyEvent* event, const InputMessage* msg) } void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) { - event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus, - msg->body.focus.inTouchMode); + event->initialize(msg->body.focus.eventId, msg->body.focus.hasFocus); } void InputConsumer::initializeCaptureEvent(CaptureEvent* event, const InputMessage* msg) { @@ -1491,9 +1485,8 @@ std::string InputConsumer::dump() const { break; } case InputMessage::Type::FOCUS: { - out += android::base::StringPrintf("hasFocus=%s inTouchMode=%s", - toString(msg.body.focus.hasFocus), - toString(msg.body.focus.inTouchMode)); + out += android::base::StringPrintf("hasFocus=%s", + toString(msg.body.focus.hasFocus)); break; } case InputMessage::Type::CAPTURE: { diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 973194c8f6..05bc0bcbe8 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -290,10 +290,9 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { constexpr uint32_t seq = 15; int32_t eventId = InputEvent::nextId(); constexpr bool hasFocus = true; - constexpr bool inTouchMode = true; const nsecs_t publishTime = systemTime(SYSTEM_TIME_MONOTONIC); - status = mPublisher->publishFocusEvent(seq, eventId, hasFocus, inTouchMode); + status = mPublisher->publishFocusEvent(seq, eventId, hasFocus); ASSERT_EQ(OK, status) << "publisher publishFocusEvent should return OK"; uint32_t consumeSeq; @@ -309,7 +308,6 @@ void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() { EXPECT_EQ(seq, consumeSeq); EXPECT_EQ(eventId, focusEvent->getId()); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); status = mConsumer->sendFinishedSignal(seq, true); ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK"; diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index 2f88704d63..b6a94764e5 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -84,8 +84,7 @@ void TestInputMessageAlignment() { CHECK_OFFSET(InputMessage::Body::Focus, eventId, 0); CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4); - CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 5); - CHECK_OFFSET(InputMessage::Body::Focus, empty, 6); + CHECK_OFFSET(InputMessage::Body::Focus, empty, 5); CHECK_OFFSET(InputMessage::Body::Capture, eventId, 0); CHECK_OFFSET(InputMessage::Body::Capture, pointerCaptureEnabled, 4); diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 695258759b..5c56e842db 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3234,8 +3234,7 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, const FocusEntry& focusEntry = static_cast(eventEntry); status = connection->inputPublisher.publishFocusEvent(dispatchEntry->seq, focusEntry.id, - focusEntry.hasFocus, - mInTouchMode); + focusEntry.hasFocus); break; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index d8fd16c5e6..515a01e137 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -842,7 +842,6 @@ public: FocusEvent* focusEvent = static_cast(event); EXPECT_EQ(hasFocus, focusEvent->getHasFocus()); - EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode()); } void consumeCaptureEvent(bool hasCapture) { -- cgit v1.2.3-59-g8ed1b From d948957b396a9301e6e247023d8509f44f6cf87c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 12 Nov 2021 20:08:38 -0800 Subject: Look up source using & instead of equality Source for an input device may be composite. On some devices, the source is specified as TOUCHSCREEN | STYLUS. That means a regular lookup of a MotionRange using just SOURCE_TOUCHSCREEN fails. Update the code to allow composite sources. Also, improve the source dump by printing words instead of numbers. Bug: 198472780 Test: adb shell dumpsys input Change-Id: I8d395f2bb5a6db031e5c2aa6c1f5152ff067a2bb --- include/input/Input.h | 4 ++ libs/input/Input.cpp | 47 ++++++++++++++++++++-- libs/input/InputDevice.cpp | 6 +-- services/inputflinger/InputClassifier.cpp | 3 +- services/inputflinger/dispatcher/Entry.cpp | 20 ++++----- .../inputflinger/dispatcher/InputDispatcher.cpp | 4 -- 6 files changed, 62 insertions(+), 22 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 7cc595a264..263ade40fb 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -203,6 +203,10 @@ class Parcel; const char* inputEventTypeToString(int32_t type); +std::string inputEventSourceToString(int32_t source); + +bool isFromSource(uint32_t source, uint32_t test); + /* * Flags that flow alongside events in the input dispatch system to help with certain * policy decisions such as waking from device sleep. diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 24a77209c4..a1d75d5fb1 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -66,10 +66,6 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) return transformedXy - transformedOrigin; } -bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; -} - bool shouldDisregardTransformation(uint32_t source) { // Do not apply any transformations to axes from joysticks or touchpads. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -148,6 +144,49 @@ const char* inputEventTypeToString(int32_t type) { return "UNKNOWN"; } +std::string inputEventSourceToString(int32_t source) { + if (source == AINPUT_SOURCE_UNKNOWN) { + return "UNKNOWN"; + } + if (source == static_cast(AINPUT_SOURCE_ANY)) { + return "ANY"; + } + static const std::map SOURCES{ + {AINPUT_SOURCE_KEYBOARD, "KEYBOARD"}, + {AINPUT_SOURCE_DPAD, "DPAD"}, + {AINPUT_SOURCE_GAMEPAD, "GAMEPAD"}, + {AINPUT_SOURCE_TOUCHSCREEN, "TOUCHSCREEN"}, + {AINPUT_SOURCE_MOUSE, "MOUSE"}, + {AINPUT_SOURCE_STYLUS, "STYLUS"}, + {AINPUT_SOURCE_BLUETOOTH_STYLUS, "BLUETOOTH_STYLUS"}, + {AINPUT_SOURCE_TRACKBALL, "TRACKBALL"}, + {AINPUT_SOURCE_MOUSE_RELATIVE, "MOUSE_RELATIVE"}, + {AINPUT_SOURCE_TOUCHPAD, "TOUCHPAD"}, + {AINPUT_SOURCE_TOUCH_NAVIGATION, "TOUCH_NAVIGATION"}, + {AINPUT_SOURCE_JOYSTICK, "JOYSTICK"}, + {AINPUT_SOURCE_HDMI, "HDMI"}, + {AINPUT_SOURCE_SENSOR, "SENSOR"}, + {AINPUT_SOURCE_ROTARY_ENCODER, "ROTARY_ENCODER"}, + }; + std::string result; + for (const auto& [source_entry, str] : SOURCES) { + if ((source & source_entry) == source_entry) { + if (!result.empty()) { + result += " | "; + } + result += str; + } + } + if (result.empty()) { + result = StringPrintf("0x%08x", source); + } + return result; +} + +bool isFromSource(uint32_t source, uint32_t test) { + return (source & test) == test; +} + VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 015bd81361..ac84627b3f 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -208,10 +208,8 @@ void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t control const InputDeviceInfo::MotionRange* InputDeviceInfo::getMotionRange( int32_t axis, uint32_t source) const { - size_t numRanges = mMotionRanges.size(); - for (size_t i = 0; i < numRanges; i++) { - const MotionRange& range = mMotionRanges[i]; - if (range.axis == axis && range.source == source) { + for (const MotionRange& range : mMotionRanges) { + if (range.axis == axis && isFromSource(range.source, source)) { return ⦥ } } diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 29d8a0f441..19cad7b9ad 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -68,7 +68,8 @@ static MotionClassification getMotionClassification(common::V1_0::Classification } static bool isTouchEvent(const NotifyMotionArgs& args) { - return args.source == AINPUT_SOURCE_TOUCHPAD || args.source == AINPUT_SOURCE_TOUCHSCREEN; + return isFromSource(args.source, AINPUT_SOURCE_TOUCHPAD) || + isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN); } // --- ClassifierEvent --- diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index c03581d8da..1674afd02e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -175,13 +175,13 @@ std::string KeyEntry::getDescription() const { if (!GetBoolProperty("ro.debuggable", false)) { return "KeyEvent"; } - return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 - ", source=0x%08x, displayId=%" PRId32 ", action=%s, " + return StringPrintf("KeyEvent(deviceId=%d, eventTime=%" PRIu64 ", source=%s, displayId=%" PRId32 + ", action=%s, " "flags=0x%08x, keyCode=%s(%d), scanCode=%d, metaState=0x%08x, " "repeatCount=%d), policyFlags=0x%08x", - deviceId, eventTime, source, displayId, KeyEvent::actionToString(action), - flags, KeyEvent::getLabel(keyCode), keyCode, scanCode, metaState, - repeatCount, policyFlags); + deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId, + KeyEvent::actionToString(action), flags, KeyEvent::getLabel(keyCode), + keyCode, scanCode, metaState, repeatCount, policyFlags); } void KeyEntry::recycle() { @@ -249,12 +249,12 @@ std::string MotionEntry::getDescription() const { } std::string msg; msg += StringPrintf("MotionEvent(deviceId=%d, eventTime=%" PRIu64 - ", source=0x%08x, displayId=%" PRId32 + ", source=%s, displayId=%" PRId32 ", action=%s, actionButton=0x%08x, flags=0x%08x, metaState=0x%08x, " "buttonState=0x%08x, " "classification=%s, edgeFlags=0x%08x, xPrecision=%.1f, yPrecision=%.1f, " "xCursorPosition=%0.1f, yCursorPosition=%0.1f, pointers=[", - deviceId, eventTime, source, displayId, + deviceId, eventTime, inputEventSourceToString(source).c_str(), displayId, MotionEvent::actionToString(action).c_str(), actionButton, flags, metaState, buttonState, motionClassificationToString(classification), edgeFlags, xPrecision, yPrecision, xCursorPosition, yCursorPosition); @@ -289,9 +289,11 @@ SensorEntry::~SensorEntry() {} std::string SensorEntry::getDescription() const { std::string msg; - msg += StringPrintf("SensorEntry(deviceId=%d, source=0x%08x, sensorType=0x%08x, " + std::string sensorTypeStr(ftl::enum_name(sensorType).value_or("?")); + msg += StringPrintf("SensorEntry(deviceId=%d, source=%s, sensorType=%s, " "accuracy=0x%08x, hwTimestamp=%" PRId64, - deviceId, source, sensorType, accuracy, hwTimestamp); + deviceId, inputEventSourceToString(source).c_str(), sensorTypeStr.c_str(), + accuracy, hwTimestamp); if (!GetBoolProperty("ro.debuggable", false)) { for (size_t i = 0; i < values.size(); i++) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 1b19311b2f..44409a262e 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -516,10 +516,6 @@ bool isConnectionResponsive(const Connection& connection) { return true; } -bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; -} - vec2 transformWithoutTranslation(const ui::Transform& transform, float x, float y) { const vec2 transformedXy = transform.transform(x, y); const vec2 transformedOrigin = transform.transform(0, 0); -- cgit v1.2.3-59-g8ed1b From 6b430413d07b6afca1a4146ea91809567b5cfe9a Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 4 Nov 2021 16:51:29 -0700 Subject: Change PointerController to display space PointerController used to work in the logical display space, so TouchInputMapper and CursorInputMapper would need to transform the coordinates before interacting with it. This CL makes PointerController work in the display space. It will transform incoming and outgoing coordinates to stay in the display space using the DisplayInfo provided by SurfaceFlinger. Using info provided by SF also means that there will be better synchonization between the pointers and display changes like rotation. Bug: 188939842 Bug: 144544464 Test: manual: ensure mouse and touch spots work in different display orientations and sizes set using "adb shell wm size" Change-Id: I764c070adef7e9f26c0062f1b3466c7115a305ac --- include/input/Input.h | 5 + libs/input/Input.cpp | 12 +-- .../include/PointerControllerInterface.h | 3 +- .../reader/mapper/CursorInputMapper.cpp | 17 +--- .../inputflinger/reader/mapper/CursorInputMapper.h | 2 - .../reader/mapper/TouchCursorInputMapperCommon.h | 20 ---- .../reader/mapper/TouchInputMapper.cpp | 110 +++++++-------------- .../inputflinger/reader/mapper/TouchInputMapper.h | 8 -- 8 files changed, 47 insertions(+), 130 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1e06257591..5242dcb476 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -201,6 +201,11 @@ namespace android { class Parcel; #endif +/* + * Apply the given transform to the point without applying any translation/offset. + */ +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy); + const char* inputEventTypeToString(int32_t type); std::string inputEventSourceToString(int32_t source); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8974b22c86..cb93c92310 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -60,12 +60,6 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return atan2f(transformedPoint.x, -transformedPoint.y); } -vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { - const vec2 transformedXy = transform.transform(xy); - const vec2 transformedOrigin = transform.transform(0, 0); - return transformedXy - transformedOrigin; -} - bool shouldDisregardTransformation(uint32_t source) { // Do not apply any transformations to axes from joysticks or touchpads. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -120,6 +114,12 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { + const vec2 transformedXy = transform.transform(xy); + const vec2 transformedOrigin = transform.transform(0, 0); + return transformedXy - transformedOrigin; +} + const char* inputEventTypeToString(int32_t type) { switch (type) { case AINPUT_EVENT_TYPE_KEY: { diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index b1069497d3..db4228d862 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -30,7 +30,8 @@ namespace android { * fingers * * The pointer controller is responsible for providing synchronization and for tracking - * display orientation changes if needed. + * display orientation changes if needed. It works in the display panel's coordinate space, which + * is the same coordinate space used by InputReader. */ class PointerControllerInterface { protected: diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 15ba45945a..fcb56ef05c 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -188,8 +188,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mOrientation = DISPLAY_ORIENTATION_0; - mDisplayWidth = 0; - mDisplayHeight = 0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -203,8 +201,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = getInverseRotation(internalViewport->orientation); - mDisplayWidth = internalViewport->deviceWidth; - mDisplayHeight = internalViewport->deviceHeight; } } @@ -335,14 +331,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { - float dx = deltaX; - float dy = deltaY; - // Rotate the delta from InputReader's un-rotated coordinate space to - // PointerController's rotated coordinate space that is oriented with the - // viewport. - rotateDelta(getInverseRotation(mOrientation), &dx, &dy); - - mPointerController->move(dx, dy); + mPointerController->move(deltaX, deltaY); } if (buttonsChanged) { @@ -353,10 +342,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - // Rotate the cursor position that is in PointerController's rotated coordinate space - // to InputReader's un-rotated coordinate space. - rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, - mDisplayWidth, mDisplayHeight); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 88e947f7d5..9a8ca01294 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -105,8 +105,6 @@ private: VelocityControl mWheelYVelocityControl; int32_t mOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; std::shared_ptr mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 8c30e38908..31a3d2e172 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -64,26 +64,6 @@ static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { } } -// Rotates the given point (x, y) by the supplied orientation. The width and height are the -// dimensions of the surface prior to this rotation being applied. -static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { - rotateDelta(orientation, &x, &y); - switch (orientation) { - case DISPLAY_ORIENTATION_90: - y += width; - break; - case DISPLAY_ORIENTATION_180: - x += width; - y += height; - break; - case DISPLAY_ORIENTATION_270: - x += height; - break; - default: - break; - } -} - // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. static bool isPointerDown(int32_t buttonState) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3fe6fd130f..913c666a4a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1668,9 +1668,10 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); } bool TouchInputMapper::isTouchScreen() { @@ -2410,9 +2411,10 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - setTouchSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); } } else { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); @@ -2562,7 +2564,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // the pointer is hovering again even if the user is not currently touching // the touch pad. This ensures that a view will receive a fresh hover enter // event after a tap. - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); PointerProperties pointerProperties; pointerProperties.clear(); @@ -2819,12 +2822,13 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the click will occur at the position of the anchor // spot and all other spots will move there. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG; mPointerGesture.currentGestureIdBits.clear(); @@ -2850,7 +2854,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) && lastFingerCount == 1) { if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { #if DEBUG_GESTURES @@ -2918,7 +2923,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER; if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) { if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; @@ -2952,7 +2958,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the hover or drag will occur at the position of the anchor spot. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -2974,7 +2980,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi down = false; } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); @@ -3047,9 +3054,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mCurrentRawState.rawPointerData .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, &mPointerGesture.referenceTouchY); - auto [x, y] = getMouseCursorPosition(); - mPointerGesture.referenceGestureX = x; - mPointerGesture.referenceGestureY = y; + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); } // Clear the reference deltas for fingers not yet included in the reference calculation. @@ -3387,13 +3393,15 @@ void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uin if (!mCurrentCookedState.stylusIdBits.isEmpty()) { uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), - mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); + mPointerController + ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), + mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); down = !hovering; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[index]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3434,7 +3442,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -3442,7 +3450,8 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint down = isPointerDown(mCurrentRawState.buttonState); hovering = !down; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3482,7 +3491,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin } int32_t displayId = mPointerController->getDisplayId(); - auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition(); + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; @@ -3648,9 +3658,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mDeviceMode == DeviceMode::POINTER) { - auto [x, y] = getMouseCursorPosition(); - xCursorPosition = x; - yCursorPosition = y; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); } const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); @@ -3999,56 +4007,4 @@ std::optional TouchInputMapper::getAssociatedDisplayId() { return std::nullopt; } -void TouchInputMapper::moveMouseCursor(float dx, float dy) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate - // space that is oriented with the viewport. - rotateDelta(mViewport.orientation, &dx, &dy); - - mPointerController->move(dx, dy); -} - -std::pair TouchInputMapper::getMouseCursorPosition() const { - float x = 0; - float y = 0; - mPointerController->getPosition(&x, &y); - - if (!mViewport.isValid()) return {x, y}; - - // Convert from PointerController's rotated coordinate space that is oriented with the viewport - // to InputReader's un-rotated coordinate space. - const int32_t orientation = getInverseRotation(mViewport.orientation); - rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight); - return {x, y}; -} - -void TouchInputMapper::setMouseCursorPosition(float x, float y) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space that is oriented with the viewport. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - mPointerController->setPosition(x, y); -} - -void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId) { - std::array outSpotCoords{}; - - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { - const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()]; - float x = spotCoords[index].getX(); - float y = spotCoords[index].getY(); - float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - } - - mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId); -} - } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 496491b62d..9b020a609a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -803,14 +803,6 @@ private: const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; - - // Wrapper methods for interfacing with PointerController. These are used to convert points - // between the coordinate spaces used by InputReader and PointerController, if they differ. - void moveMouseCursor(float dx, float dy) const; - std::pair getMouseCursorPosition() const; - void setMouseCursorPosition(float x, float y) const; - void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From 259a2122aeafea0f20bfcc7c9bcd089511a5aa56 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 17 Nov 2021 21:48:11 +0000 Subject: Revert "Change PointerController to display space" Revert submission 16194643-pointer-controller-in-display-space Reason for revert: b/206817973 Reverted Changes: I764c070ad:Change PointerController to display space I5e9e19c36:Change PointerController to display space Change-Id: If6e96f41873dd9601f49fc9f9f514a95394f3c58 --- include/input/Input.h | 5 - libs/input/Input.cpp | 12 +-- .../include/PointerControllerInterface.h | 3 +- .../reader/mapper/CursorInputMapper.cpp | 17 +++- .../inputflinger/reader/mapper/CursorInputMapper.h | 2 + .../reader/mapper/TouchCursorInputMapperCommon.h | 20 ++++ .../reader/mapper/TouchInputMapper.cpp | 110 ++++++++++++++------- .../inputflinger/reader/mapper/TouchInputMapper.h | 8 ++ 8 files changed, 130 insertions(+), 47 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 5242dcb476..1e06257591 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -201,11 +201,6 @@ namespace android { class Parcel; #endif -/* - * Apply the given transform to the point without applying any translation/offset. - */ -vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy); - const char* inputEventTypeToString(int32_t type); std::string inputEventSourceToString(int32_t source); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index cb93c92310..8974b22c86 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -60,6 +60,12 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return atan2f(transformedPoint.x, -transformedPoint.y); } +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { + const vec2 transformedXy = transform.transform(xy); + const vec2 transformedOrigin = transform.transform(0, 0); + return transformedXy - transformedOrigin; +} + bool shouldDisregardTransformation(uint32_t source) { // Do not apply any transformations to axes from joysticks or touchpads. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -114,12 +120,6 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- -vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { - const vec2 transformedXy = transform.transform(xy); - const vec2 transformedOrigin = transform.transform(0, 0); - return transformedXy - transformedOrigin; -} - const char* inputEventTypeToString(int32_t type) { switch (type) { case AINPUT_EVENT_TYPE_KEY: { diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index db4228d862..b1069497d3 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -30,8 +30,7 @@ namespace android { * fingers * * The pointer controller is responsible for providing synchronization and for tracking - * display orientation changes if needed. It works in the display panel's coordinate space, which - * is the same coordinate space used by InputReader. + * display orientation changes if needed. */ class PointerControllerInterface { protected: diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index fcb56ef05c..15ba45945a 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -188,6 +188,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mOrientation = DISPLAY_ORIENTATION_0; + mDisplayWidth = 0; + mDisplayHeight = 0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -201,6 +203,8 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = getInverseRotation(internalViewport->orientation); + mDisplayWidth = internalViewport->deviceWidth; + mDisplayHeight = internalViewport->deviceHeight; } } @@ -331,7 +335,14 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { - mPointerController->move(deltaX, deltaY); + float dx = deltaX; + float dy = deltaY; + // Rotate the delta from InputReader's un-rotated coordinate space to + // PointerController's rotated coordinate space that is oriented with the + // viewport. + rotateDelta(getInverseRotation(mOrientation), &dx, &dy); + + mPointerController->move(dx, dy); } if (buttonsChanged) { @@ -342,6 +353,10 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + // Rotate the cursor position that is in PointerController's rotated coordinate space + // to InputReader's un-rotated coordinate space. + rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, + mDisplayWidth, mDisplayHeight); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 9a8ca01294..88e947f7d5 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -105,6 +105,8 @@ private: VelocityControl mWheelYVelocityControl; int32_t mOrientation; + int32_t mDisplayWidth; + int32_t mDisplayHeight; std::shared_ptr mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 31a3d2e172..8c30e38908 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -64,6 +64,26 @@ static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { } } +// Rotates the given point (x, y) by the supplied orientation. The width and height are the +// dimensions of the surface prior to this rotation being applied. +static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { + rotateDelta(orientation, &x, &y); + switch (orientation) { + case DISPLAY_ORIENTATION_90: + y += width; + break; + case DISPLAY_ORIENTATION_180: + x += width; + y += height; + break; + case DISPLAY_ORIENTATION_270: + x += height; + break; + default: + break; + } +} + // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. static bool isPointerDown(int32_t buttonState) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 913c666a4a..3fe6fd130f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1668,10 +1668,9 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, - mViewport.displayId); + setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); } bool TouchInputMapper::isTouchScreen() { @@ -2411,10 +2410,9 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - mPointerController->setSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, - mPointerController->getDisplayId()); + setTouchSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); } } else { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); @@ -2564,8 +2562,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // the pointer is hovering again even if the user is not currently touching // the touch pad. This ensures that a view will receive a fresh hover enter // event after a tap. - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); PointerProperties pointerProperties; pointerProperties.clear(); @@ -2822,13 +2819,12 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the click will occur at the position of the anchor // spot and all other spots will move there. - mPointerController->move(deltaX, deltaY); + moveMouseCursor(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG; mPointerGesture.currentGestureIdBits.clear(); @@ -2854,8 +2850,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) && lastFingerCount == 1) { if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { #if DEBUG_GESTURES @@ -2923,8 +2918,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER; if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) { if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; @@ -2958,7 +2952,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the hover or drag will occur at the position of the anchor spot. - mPointerController->move(deltaX, deltaY); + moveMouseCursor(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -2980,8 +2974,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi down = false; } - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); @@ -3054,8 +3047,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mCurrentRawState.rawPointerData .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, &mPointerGesture.referenceTouchY); - mPointerController->getPosition(&mPointerGesture.referenceGestureX, - &mPointerGesture.referenceGestureY); + auto [x, y] = getMouseCursorPosition(); + mPointerGesture.referenceGestureX = x; + mPointerGesture.referenceGestureY = y; } // Clear the reference deltas for fingers not yet included in the reference calculation. @@ -3393,15 +3387,13 @@ void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uin if (!mCurrentCookedState.stylusIdBits.isEmpty()) { uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - mPointerController - ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), - mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); + setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), + mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); down = !hovering; - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[index]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3442,7 +3434,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); - mPointerController->move(deltaX, deltaY); + moveMouseCursor(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -3450,8 +3442,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint down = isPointerDown(mCurrentRawState.buttonState); hovering = !down; - float x, y; - mPointerController->getPosition(&x, &y); + auto [x, y] = getMouseCursorPosition(); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3491,8 +3482,7 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin } int32_t displayId = mPointerController->getDisplayId(); - float xCursorPosition, yCursorPosition; - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition(); if (mPointerSimple.down && !down) { mPointerSimple.down = false; @@ -3658,7 +3648,9 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mDeviceMode == DeviceMode::POINTER) { - mPointerController->getPosition(&xCursorPosition, &yCursorPosition); + auto [x, y] = getMouseCursorPosition(); + xCursorPosition = x; + yCursorPosition = y; } const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); @@ -4007,4 +3999,56 @@ std::optional TouchInputMapper::getAssociatedDisplayId() { return std::nullopt; } +void TouchInputMapper::moveMouseCursor(float dx, float dy) const { + // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate + // space that is oriented with the viewport. + rotateDelta(mViewport.orientation, &dx, &dy); + + mPointerController->move(dx, dy); +} + +std::pair TouchInputMapper::getMouseCursorPosition() const { + float x = 0; + float y = 0; + mPointerController->getPosition(&x, &y); + + if (!mViewport.isValid()) return {x, y}; + + // Convert from PointerController's rotated coordinate space that is oriented with the viewport + // to InputReader's un-rotated coordinate space. + const int32_t orientation = getInverseRotation(mViewport.orientation); + rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight); + return {x, y}; +} + +void TouchInputMapper::setMouseCursorPosition(float x, float y) const { + // Convert from InputReader's un-rotated coordinate space to PointerController's rotated + // coordinate space that is oriented with the viewport. + rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); + + mPointerController->setPosition(x, y); +} + +void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId) { + std::array outSpotCoords{}; + + for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { + const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()]; + float x = spotCoords[index].getX(); + float y = spotCoords[index].getY(); + float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + + // Convert from InputReader's un-rotated coordinate space to PointerController's rotated + // coordinate space. + rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); + + outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x); + outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y); + outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); + } + + mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId); +} + } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 9b020a609a..496491b62d 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -803,6 +803,14 @@ private: const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; + + // Wrapper methods for interfacing with PointerController. These are used to convert points + // between the coordinate spaces used by InputReader and PointerController, if they differ. + void moveMouseCursor(float dx, float dy) const; + std::pair getMouseCursorPosition() const; + void setMouseCursorPosition(float x, float y) const; + void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, + BitSet32 spotIdBits, int32_t displayId); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From 5e6b390919a9786a50f9d5cbd539e9dc53ad78c2 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 18 Nov 2021 07:54:09 -0800 Subject: Remove x/y offset from MotionEntry Window or display offsets are now captured in the various transforms, so we can remove the ability to specify the offset by itself. Bug: 188939842 Test: presubmit Test: atest inputflinger_tests Change-Id: Ifd359ebb03850bb30efb358e84f0a7e77260a8b2 --- include/input/Input.h | 5 ++--- libs/input/Input.cpp | 5 ----- services/inputflinger/dispatcher/Entry.cpp | 5 +---- services/inputflinger/dispatcher/Entry.h | 3 +-- services/inputflinger/dispatcher/InputDispatcher.cpp | 10 +++++----- services/inputflinger/dispatcher/InputState.cpp | 6 ++---- 6 files changed, 11 insertions(+), 23 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1e06257591..1c79c4a21c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -377,7 +377,6 @@ struct PointerCoords { // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); - void applyOffset(float xOffset, float yOffset); void transform(const ui::Transform& transform); @@ -567,7 +566,7 @@ public: inline float getYOffset() const { return mTransform.ty(); } - inline ui::Transform getTransform() const { return mTransform; } + inline const ui::Transform& getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -583,7 +582,7 @@ public: void setCursorPosition(float x, float y); - ui::Transform getRawTransform() const { return mRawTransform; } + inline const ui::Transform& getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8974b22c86..69ea7dfcb5 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -343,11 +343,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } -void PointerCoords::applyOffset(float xOffset, float yOffset) { - setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); - setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); -} - #ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 1674afd02e..3d0818b738 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -214,7 +214,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xOffset, float yOffset) + const PointerCoords* pointerCoords) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -235,9 +235,6 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } } } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 477781a2ad..0f792967eb 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -184,8 +184,7 @@ struct MotionEntry : EventEntry { int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); std::string getDescription() const override; ~MotionEntry() override; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3fd1c8aa27..04ff599f13 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -378,7 +378,7 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, - pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); + pointerCoords.data()); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -3752,7 +3752,7 @@ std::unique_ptr InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords, 0, 0); + splitPointerProperties, splitPointerCoords); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3978,7 +3978,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords, 0, 0); + args->pointerProperties, args->pointerCoords); if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && @@ -4208,7 +4208,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawXCursorPosition(), motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, 0, 0); + pointerProperties, samplePointerCoords); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { @@ -4228,7 +4228,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), pointerProperties, - samplePointerCoords, 0, 0); + samplePointerCoords); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(nextInjectedEntry)); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 3bb0bc995c..ad3c6159ef 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -296,8 +296,7 @@ std::vector> InputState::synthesizeCancelationEvents memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, - memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); + memento.pointerCoords)); } } return events; @@ -349,8 +348,7 @@ std::vector> InputState::synthesizePointerDownEvents AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + pointerCount, pointerProperties, pointerCoords)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; -- cgit v1.2.3-59-g8ed1b From 2b80b386c4ed5a1b3240da32080280b0a1ec9cef Mon Sep 17 00:00:00 2001 From: Jay Wang Date: Fri, 19 Nov 2021 17:26:26 +0000 Subject: Revert "Remove x/y offset from MotionEntry" Revert submission 16295572 Reason for revert: DroidMonitor-triggered revert due to breakage bug http://b/207128427. BUG: 207128427 Reverted Changes: I65c284e5e:Input injection: Assume transformed values are in ... Ifd359ebb0:Remove x/y offset from MotionEntry Change-Id: Iab395d41d49db17c0a682bdd6c77fe2aacc004f4 --- include/input/Input.h | 5 +++-- libs/input/Input.cpp | 5 +++++ services/inputflinger/dispatcher/Entry.cpp | 5 ++++- services/inputflinger/dispatcher/Entry.h | 3 ++- services/inputflinger/dispatcher/InputDispatcher.cpp | 10 +++++----- services/inputflinger/dispatcher/InputState.cpp | 6 ++++-- 6 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1c79c4a21c..1e06257591 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -377,6 +377,7 @@ struct PointerCoords { // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); + void applyOffset(float xOffset, float yOffset); void transform(const ui::Transform& transform); @@ -566,7 +567,7 @@ public: inline float getYOffset() const { return mTransform.ty(); } - inline const ui::Transform& getTransform() const { return mTransform; } + inline ui::Transform getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -582,7 +583,7 @@ public: void setCursorPosition(float x, float y); - inline const ui::Transform& getRawTransform() const { return mRawTransform; } + ui::Transform getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 69ea7dfcb5..8974b22c86 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -343,6 +343,11 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } +void PointerCoords::applyOffset(float xOffset, float yOffset) { + setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); + setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); +} + #ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 3d0818b738..1674afd02e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -214,7 +214,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) + const PointerCoords* pointerCoords, float xOffset, float yOffset) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -235,6 +235,9 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); + if (xOffset || yOffset) { + this->pointerCoords[i].applyOffset(xOffset, yOffset); + } } } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 0f792967eb..477781a2ad 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -184,7 +184,8 @@ struct MotionEntry : EventEntry { int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); std::string getDescription() const override; ~MotionEntry() override; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 04ff599f13..3fd1c8aa27 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -378,7 +378,7 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, - pointerCoords.data()); + pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -3752,7 +3752,7 @@ std::unique_ptr InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords); + splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3978,7 +3978,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + args->pointerProperties, args->pointerCoords, 0, 0); if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && @@ -4208,7 +4208,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawXCursorPosition(), motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords); + pointerProperties, samplePointerCoords, 0, 0); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { @@ -4228,7 +4228,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), pointerProperties, - samplePointerCoords); + samplePointerCoords, 0, 0); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(nextInjectedEntry)); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index ad3c6159ef..3bb0bc995c 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -296,7 +296,8 @@ std::vector> InputState::synthesizeCancelationEvents memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, - memento.pointerCoords)); + memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); } } return events; @@ -348,7 +349,8 @@ std::vector> InputState::synthesizePointerDownEvents AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords)); + pointerCount, pointerProperties, pointerCoords, + 0 /*xOffset*/, 0 /*yOffset*/)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; -- cgit v1.2.3-59-g8ed1b From de69f8ae323ccfbf150a368145525b619568469c Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 18 Nov 2021 16:40:34 +0000 Subject: Reland "Change PointerController to display space" 259a2122aeafea0f20bfcc7c9bcd089511a5aa56 Bug: 188939842 Bug: 144544464 Bug: 206817973 Test: forrest run - CtsHardwareTestCases Change-Id: I8f0312a502ec5c79038ef1697cf2d5b23db9fcfc --- include/input/Input.h | 5 + libs/input/Input.cpp | 12 +-- .../include/PointerControllerInterface.h | 3 +- .../reader/mapper/CursorInputMapper.cpp | 17 +--- .../inputflinger/reader/mapper/CursorInputMapper.h | 2 - .../reader/mapper/TouchCursorInputMapperCommon.h | 20 ---- .../reader/mapper/TouchInputMapper.cpp | 110 +++++++-------------- .../inputflinger/reader/mapper/TouchInputMapper.h | 8 -- 8 files changed, 47 insertions(+), 130 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1e06257591..5242dcb476 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -201,6 +201,11 @@ namespace android { class Parcel; #endif +/* + * Apply the given transform to the point without applying any translation/offset. + */ +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy); + const char* inputEventTypeToString(int32_t type); std::string inputEventSourceToString(int32_t source); diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8974b22c86..cb93c92310 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -60,12 +60,6 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { return atan2f(transformedPoint.x, -transformedPoint.y); } -vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { - const vec2 transformedXy = transform.transform(xy); - const vec2 transformedOrigin = transform.transform(0, 0); - return transformedXy - transformedOrigin; -} - bool shouldDisregardTransformation(uint32_t source) { // Do not apply any transformations to axes from joysticks or touchpads. return isFromSource(source, AINPUT_SOURCE_CLASS_JOYSTICK) || @@ -120,6 +114,12 @@ int32_t IdGenerator::nextId() const { // --- InputEvent --- +vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) { + const vec2 transformedXy = transform.transform(xy); + const vec2 transformedOrigin = transform.transform(0, 0); + return transformedXy - transformedOrigin; +} + const char* inputEventTypeToString(int32_t type) { switch (type) { case AINPUT_EVENT_TYPE_KEY: { diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index b1069497d3..db4228d862 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -30,7 +30,8 @@ namespace android { * fingers * * The pointer controller is responsible for providing synchronization and for tracking - * display orientation changes if needed. + * display orientation changes if needed. It works in the display panel's coordinate space, which + * is the same coordinate space used by InputReader. */ class PointerControllerInterface { protected: diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 15ba45945a..fcb56ef05c 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -188,8 +188,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* if (!changes || (changes & InputReaderConfiguration::CHANGE_DISPLAY_INFO)) { mOrientation = DISPLAY_ORIENTATION_0; - mDisplayWidth = 0; - mDisplayHeight = 0; const bool isOrientedDevice = (mParameters.orientationAware && mParameters.hasAssociatedDisplay); @@ -203,8 +201,6 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* config->getDisplayViewportByType(ViewportType::INTERNAL); if (internalViewport) { mOrientation = getInverseRotation(internalViewport->orientation); - mDisplayWidth = internalViewport->deviceWidth; - mDisplayHeight = internalViewport->deviceHeight; } } @@ -335,14 +331,7 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { - float dx = deltaX; - float dy = deltaY; - // Rotate the delta from InputReader's un-rotated coordinate space to - // PointerController's rotated coordinate space that is oriented with the - // viewport. - rotateDelta(getInverseRotation(mOrientation), &dx, &dy); - - mPointerController->move(dx, dy); + mPointerController->move(deltaX, deltaY); } if (buttonsChanged) { @@ -353,10 +342,6 @@ void CursorInputMapper::sync(nsecs_t when, nsecs_t readTime) { } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); - // Rotate the cursor position that is in PointerController's rotated coordinate space - // to InputReader's un-rotated coordinate space. - rotatePoint(mOrientation, xCursorPosition /*byRef*/, yCursorPosition /*byRef*/, - mDisplayWidth, mDisplayHeight); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition); pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index 88e947f7d5..9a8ca01294 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -105,8 +105,6 @@ private: VelocityControl mWheelYVelocityControl; int32_t mOrientation; - int32_t mDisplayWidth; - int32_t mDisplayHeight; std::shared_ptr mPointerController; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 8c30e38908..31a3d2e172 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -64,26 +64,6 @@ static void rotateDelta(int32_t orientation, float* deltaX, float* deltaY) { } } -// Rotates the given point (x, y) by the supplied orientation. The width and height are the -// dimensions of the surface prior to this rotation being applied. -static void rotatePoint(int32_t orientation, float& x, float& y, int32_t width, int32_t height) { - rotateDelta(orientation, &x, &y); - switch (orientation) { - case DISPLAY_ORIENTATION_90: - y += width; - break; - case DISPLAY_ORIENTATION_180: - x += width; - y += height; - break; - case DISPLAY_ORIENTATION_270: - x += height; - break; - default: - break; - } -} - // Returns true if the pointer should be reported as being down given the specified // button states. This determines whether the event is reported as a touch event. static bool isPointerDown(int32_t buttonState) { diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 3fe6fd130f..913c666a4a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -1668,9 +1668,10 @@ void TouchInputMapper::updateTouchSpots() { mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); - setTouchSpots(mCurrentCookedState.cookedPointerData.pointerCoords, - mCurrentCookedState.cookedPointerData.idToIndex, - mCurrentCookedState.cookedPointerData.touchingIdBits, mViewport.displayId); + mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, + mCurrentCookedState.cookedPointerData.idToIndex, + mCurrentCookedState.cookedPointerData.touchingIdBits, + mViewport.displayId); } bool TouchInputMapper::isTouchScreen() { @@ -2410,9 +2411,10 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u } if (mPointerGesture.currentGestureMode == PointerGesture::Mode::FREEFORM) { - setTouchSpots(mPointerGesture.currentGestureCoords, - mPointerGesture.currentGestureIdToIndex, - mPointerGesture.currentGestureIdBits, mPointerController->getDisplayId()); + mPointerController->setSpots(mPointerGesture.currentGestureCoords, + mPointerGesture.currentGestureIdToIndex, + mPointerGesture.currentGestureIdBits, + mPointerController->getDisplayId()); } } else { mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); @@ -2562,7 +2564,8 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, nsecs_t readTime, u // the pointer is hovering again even if the user is not currently touching // the touch pad. This ensures that a view will receive a fresh hover enter // event after a tap. - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); PointerProperties pointerProperties; pointerProperties.clear(); @@ -2819,12 +2822,13 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the click will occur at the position of the anchor // spot and all other spots will move there. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureMode = PointerGesture::Mode::BUTTON_CLICK_OR_DRAG; mPointerGesture.currentGestureIdBits.clear(); @@ -2850,7 +2854,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP_DRAG) && lastFingerCount == 1) { if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { #if DEBUG_GESTURES @@ -2918,7 +2923,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mPointerGesture.currentGestureMode = PointerGesture::Mode::HOVER; if (mPointerGesture.lastGestureMode == PointerGesture::Mode::TAP) { if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::Mode::TAP_DRAG; @@ -2952,7 +2958,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi // Move the pointer using a relative motion. // When using spots, the hover or drag will occur at the position of the anchor spot. - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -2974,7 +2980,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi down = false; } - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerGesture.currentGestureIdBits.clear(); mPointerGesture.currentGestureIdBits.markBit(mPointerGesture.activeGestureId); @@ -3047,9 +3054,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, bool* outCancelPrevi mCurrentRawState.rawPointerData .getCentroidOfTouchingPointers(&mPointerGesture.referenceTouchX, &mPointerGesture.referenceTouchY); - auto [x, y] = getMouseCursorPosition(); - mPointerGesture.referenceGestureX = x; - mPointerGesture.referenceGestureY = y; + mPointerController->getPosition(&mPointerGesture.referenceGestureX, + &mPointerGesture.referenceGestureY); } // Clear the reference deltas for fingers not yet included in the reference calculation. @@ -3387,13 +3393,15 @@ void TouchInputMapper::dispatchPointerStylus(nsecs_t when, nsecs_t readTime, uin if (!mCurrentCookedState.stylusIdBits.isEmpty()) { uint32_t id = mCurrentCookedState.stylusIdBits.firstMarkedBit(); uint32_t index = mCurrentCookedState.cookedPointerData.idToIndex[id]; - setMouseCursorPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), - mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); + mPointerController + ->setPosition(mCurrentCookedState.cookedPointerData.pointerCoords[index].getX(), + mCurrentCookedState.cookedPointerData.pointerCoords[index].getY()); hovering = mCurrentCookedState.cookedPointerData.hoveringIdBits.hasBit(id); down = !hovering; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[index]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3434,7 +3442,7 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint rotateDelta(mInputDeviceOrientation, &deltaX, &deltaY); mPointerVelocityControl.move(when, &deltaX, &deltaY); - moveMouseCursor(deltaX, deltaY); + mPointerController->move(deltaX, deltaY); } else { mPointerVelocityControl.reset(); } @@ -3442,7 +3450,8 @@ void TouchInputMapper::dispatchPointerMouse(nsecs_t when, nsecs_t readTime, uint down = isPointerDown(mCurrentRawState.buttonState); hovering = !down; - auto [x, y] = getMouseCursorPosition(); + float x, y; + mPointerController->getPosition(&x, &y); mPointerSimple.currentCoords.copyFrom( mCurrentCookedState.cookedPointerData.pointerCoords[currentIndex]); mPointerSimple.currentCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x); @@ -3482,7 +3491,8 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, nsecs_t readTime, uin } int32_t displayId = mPointerController->getDisplayId(); - auto [xCursorPosition, yCursorPosition] = getMouseCursorPosition(); + float xCursorPosition, yCursorPosition; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); if (mPointerSimple.down && !down) { mPointerSimple.down = false; @@ -3648,9 +3658,7 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, nsecs_t readTime, uint32_t p float xCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mDeviceMode == DeviceMode::POINTER) { - auto [x, y] = getMouseCursorPosition(); - xCursorPosition = x; - yCursorPosition = y; + mPointerController->getPosition(&xCursorPosition, &yCursorPosition); } const int32_t displayId = getAssociatedDisplayId().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); @@ -3999,56 +4007,4 @@ std::optional TouchInputMapper::getAssociatedDisplayId() { return std::nullopt; } -void TouchInputMapper::moveMouseCursor(float dx, float dy) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's coordinate - // space that is oriented with the viewport. - rotateDelta(mViewport.orientation, &dx, &dy); - - mPointerController->move(dx, dy); -} - -std::pair TouchInputMapper::getMouseCursorPosition() const { - float x = 0; - float y = 0; - mPointerController->getPosition(&x, &y); - - if (!mViewport.isValid()) return {x, y}; - - // Convert from PointerController's rotated coordinate space that is oriented with the viewport - // to InputReader's un-rotated coordinate space. - const int32_t orientation = getInverseRotation(mViewport.orientation); - rotatePoint(orientation, x, y, mViewport.deviceWidth, mViewport.deviceHeight); - return {x, y}; -} - -void TouchInputMapper::setMouseCursorPosition(float x, float y) const { - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space that is oriented with the viewport. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - mPointerController->setPosition(x, y); -} - -void TouchInputMapper::setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId) { - std::array outSpotCoords{}; - - for (BitSet32 idBits(spotIdBits); !idBits.isEmpty();) { - const uint32_t index = spotIdToIndex[idBits.clearFirstMarkedBit()]; - float x = spotCoords[index].getX(); - float y = spotCoords[index].getY(); - float pressure = spotCoords[index].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); - - // Convert from InputReader's un-rotated coordinate space to PointerController's rotated - // coordinate space. - rotatePoint(mViewport.orientation, x, y, mDisplayWidth, mDisplayHeight); - - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_X, x); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_Y, y); - outSpotCoords[index].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pressure); - } - - mPointerController->setSpots(outSpotCoords.data(), spotIdToIndex, spotIdBits, displayId); -} - } // namespace android diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 496491b62d..9b020a609a 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -803,14 +803,6 @@ private: const char* modeToString(DeviceMode deviceMode); void rotateAndScale(float& x, float& y) const; - - // Wrapper methods for interfacing with PointerController. These are used to convert points - // between the coordinate spaces used by InputReader and PointerController, if they differ. - void moveMouseCursor(float dx, float dy) const; - std::pair getMouseCursorPosition() const; - void setMouseCursorPosition(float x, float y) const; - void setTouchSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, - BitSet32 spotIdBits, int32_t displayId); }; } // namespace android -- cgit v1.2.3-59-g8ed1b From d6a7f22182d88888c6ac2824a888766203d33dfc Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Wed, 24 Nov 2021 15:36:43 +0000 Subject: Reland "Remove x/y offset from MotionEntry" 2b80b386c4ed5a1b3240da32080280b0a1ec9cef Change-Id: I5afb6f7fd25cd13e42a9b4368fa387bf574e5ec0 --- include/input/Input.h | 5 ++--- libs/input/Input.cpp | 5 ----- services/inputflinger/dispatcher/Entry.cpp | 5 +---- services/inputflinger/dispatcher/Entry.h | 3 +-- services/inputflinger/dispatcher/InputDispatcher.cpp | 10 +++++----- services/inputflinger/dispatcher/InputState.cpp | 6 ++---- 6 files changed, 11 insertions(+), 23 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1e06257591..1c79c4a21c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -377,7 +377,6 @@ struct PointerCoords { // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); - void applyOffset(float xOffset, float yOffset); void transform(const ui::Transform& transform); @@ -567,7 +566,7 @@ public: inline float getYOffset() const { return mTransform.ty(); } - inline ui::Transform getTransform() const { return mTransform; } + inline const ui::Transform& getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -583,7 +582,7 @@ public: void setCursorPosition(float x, float y); - ui::Transform getRawTransform() const { return mRawTransform; } + inline const ui::Transform& getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8974b22c86..69ea7dfcb5 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -343,11 +343,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } -void PointerCoords::applyOffset(float xOffset, float yOffset) { - setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); - setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); -} - #ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 1674afd02e..3d0818b738 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -214,7 +214,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xOffset, float yOffset) + const PointerCoords* pointerCoords) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -235,9 +235,6 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } } } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 477781a2ad..0f792967eb 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -184,8 +184,7 @@ struct MotionEntry : EventEntry { int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); std::string getDescription() const override; ~MotionEntry() override; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3fd1c8aa27..04ff599f13 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -378,7 +378,7 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, - pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); + pointerCoords.data()); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -3752,7 +3752,7 @@ std::unique_ptr InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords, 0, 0); + splitPointerProperties, splitPointerCoords); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3978,7 +3978,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords, 0, 0); + args->pointerProperties, args->pointerCoords); if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && @@ -4208,7 +4208,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawXCursorPosition(), motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, 0, 0); + pointerProperties, samplePointerCoords); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { @@ -4228,7 +4228,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), pointerProperties, - samplePointerCoords, 0, 0); + samplePointerCoords); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(nextInjectedEntry)); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 3bb0bc995c..ad3c6159ef 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -296,8 +296,7 @@ std::vector> InputState::synthesizeCancelationEvents memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, - memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); + memento.pointerCoords)); } } return events; @@ -349,8 +348,7 @@ std::vector> InputState::synthesizePointerDownEvents AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + pointerCount, pointerProperties, pointerCoords)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; -- cgit v1.2.3-59-g8ed1b From 092f3a9b75b1fd465373730ca8d6038c89ed9220 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 25 Nov 2021 10:53:27 -0800 Subject: Add getSurfaceRotation API to MotionEvent Add an API to get the current rotation value of the transform of the MotionEvent. Bug: 207771136 Test: atest MotionEventTest Change-Id: I05fb4455d0dcfc0de8c8564473ee8d43ac86c0bd --- include/input/Input.h | 2 ++ libs/input/Input.cpp | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 5242dcb476..29503af65f 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -574,6 +574,8 @@ public: inline ui::Transform getTransform() const { return mTransform; } + int getSurfaceRotation() const; + inline float getXPrecision() const { return mXPrecision; } inline float getYPrecision() const { return mYPrecision; } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index cb93c92310..44487c3a85 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -506,6 +507,24 @@ void MotionEvent::addSample( mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); } +int MotionEvent::getSurfaceRotation() const { + // The surface rotation is the rotation from the window's coordinate space to that of the + // display. Since the event's transform takes display space coordinates to window space, the + // returned surface rotation is the inverse of the rotation for the surface. + switch (mTransform.getOrientation()) { + case ui::Transform::ROT_0: + return DISPLAY_ORIENTATION_0; + case ui::Transform::ROT_90: + return DISPLAY_ORIENTATION_270; + case ui::Transform::ROT_180: + return DISPLAY_ORIENTATION_180; + case ui::Transform::ROT_270: + return DISPLAY_ORIENTATION_90; + default: + return -1; + } +} + float MotionEvent::getXCursorPosition() const { vec2 vals = mTransform.transform(getRawXCursorPosition(), getRawYCursorPosition()); return vals.x; -- cgit v1.2.3-59-g8ed1b From f7c99f3f2404f9605c2801caa20b236a595afa3d Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Fri, 3 Dec 2021 10:37:54 +0000 Subject: Revert "Reland "Remove x/y offset from MotionEntry"" Revert submission 16336498-revert-16306432-revert-16295572-ORWQJPYRMQ-IQBEBXXQNX Reason for revert: b/208849306 Reverted Changes: I5afb6f7fd:Reland "Remove x/y offset from MotionEntry" I68b5619bf:Reland "Input injection: Assume transformed values... Change-Id: Id944e4eb39b26e3ffd51998b315a06e87a2af3d5 --- include/input/Input.h | 5 +++-- libs/input/Input.cpp | 5 +++++ services/inputflinger/dispatcher/Entry.cpp | 5 ++++- services/inputflinger/dispatcher/Entry.h | 3 ++- services/inputflinger/dispatcher/InputDispatcher.cpp | 10 +++++----- services/inputflinger/dispatcher/InputState.cpp | 6 ++++-- 6 files changed, 23 insertions(+), 11 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1c79c4a21c..1e06257591 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -377,6 +377,7 @@ struct PointerCoords { // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); + void applyOffset(float xOffset, float yOffset); void transform(const ui::Transform& transform); @@ -566,7 +567,7 @@ public: inline float getYOffset() const { return mTransform.ty(); } - inline const ui::Transform& getTransform() const { return mTransform; } + inline ui::Transform getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -582,7 +583,7 @@ public: void setCursorPosition(float x, float y); - inline const ui::Transform& getRawTransform() const { return mRawTransform; } + ui::Transform getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 69ea7dfcb5..8974b22c86 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -343,6 +343,11 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } +void PointerCoords::applyOffset(float xOffset, float yOffset) { + setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); + setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); +} + #ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 3d0818b738..1674afd02e 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -214,7 +214,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords) + const PointerCoords* pointerCoords, float xOffset, float yOffset) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -235,6 +235,9 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); + if (xOffset || yOffset) { + this->pointerCoords[i].applyOffset(xOffset, yOffset); + } } } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 0f792967eb..477781a2ad 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -184,7 +184,8 @@ struct MotionEntry : EventEntry { int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, + float xOffset, float yOffset); std::string getDescription() const override; ~MotionEntry() override; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 04ff599f13..3fd1c8aa27 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -378,7 +378,7 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, - pointerCoords.data()); + pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -3752,7 +3752,7 @@ std::unique_ptr InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords); + splitPointerProperties, splitPointerCoords, 0, 0); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3978,7 +3978,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords); + args->pointerProperties, args->pointerCoords, 0, 0); if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && @@ -4208,7 +4208,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawXCursorPosition(), motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords); + pointerProperties, samplePointerCoords, 0, 0); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { @@ -4228,7 +4228,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), pointerProperties, - samplePointerCoords); + samplePointerCoords, 0, 0); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(nextInjectedEntry)); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index ad3c6159ef..3bb0bc995c 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -296,7 +296,8 @@ std::vector> InputState::synthesizeCancelationEvents memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, - memento.pointerCoords)); + memento.pointerCoords, 0 /*xOffset*/, + 0 /*yOffset*/)); } } return events; @@ -348,7 +349,8 @@ std::vector> InputState::synthesizePointerDownEvents AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords)); + pointerCount, pointerProperties, pointerCoords, + 0 /*xOffset*/, 0 /*yOffset*/)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; -- cgit v1.2.3-59-g8ed1b From f355bf982478381f838e8d3318aa67cede2daf10 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 9 Dec 2021 10:43:21 -0800 Subject: Reorder VerifiedKey/MotionEvent structs for hwasan Now, the 'nsecs_t' fields of this struct will no longer be falling on 4-byte boundaries. This prevents a hwasan crash. Bug: 209991446 Test: atest inputflinger_tests Change-Id: If28c7ff66b9495a3d61f590464d2b995afbe0c2b --- include/input/Input.h | 4 ++-- libs/input/Input.cpp | 4 ++-- services/inputflinger/dispatcher/Entry.cpp | 4 ++-- services/inputflinger/tests/InputDispatcher_test.cpp | 6 +++--- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 29503af65f..e1cacac273 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -945,8 +945,8 @@ struct __attribute__((__packed__)) VerifiedInputEvent { */ struct __attribute__((__packed__)) VerifiedKeyEvent : public VerifiedInputEvent { int32_t action; - nsecs_t downTimeNanos; int32_t flags; + nsecs_t downTimeNanos; int32_t keyCode; int32_t scanCode; int32_t metaState; @@ -961,8 +961,8 @@ struct __attribute__((__packed__)) VerifiedMotionEvent : public VerifiedInputEve float rawX; float rawY; int32_t actionMasked; - nsecs_t downTimeNanos; int32_t flags; + nsecs_t downTimeNanos; int32_t metaState; int32_t buttonState; }; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 44487c3a85..f0b97a7ba8 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -192,8 +192,8 @@ VerifiedKeyEvent verifiedKeyEventFromKeyEvent(const KeyEvent& event) { return {{VerifiedInputEvent::Type::KEY, event.getDeviceId(), event.getEventTime(), event.getSource(), event.getDisplayId()}, event.getAction(), - event.getDownTime(), event.getFlags() & VERIFIED_KEY_EVENT_FLAGS, + event.getDownTime(), event.getKeyCode(), event.getScanCode(), event.getMetaState(), @@ -206,8 +206,8 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEvent(const MotionEvent& event) event.getRawX(0), event.getRawY(0), event.getActionMasked(), - event.getDownTime(), event.getFlags() & VERIFIED_MOTION_EVENT_FLAGS, + event.getDownTime(), event.getMetaState(), event.getButtonState()}; } diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index f6bb6a63a7..936ecf9889 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -32,8 +32,8 @@ VerifiedKeyEvent verifiedKeyEventFromKeyEntry(const KeyEntry& entry) { return {{VerifiedInputEvent::Type::KEY, entry.deviceId, entry.eventTime, entry.source, entry.displayId}, entry.action, - entry.downTime, entry.flags & VERIFIED_KEY_EVENT_FLAGS, + entry.downTime, entry.keyCode, entry.scanCode, entry.metaState, @@ -50,8 +50,8 @@ VerifiedMotionEvent verifiedMotionEventFromMotionEntry(const MotionEntry& entry, rawXY.x, rawXY.y, actionMasked, - entry.downTime, entry.flags & VERIFIED_MOTION_EVENT_FLAGS, + entry.downTime, entry.metaState, entry.buttonState}; } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 39c52628d4..2c64271cca 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -3145,8 +3145,8 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_KeyEvent) { const VerifiedKeyEvent& verifiedKey = static_cast(*verified); ASSERT_EQ(keyArgs.action, verifiedKey.action); - ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos); ASSERT_EQ(keyArgs.flags & VERIFIED_KEY_EVENT_FLAGS, verifiedKey.flags); + ASSERT_EQ(keyArgs.downTime, verifiedKey.downTimeNanos); ASSERT_EQ(keyArgs.keyCode, verifiedKey.keyCode); ASSERT_EQ(keyArgs.scanCode, verifiedKey.scanCode); ASSERT_EQ(keyArgs.metaState, verifiedKey.metaState); @@ -3194,8 +3194,8 @@ TEST_F(InputDispatcherTest, VerifyInputEvent_MotionEvent) { EXPECT_EQ(rawXY.x, verifiedMotion.rawX); EXPECT_EQ(rawXY.y, verifiedMotion.rawY); EXPECT_EQ(motionArgs.action & AMOTION_EVENT_ACTION_MASK, verifiedMotion.actionMasked); - EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); EXPECT_EQ(motionArgs.flags & VERIFIED_MOTION_EVENT_FLAGS, verifiedMotion.flags); + EXPECT_EQ(motionArgs.downTime, verifiedMotion.downTimeNanos); EXPECT_EQ(motionArgs.metaState, verifiedMotion.metaState); EXPECT_EQ(motionArgs.buttonState, verifiedMotion.buttonState); } @@ -4179,7 +4179,7 @@ protected: } } - void touchAndAssertPositions(int32_t action, std::vector touchedPoints, + void touchAndAssertPositions(int32_t action, const std::vector& touchedPoints, std::vector expectedPoints) { NotifyMotionArgs motionArgs = generateMotionArgs(action, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, touchedPoints); -- cgit v1.2.3-59-g8ed1b From 5beda769594b8a86439ae5969a8448f7b4c5e522 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Fri, 10 Dec 2021 09:30:08 +0000 Subject: Reland^2 "Remove x/y offset from MotionEntry" f7c99f3f2404f9605c2801caa20b236a595afa3d Bug: 206842332 Change-Id: Id42790e6c1d562c61efb6673407a2861927df40b --- include/input/Input.h | 5 ++--- libs/input/Input.cpp | 5 ----- services/inputflinger/dispatcher/Entry.cpp | 5 +---- services/inputflinger/dispatcher/Entry.h | 3 +-- services/inputflinger/dispatcher/InputDispatcher.cpp | 10 +++++----- services/inputflinger/dispatcher/InputState.cpp | 6 ++---- 6 files changed, 11 insertions(+), 23 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 1e06257591..1c79c4a21c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -377,7 +377,6 @@ struct PointerCoords { // window scale. The global scale will be applied to TOUCH/TOOL_MAJOR/MINOR // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); - void applyOffset(float xOffset, float yOffset); void transform(const ui::Transform& transform); @@ -567,7 +566,7 @@ public: inline float getYOffset() const { return mTransform.ty(); } - inline ui::Transform getTransform() const { return mTransform; } + inline const ui::Transform& getTransform() const { return mTransform; } inline float getXPrecision() const { return mXPrecision; } @@ -583,7 +582,7 @@ public: void setCursorPosition(float x, float y); - ui::Transform getRawTransform() const { return mRawTransform; } + inline const ui::Transform& getRawTransform() const { return mRawTransform; } static inline bool isValidCursorPosition(float x, float y) { return !isnan(x) && !isnan(y); } diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 8974b22c86..69ea7dfcb5 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -343,11 +343,6 @@ void PointerCoords::scale(float globalScaleFactor, float windowXScale, float win scaleAxisValue(*this, AMOTION_EVENT_AXIS_RELATIVE_Y, windowYScale); } -void PointerCoords::applyOffset(float xOffset, float yOffset) { - setAxisValue(AMOTION_EVENT_AXIS_X, getX() + xOffset); - setAxisValue(AMOTION_EVENT_AXIS_Y, getY() + yOffset); -} - #ifdef __linux__ status_t PointerCoords::readFromParcel(Parcel* parcel) { bits = parcel->readInt64(); diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index 1674afd02e..3d0818b738 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -214,7 +214,7 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, const PointerProperties* pointerProperties, - const PointerCoords* pointerCoords, float xOffset, float yOffset) + const PointerCoords* pointerCoords) : EventEntry(id, Type::MOTION, eventTime, policyFlags), deviceId(deviceId), source(source), @@ -235,9 +235,6 @@ MotionEntry::MotionEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32 for (uint32_t i = 0; i < pointerCount; i++) { this->pointerProperties[i].copyFrom(pointerProperties[i]); this->pointerCoords[i].copyFrom(pointerCoords[i]); - if (xOffset || yOffset) { - this->pointerCoords[i].applyOffset(xOffset, yOffset); - } } } diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h index 477781a2ad..0f792967eb 100644 --- a/services/inputflinger/dispatcher/Entry.h +++ b/services/inputflinger/dispatcher/Entry.h @@ -184,8 +184,7 @@ struct MotionEntry : EventEntry { int32_t metaState, int32_t buttonState, MotionClassification classification, int32_t edgeFlags, float xPrecision, float yPrecision, float xCursorPosition, float yCursorPosition, nsecs_t downTime, uint32_t pointerCount, - const PointerProperties* pointerProperties, const PointerCoords* pointerCoords, - float xOffset, float yOffset); + const PointerProperties* pointerProperties, const PointerCoords* pointerCoords); std::string getDescription() const override; ~MotionEntry() override; diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3fd1c8aa27..04ff599f13 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -378,7 +378,7 @@ std::unique_ptr createDispatchEntry(const InputTarget& inputTarge motionEntry.yPrecision, motionEntry.xCursorPosition, motionEntry.yCursorPosition, motionEntry.downTime, motionEntry.pointerCount, motionEntry.pointerProperties, - pointerCoords.data(), 0 /* xOffset */, 0 /* yOffset */); + pointerCoords.data()); if (motionEntry.injectionState) { combinedMotionEntry->injectionState = motionEntry.injectionState; @@ -3752,7 +3752,7 @@ std::unique_ptr InputDispatcher::splitMotionEvent( originalMotionEntry.xCursorPosition, originalMotionEntry.yCursorPosition, originalMotionEntry.downTime, splitPointerCount, - splitPointerProperties, splitPointerCoords, 0, 0); + splitPointerProperties, splitPointerCoords); if (originalMotionEntry.injectionState) { splitMotionEntry->injectionState = originalMotionEntry.injectionState; @@ -3978,7 +3978,7 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { args->xPrecision, args->yPrecision, args->xCursorPosition, args->yCursorPosition, args->downTime, args->pointerCount, - args->pointerProperties, args->pointerCoords, 0, 0); + args->pointerProperties, args->pointerCoords); if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID && IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER && @@ -4208,7 +4208,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawXCursorPosition(), motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), - pointerProperties, samplePointerCoords, 0, 0); + pointerProperties, samplePointerCoords); transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(injectedEntry)); for (size_t i = motionEvent.getHistorySize(); i > 0; i--) { @@ -4228,7 +4228,7 @@ InputEventInjectionResult InputDispatcher::injectInputEvent( motionEvent.getRawYCursorPosition(), motionEvent.getDownTime(), uint32_t(pointerCount), pointerProperties, - samplePointerCoords, 0, 0); + samplePointerCoords); transformMotionEntryForInjectionLocked(*nextInjectedEntry, motionEvent.getTransform()); injectedEntries.push(std::move(nextInjectedEntry)); diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp index 3bb0bc995c..ad3c6159ef 100644 --- a/services/inputflinger/dispatcher/InputState.cpp +++ b/services/inputflinger/dispatcher/InputState.cpp @@ -296,8 +296,7 @@ std::vector> InputState::synthesizeCancelationEvents memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, memento.pointerCount, memento.pointerProperties, - memento.pointerCoords, 0 /*xOffset*/, - 0 /*yOffset*/)); + memento.pointerCoords)); } } return events; @@ -349,8 +348,7 @@ std::vector> InputState::synthesizePointerDownEvents AMOTION_EVENT_EDGE_FLAG_NONE, memento.xPrecision, memento.yPrecision, memento.xCursorPosition, memento.yCursorPosition, memento.downTime, - pointerCount, pointerProperties, pointerCoords, - 0 /*xOffset*/, 0 /*yOffset*/)); + pointerCount, pointerProperties, pointerCoords)); } memento.firstNewPointerIdx = INVALID_POINTER_INDEX; -- cgit v1.2.3-59-g8ed1b From 90bc949048f2b91fa841cb8272381af8c301c055 Mon Sep 17 00:00:00 2001 From: Philip Junker Date: Fri, 10 Dec 2021 18:39:42 +0100 Subject: Fix KeyCharacterMap overlays by reloading base overlay before applying another overlay. Add all fields to parcel (mLoadFileName, mLayoutOverlayApplied, mKeysByScanCode, mKeysByUsageCode). Add all fields to equality operator. Add tests to libinput_tests. Bug: 210090260 Test: Manually verified that layout changes correctly. Test: atest libinput_tests Change-Id: I1777b87e5c2ce8a0dbd42ad48748ea9e669c9139 --- include/input/KeyCharacterMap.h | 18 +- libs/input/KeyCharacterMap.cpp | 136 +++++++++--- libs/input/tests/Android.bp | 3 +- libs/input/tests/InputDevice_test.cpp | 50 +++++ libs/input/tests/data/english_us.kcm | 311 +++++++++++++++++++++++++++ libs/input/tests/data/french.kcm | 336 ++++++++++++++++++++++++++++++ libs/input/tests/data/german.kcm | 336 ++++++++++++++++++++++++++++++ services/inputflinger/reader/EventHub.cpp | 9 +- 8 files changed, 1163 insertions(+), 36 deletions(-) create mode 100644 libs/input/tests/data/english_us.kcm create mode 100644 libs/input/tests/data/french.kcm create mode 100644 libs/input/tests/data/german.kcm (limited to 'include/input') diff --git a/include/input/KeyCharacterMap.h b/include/input/KeyCharacterMap.h index 451ca3c6cd..f6f8939b7a 100644 --- a/include/input/KeyCharacterMap.h +++ b/include/input/KeyCharacterMap.h @@ -84,7 +84,7 @@ public: const std::string getLoadFileName() const; - /* Combines this key character map with an overlay. */ + /* Combines this key character map with the provided overlay. */ void combine(const KeyCharacterMap& overlay); /* Gets the keyboard type. */ @@ -144,6 +144,8 @@ public: bool operator==(const KeyCharacterMap& other) const; + bool operator!=(const KeyCharacterMap& other) const; + KeyCharacterMap(const KeyCharacterMap& other); virtual ~KeyCharacterMap(); @@ -230,11 +232,12 @@ private: KeyedVector mKeys; KeyboardType mType; std::string mLoadFileName; + bool mLayoutOverlayApplied; KeyedVector mKeysByScanCode; KeyedVector mKeysByUsageCode; - KeyCharacterMap(); + KeyCharacterMap(const std::string& filename); bool getKey(int32_t keyCode, const Key** outKey) const; bool getKeyBehavior(int32_t keyCode, int32_t metaState, @@ -243,8 +246,6 @@ private: bool findKey(char16_t ch, int32_t* outKeyCode, int32_t* outMetaState) const; - static base::Result> load(Tokenizer* tokenizer, Format format); - static void addKey(Vector& outEvents, int32_t deviceId, int32_t keyCode, int32_t metaState, bool down, nsecs_t time); static void addMetaKeys(Vector& outEvents, @@ -264,6 +265,15 @@ private: int32_t deviceId, int32_t metaState, nsecs_t time, int32_t keyCode, int32_t keyMetaState, int32_t* currentMetaState); + + /* Clears all data stored in this key character map */ + void clear(); + + /* Loads the KeyCharacterMap provided by the tokenizer into this instance. */ + status_t load(Tokenizer* tokenizer, Format format); + + /* Reloads the data from mLoadFileName and unapplies any overlay. */ + status_t reloadBaseFromFile(); }; } // namespace android diff --git a/libs/input/KeyCharacterMap.cpp b/libs/input/KeyCharacterMap.cpp index 3baeb55009..2039fa6553 100644 --- a/libs/input/KeyCharacterMap.cpp +++ b/libs/input/KeyCharacterMap.cpp @@ -86,10 +86,13 @@ static String8 toString(const char16_t* chars, size_t numChars) { // --- KeyCharacterMap --- -KeyCharacterMap::KeyCharacterMap() : mType(KeyboardType::UNKNOWN) {} +KeyCharacterMap::KeyCharacterMap(const std::string& filename) + : mType(KeyboardType::UNKNOWN), mLoadFileName(filename) {} KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) : mType(other.mType), + mLoadFileName(other.mLoadFileName), + mLayoutOverlayApplied(other.mLayoutOverlayApplied), mKeysByScanCode(other.mKeysByScanCode), mKeysByUsageCode(other.mKeysByUsageCode) { for (size_t i = 0; i < other.mKeys.size(); i++) { @@ -98,16 +101,19 @@ KeyCharacterMap::KeyCharacterMap(const KeyCharacterMap& other) } KeyCharacterMap::~KeyCharacterMap() { - for (size_t i = 0; i < mKeys.size(); i++) { - Key* key = mKeys.editValueAt(i); - delete key; - } + clear(); } bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { if (mType != other.mType) { return false; } + if (mLoadFileName != other.mLoadFileName) { + return false; + } + if (mLayoutOverlayApplied != other.mLayoutOverlayApplied) { + return false; + } if (mKeys.size() != other.mKeys.size() || mKeysByScanCode.size() != other.mKeysByScanCode.size() || mKeysByUsageCode.size() != other.mKeysByUsageCode.size()) { @@ -146,6 +152,10 @@ bool KeyCharacterMap::operator==(const KeyCharacterMap& other) const { return true; } +bool KeyCharacterMap::operator!=(const KeyCharacterMap& other) const { + return !(*this == other); +} + base::Result> KeyCharacterMap::load(const std::string& filename, Format format) { Tokenizer* tokenizer; @@ -153,12 +163,18 @@ base::Result> KeyCharacterMap::load(const std:: if (status) { return Errorf("Error {} opening key character map file {}.", status, filename.c_str()); } + std::shared_ptr map = + std::shared_ptr(new KeyCharacterMap(filename)); + if (!map.get()) { + ALOGE("Error allocating key character map."); + return Errorf("Error allocating key character map."); + } std::unique_ptr t(tokenizer); - auto ret = load(t.get(), format); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + status = map->load(t.get(), format); + if (status == OK) { + return map; } - return ret; + return Errorf("Load KeyCharacterMap failed {}.", status); } base::Result> KeyCharacterMap::loadContents( @@ -169,40 +185,67 @@ base::Result> KeyCharacterMap::loadContents( ALOGE("Error %d opening key character map.", status); return Errorf("Error {} opening key character map.", status); } + std::shared_ptr map = + std::shared_ptr(new KeyCharacterMap(filename)); + if (!map.get()) { + ALOGE("Error allocating key character map."); + return Errorf("Error allocating key character map."); + } std::unique_ptr t(tokenizer); - auto ret = load(t.get(), format); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + status = map->load(t.get(), format); + if (status == OK) { + return map; } - return ret; + return Errorf("Load KeyCharacterMap failed {}.", status); } -base::Result> KeyCharacterMap::load(Tokenizer* tokenizer, - Format format) { +status_t KeyCharacterMap::load(Tokenizer* tokenizer, Format format) { status_t status = OK; - std::shared_ptr map = std::shared_ptr(new KeyCharacterMap()); - if (!map.get()) { - ALOGE("Error allocating key character map."); - return Errorf("Error allocating key character map."); - } #if DEBUG_PARSER_PERFORMANCE nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); #endif - Parser parser(map.get(), tokenizer, format); + Parser parser(this, tokenizer, format); status = parser.parse(); #if DEBUG_PARSER_PERFORMANCE nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", tokenizer->getFilename().string(), tokenizer->getLineNumber(), elapsedTime / 1000000.0); #endif - if (status == OK) { - return map; + if (status != OK) { + ALOGE("Loading KeyCharacterMap failed with status %s", statusToString(status).c_str()); } + return status; +} - return Errorf("Load KeyCharacterMap failed {}.", status); +void KeyCharacterMap::clear() { + mKeysByScanCode.clear(); + mKeysByUsageCode.clear(); + for (size_t i = 0; i < mKeys.size(); i++) { + Key* key = mKeys.editValueAt(i); + delete key; + } + mKeys.clear(); + mLayoutOverlayApplied = false; + mType = KeyboardType::UNKNOWN; +} + +status_t KeyCharacterMap::reloadBaseFromFile() { + clear(); + Tokenizer* tokenizer; + status_t status = Tokenizer::open(String8(mLoadFileName.c_str()), &tokenizer); + if (status) { + ALOGE("Error %s opening key character map file %s.", statusToString(status).c_str(), + mLoadFileName.c_str()); + return status; + } + std::unique_ptr t(tokenizer); + return load(t.get(), KeyCharacterMap::Format::BASE); } void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { + if (mLayoutOverlayApplied) { + reloadBaseFromFile(); + } for (size_t i = 0; i < overlay.mKeys.size(); i++) { int32_t keyCode = overlay.mKeys.keyAt(i); Key* key = overlay.mKeys.valueAt(i); @@ -224,7 +267,7 @@ void KeyCharacterMap::combine(const KeyCharacterMap& overlay) { mKeysByUsageCode.replaceValueFor(overlay.mKeysByUsageCode.keyAt(i), overlay.mKeysByUsageCode.valueAt(i)); } - mLoadFileName = overlay.mLoadFileName; + mLayoutOverlayApplied = true; } KeyCharacterMap::KeyboardType KeyCharacterMap::getKeyboardType() const { @@ -636,8 +679,11 @@ std::shared_ptr KeyCharacterMap::readFromParcel(Parcel* parcel) ALOGE("%s: Null parcel", __func__); return nullptr; } - std::shared_ptr map = std::shared_ptr(new KeyCharacterMap()); + std::string loadFileName = parcel->readCString(); + std::shared_ptr map = + std::shared_ptr(new KeyCharacterMap(loadFileName)); map->mType = static_cast(parcel->readInt32()); + map->mLayoutOverlayApplied = parcel->readBool(); size_t numKeys = parcel->readInt32(); if (parcel->errorCheck()) { return nullptr; @@ -687,6 +733,30 @@ std::shared_ptr KeyCharacterMap::readFromParcel(Parcel* parcel) return nullptr; } } + size_t numKeysByScanCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return nullptr; + } + for (size_t i = 0; i < numKeysByScanCode; i++) { + int32_t key = parcel->readInt32(); + int32_t value = parcel->readInt32(); + map->mKeysByScanCode.add(key, value); + if (parcel->errorCheck()) { + return nullptr; + } + } + size_t numKeysByUsageCode = parcel->readInt32(); + if (parcel->errorCheck()) { + return nullptr; + } + for (size_t i = 0; i < numKeysByUsageCode; i++) { + int32_t key = parcel->readInt32(); + int32_t value = parcel->readInt32(); + map->mKeysByUsageCode.add(key, value); + if (parcel->errorCheck()) { + return nullptr; + } + } return map; } @@ -695,7 +765,9 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { ALOGE("%s: Null parcel", __func__); return; } + parcel->writeCString(mLoadFileName.c_str()); parcel->writeInt32(static_cast(mType)); + parcel->writeBool(mLayoutOverlayApplied); size_t numKeys = mKeys.size(); parcel->writeInt32(numKeys); @@ -715,6 +787,18 @@ void KeyCharacterMap::writeToParcel(Parcel* parcel) const { } parcel->writeInt32(0); } + size_t numKeysByScanCode = mKeysByScanCode.size(); + parcel->writeInt32(numKeysByScanCode); + for (size_t i = 0; i < numKeysByScanCode; i++) { + parcel->writeInt32(mKeysByScanCode.keyAt(i)); + parcel->writeInt32(mKeysByScanCode.valueAt(i)); + } + size_t numKeysByUsageCode = mKeysByUsageCode.size(); + parcel->writeInt32(numKeysByUsageCode); + for (size_t i = 0; i < numKeysByUsageCode; i++) { + parcel->writeInt32(mKeysByUsageCode.keyAt(i)); + parcel->writeInt32(mKeysByUsageCode.valueAt(i)); + } } #endif // __linux__ diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 18ab1cb522..6ffe8518b6 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -37,6 +37,7 @@ cc_test { "libui", "libutils", ], + data: ["data/*.kcm"], test_suites: ["device-tests"], } @@ -59,5 +60,5 @@ cc_library_static { "libbinder", "libui", "libbase", - ] + ], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index f8f2f4e931..61e88df11d 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -20,6 +20,7 @@ #include #include #include +#include "android-base/file.h" namespace android { @@ -82,4 +83,53 @@ TEST_F(InputDeviceKeyMapTest, keyCharacterMapParcelingTest) { ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); } +TEST_F(InputDeviceKeyMapTest, keyCharacterMapWithOverlayParcelingTest) { + Parcel parcel; + std::string overlayPath = base::GetExecutableDirectory() + "/data/german.kcm"; + base::Result> overlay = + KeyCharacterMap::load(overlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(overlay.ok()) << "Cannot load KeyCharacterMap at " << overlayPath; + mKeyMap.keyCharacterMap->combine(*overlay->get()); + mKeyMap.keyCharacterMap->writeToParcel(&parcel); + parcel.setDataPosition(0); + std::shared_ptr map = KeyCharacterMap::readFromParcel(&parcel); + ASSERT_EQ(*map, *mKeyMap.keyCharacterMap); +} + +TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { + std::string frenchOverlayPath = base::GetExecutableDirectory() + "/data/french.kcm"; + std::string englishOverlayPath = base::GetExecutableDirectory() + "/data/english_us.kcm"; + std::string germanOverlayPath = base::GetExecutableDirectory() + "/data/german.kcm"; + base::Result> frenchOverlay = + KeyCharacterMap::load(frenchOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(frenchOverlay.ok()) << "Cannot load KeyCharacterMap at " << frenchOverlayPath; + base::Result> englishOverlay = + KeyCharacterMap::load(englishOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(englishOverlay.ok()) << "Cannot load KeyCharacterMap at " << englishOverlayPath; + base::Result> germanOverlay = + KeyCharacterMap::load(germanOverlayPath, KeyCharacterMap::Format::OVERLAY); + ASSERT_TRUE(germanOverlay.ok()) << "Cannot load KeyCharacterMap at " << germanOverlayPath; + + // Apply the French overlay + mKeyMap.keyCharacterMap->combine(*frenchOverlay->get()); + // Copy the result for later + std::shared_ptr frenchOverlaidKeyCharacterMap = + std::make_shared(*mKeyMap.keyCharacterMap); + + // Apply the English overlay + mKeyMap.keyCharacterMap->combine(*englishOverlay->get()); + // Verify that the result is different from the French overlay result + ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); + + // Apply the German overlay + mKeyMap.keyCharacterMap->combine(*germanOverlay->get()); + // Verify that the result is different from the French overlay result + ASSERT_NE(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); + + // Apply the French overlay + mKeyMap.keyCharacterMap->combine(*frenchOverlay->get()); + // Verify that the result is the same like after applying it initially + ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); +} + } // namespace android diff --git a/libs/input/tests/data/english_us.kcm b/libs/input/tests/data/english_us.kcm new file mode 100644 index 0000000000..d0ef027f85 --- /dev/null +++ b/libs/input/tests/data/english_us.kcm @@ -0,0 +1,311 @@ +# Copyright (C) 2021 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. + +# +# English (US) keyboard layout. +# Unlike the default (generic) keyboard layout, English (US) does not contain any +# special ALT characters. +# + +type OVERLAY + +### ROW 1 + +key GRAVE { + label: '`' + base: '`' + shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '@' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '^' +} + +key 7 { + label: '7' + base: '7' + shift: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '[' + base: '[' + shift: '{' +} + +key RIGHT_BRACKET { + label: ']' + base: ']' + shift: '}' +} + +key BACKSLASH { + label: '\\' + base: '\\' + shift: '|' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: ':' +} + +key APOSTROPHE { + label: '\'' + base: '\'' + shift: '"' +} + +### ROW 4 + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key COMMA { + label: ',' + base: ',' + shift: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: '>' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +} \ No newline at end of file diff --git a/libs/input/tests/data/french.kcm b/libs/input/tests/data/french.kcm new file mode 100644 index 0000000000..db69ea0430 --- /dev/null +++ b/libs/input/tests/data/french.kcm @@ -0,0 +1,336 @@ +# Copyright (C) 2021 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. + +# +# French keyboard layout, AZERTY style. +# + +type OVERLAY + +map key 16 A +map key 17 Z +map key 30 Q +map key 39 M +map key 44 W +map key 50 COMMA +map key 51 SEMICOLON +map key 86 PLUS + +### ROW 1 + +key GRAVE { + label: '\u00b2' + base: '\u00b2' +} + +key 1 { + label: '1' + base: '&' + shift: '1' +} + +key 2 { + label: '2' + base: '\u00e9' + shift: '2' + ralt: '~' +} + +key 3 { + label: '3' + base: '"' + shift: '3' + ralt: '#' +} + +key 4 { + label: '4' + base: '\'' + shift: '4' + ralt: '{' +} + +key 5 { + label: '5' + base: '(' + shift: '5' + ralt: '[' +} + +key 6 { + label: '6' + base: '-' + shift: '6' + ralt: '|' +} + +key 7 { + label: '7' + base: '\u00e8' + shift: '7' + ralt: '`' +} + +key 8 { + label: '8' + base: '_' + shift: '8' + ralt: '\\' +} + +key 9 { + label: '9' + base: '\u00e7' + shift: '9' + ralt: '^' +} + +key 0 { + label: '0' + base: '\u00e0' + shift: '0' + ralt: '@' +} + +key MINUS { + label: ')' + base: ')' + shift: '\u00b0' + ralt: ']' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '}' +} + +### ROW 2 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u02c6' + base: '\u0302' + shift: '\u0308' +} + +key RIGHT_BRACKET { + label: '$' + base: '$' + shift: '\u00a3' + ralt: '\u00a4' +} + +### ROW 3 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key APOSTROPHE { + label: '\u00f9' + base: '\u00f9' + shift: '%' +} + +key BACKSLASH { + label: '*' + base: '*' + shift: '\u00b5' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key COMMA { + label: ',' + base: ',' + shift: '?' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: '.' +} + +key PERIOD { + label: ':' + base: ':' + shift: '/' +} + +key SLASH { + label: '!' + base: '!' + shift: '\u00a7' +} \ No newline at end of file diff --git a/libs/input/tests/data/german.kcm b/libs/input/tests/data/german.kcm new file mode 100644 index 0000000000..2fbc5e54e3 --- /dev/null +++ b/libs/input/tests/data/german.kcm @@ -0,0 +1,336 @@ +# Copyright (C) 2021 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. + +# +# German keyboard layout, QWERTZ style. +# + +type OVERLAY + +map key 12 SLASH # � ? \ +map key 21 Z +map key 44 Y +map key 53 MINUS # - _ +map key 86 PLUS # < > | + +map key usage 32 A # for testing purposes only +map key usage 67 B # for testing purposes only + +### ROW 1 + +key GRAVE { + label: '^' + base: '^' + shift: '\u00b0' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '"' + ralt: '\u00b2' +} + +key 3 { + label: '3' + base: '3' + shift: '\u00a7' + ralt: '\u00b3' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '&' +} + +key 7 { + label: '7' + base: '7' + shift: '/' + ralt: '{' +} + +key 8 { + label: '8' + base: '8' + shift: '(' + ralt: '[' +} + +key 9 { + label: '9' + base: '9' + shift: ')' + ralt: ']' +} + +key 0 { + label: '0' + base: '0' + shift: '=' + ralt: '}' +} + +key SLASH { + label: '\u00df' + base: '\u00df' + shift: '?' + ralt: '\\' +} + +key EQUALS { + label: '\u00b4' + base: '\u0301' + shift: '\u0300' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ralt: '@' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u00dc' + base: '\u00fc' + shift, capslock: '\u00dc' +} + +key RIGHT_BRACKET { + label: '+' + base: '+' + shift: '*' + ralt: '~' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: '\u00d6' + base: '\u00f6' + shift, capslock: '\u00d6' +} + +key APOSTROPHE { + label: '\u00c4' + base: '\u00e4' + shift, capslock: '\u00c4' +} + +key BACKSLASH { + label: '#' + base: '#' + shift: '\'' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' + ralt: '|' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ralt: '\u00b5' +} + +key COMMA { + label: ',' + base: ',' + shift: ';' +} + +key PERIOD { + label: '.' + base: '.' + shift: ':' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index d10f8b6605..110f5f7f12 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -1262,12 +1262,11 @@ const std::shared_ptr EventHub::getKeyCharacterMap(int32_t devi bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, std::shared_ptr map) { std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && map != nullptr && device->keyMap.keyCharacterMap != nullptr) { - device->keyMap.keyCharacterMap->combine(*map); - device->keyMap.keyCharacterMapFile = device->keyMap.keyCharacterMap->getLoadFileName(); - return true; + if (device == nullptr || map == nullptr || device->keyMap.keyCharacterMap == nullptr) { + return false; } - return false; + device->keyMap.keyCharacterMap->combine(*map); + return true; } static std::string generateDescriptor(InputDeviceIdentifier& identifier) { -- cgit v1.2.3-59-g8ed1b From 6dbd0ce22d86e487b000d4eac75af35c1ffec58e Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 13 Jan 2022 01:24:14 -0800 Subject: Use std::vector in Input.h Convert old code from android's Vector to std::vector. Bug: 167946763 Test: atest libinput_tests Change-Id: I5c53583f6f1e5d577882d94d356f23bdd516be1e --- include/input/Input.h | 12 +++++------ libs/input/Input.cpp | 33 +++++++++++++++++-------------- services/inputflinger/InputClassifier.cpp | 2 -- services/inputflinger/InputManager.h | 1 - 4 files changed, 23 insertions(+), 25 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index ddff144954..f4147a0409 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -31,10 +31,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -88,7 +86,7 @@ enum { */ AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40, -#ifdef __linux__ +#if defined(__linux__) /** * This event was generated or modified by accessibility service. */ @@ -799,11 +797,11 @@ public: // Low-level accessors. inline const PointerProperties* getPointerProperties() const { - return mPointerProperties.array(); + return mPointerProperties.data(); } inline const nsecs_t* getSampleEventTimes() const { return mSampleEventTimes.data(); } inline const PointerCoords* getSamplePointerCoords() const { - return mSamplePointerCoords.array(); + return mSamplePointerCoords.data(); } static const char* getLabel(int32_t axis); @@ -834,9 +832,9 @@ protected: float mRawYCursorPosition; ui::Transform mRawTransform; nsecs_t mDownTime; - Vector mPointerProperties; + std::vector mPointerProperties; std::vector mSampleEventTimes; - Vector mSamplePointerCoords; + std::vector mSamplePointerCoords; }; /* diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 84dba84e2b..3073d94dbe 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -456,7 +456,8 @@ void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, int3 mRawTransform = rawTransform; mDownTime = downTime; mPointerProperties.clear(); - mPointerProperties.appendArray(pointerProperties, pointerCount); + mPointerProperties.insert(mPointerProperties.end(), &pointerProperties[0], + &pointerProperties[pointerCount]); mSampleEventTimes.clear(); mSamplePointerCoords.clear(); addSample(eventTime, pointerCoords); @@ -490,8 +491,10 @@ void MotionEvent::copyFrom(const MotionEvent* other, bool keepHistory) { mSamplePointerCoords.clear(); size_t pointerCount = other->getPointerCount(); size_t historySize = other->getHistorySize(); - mSamplePointerCoords.appendArray(other->mSamplePointerCoords.array() - + (historySize * pointerCount), pointerCount); + mSamplePointerCoords + .insert(mSamplePointerCoords.end(), + &other->mSamplePointerCoords[historySize * pointerCount], + &other->mSamplePointerCoords[historySize * pointerCount + pointerCount]); } } @@ -499,7 +502,8 @@ void MotionEvent::addSample( int64_t eventTime, const PointerCoords* pointerCoords) { mSampleEventTimes.push_back(eventTime); - mSamplePointerCoords.appendArray(pointerCoords, getPointerCount()); + mSamplePointerCoords.insert(mSamplePointerCoords.end(), &pointerCoords[0], + &pointerCoords[getPointerCount()]); } int MotionEvent::getSurfaceRotation() const { @@ -569,7 +573,7 @@ float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { size_t pointerCount = mPointerProperties.size(); for (size_t i = 0; i < pointerCount; i++) { - if (mPointerProperties.itemAt(i).id == pointerId) { + if (mPointerProperties[i].id == pointerId) { return i; } } @@ -591,8 +595,7 @@ void MotionEvent::scale(float globalScaleFactor) { size_t numSamples = mSamplePointerCoords.size(); for (size_t i = 0; i < numSamples; i++) { - mSamplePointerCoords.editItemAt(i).scale(globalScaleFactor, globalScaleFactor, - globalScaleFactor); + mSamplePointerCoords[i].scale(globalScaleFactor, globalScaleFactor, globalScaleFactor); } } @@ -686,15 +689,15 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { mDownTime = parcel->readInt64(); mPointerProperties.clear(); - mPointerProperties.setCapacity(pointerCount); + mPointerProperties.reserve(pointerCount); mSampleEventTimes.clear(); mSampleEventTimes.reserve(sampleCount); mSamplePointerCoords.clear(); - mSamplePointerCoords.setCapacity(sampleCount * pointerCount); + mSamplePointerCoords.reserve(sampleCount * pointerCount); for (size_t i = 0; i < pointerCount; i++) { - mPointerProperties.push(); - PointerProperties& properties = mPointerProperties.editTop(); + mPointerProperties.push_back({}); + PointerProperties& properties = mPointerProperties.back(); properties.id = parcel->readInt32(); properties.toolType = parcel->readInt32(); } @@ -703,8 +706,8 @@ status_t MotionEvent::readFromParcel(Parcel* parcel) { sampleCount--; mSampleEventTimes.push_back(parcel->readInt64()); for (size_t i = 0; i < pointerCount; i++) { - mSamplePointerCoords.push(); - status_t status = mSamplePointerCoords.editTop().readFromParcel(parcel); + mSamplePointerCoords.push_back({}); + status_t status = mSamplePointerCoords.back().readFromParcel(parcel); if (status) { return status; } @@ -750,12 +753,12 @@ status_t MotionEvent::writeToParcel(Parcel* parcel) const { parcel->writeInt64(mDownTime); for (size_t i = 0; i < pointerCount; i++) { - const PointerProperties& properties = mPointerProperties.itemAt(i); + const PointerProperties& properties = mPointerProperties[i]; parcel->writeInt32(properties.id); parcel->writeInt32(properties.toolType); } - const PointerCoords* pc = mSamplePointerCoords.array(); + const PointerCoords* pc = mSamplePointerCoords.data(); for (size_t h = 0; h < sampleCount; h++) { parcel->writeInt64(mSampleEventTimes[h]); for (size_t i = 0; i < pointerCount; i++) { diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 19cad7b9ad..6c4b11e0e5 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -29,8 +29,6 @@ #endif #include -#include - #define INDENT1 " " #define INDENT2 " " #define INDENT3 " " diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index a6baf2f56d..e00028364a 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -33,7 +33,6 @@ #include #include #include -#include using android::os::BnInputFlinger; -- cgit v1.2.3-59-g8ed1b From f8437960340854120486a547f0687b661629c3d9 Mon Sep 17 00:00:00 2001 From: Philip Junker Date: Tue, 25 Jan 2022 21:20:19 +0100 Subject: Set keyboard type to maximum of all sub devices. Bug: 216328642 Test: atest KeyboardLayoutChangeTest Change-Id: Id01a7ea1c766dad0f5c6bb36f61d71a9eafa2d10 --- include/android/input.h | 1 + include/input/InputDevice.h | 2 +- libs/input/InputDevice.cpp | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) (limited to 'include/input') diff --git a/include/android/input.h b/include/android/input.h index fbd61b53f4..e6ad943f55 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -877,6 +877,7 @@ enum { * Keyboard types. * * Refer to the documentation on android.view.InputDevice for more details. + * Note: When adding a new keyboard type here InputDeviceInfo::setKeyboardType needs to be updated. */ enum { /** none */ diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index 22aae196c6..c4f03c9119 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -235,7 +235,7 @@ public: void addBatteryInfo(const InputDeviceBatteryInfo& info); void addLightInfo(const InputDeviceLightInfo& info); - inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } + void setKeyboardType(int32_t keyboardType); inline int32_t getKeyboardType() const { return mKeyboardType; } inline void setKeyCharacterMap(const std::shared_ptr value) { diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index ac84627b3f..0bee1b6f2a 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -252,6 +252,13 @@ void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo& info) { mLights.insert_or_assign(info.id, info); } +void InputDeviceInfo::setKeyboardType(int32_t keyboardType) { + static_assert(AINPUT_KEYBOARD_TYPE_NONE < AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC); + static_assert(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC < AINPUT_KEYBOARD_TYPE_ALPHABETIC); + // There can be multiple subdevices with different keyboard types, set it to the highest type + mKeyboardType = std::max(mKeyboardType, keyboardType); +} + std::vector InputDeviceInfo::getSensors() { std::vector infos; infos.reserve(mSensors.size()); -- cgit v1.2.3-59-g8ed1b From 0174738c28994b2c710c52cf0ebcfc51013ed833 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 20 Jan 2022 13:23:27 -0800 Subject: Add static_asserts for the size of InputMessage Inside InputTransport.cpp, we are reading and writing InputMessages. This is done in the following way: read ::recv(getFd(), msg, sizeof(InputMessage), MSG_DONTWAIT); write ::send(getFd(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); We are sending a variable-length message across the socket, and receiving a maximum of sizeof(InputMessage) when reading it. In this CL, we are adding asserts on the _maximum_ length of the message that we would send across the socket. Since we typically only have a few pointers at most, while MAX_POINTERS=16, in reality the communication between system_server and app will use much less data. However, it's still useful to add these asserts to understand the worst-case scenario of message transfer. Bug: 167946763 Test: m StructLayout_test Change-Id: I281ecea62b392dea56936d031ab9c4ee18add93f --- include/input/Input.h | 2 +- include/input/InputTransport.h | 18 ----------- libs/input/InputTransport.cpp | 4 --- libs/input/tests/StructLayout_test.cpp | 37 +++++++++++++++++++--- .../inputflinger/dispatcher/InputDispatcher.cpp | 2 +- .../reader/mapper/MultiTouchInputMapper.cpp | 2 +- .../inputflinger/tests/InputDispatcher_test.cpp | 2 +- 7 files changed, 37 insertions(+), 30 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index f4147a0409..55ebb90ff6 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -164,7 +164,7 @@ enum { * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers * will occasionally emit 11. There is not much harm making this constant bigger.) */ -#define MAX_POINTERS 16 +static constexpr size_t MAX_POINTERS = 16; /* * Maximum number of samples supported per motion event. diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h index edcb615491..5f9a37d69c 100644 --- a/include/input/InputTransport.h +++ b/include/input/InputTransport.h @@ -500,24 +500,6 @@ public: status_t sendTimeline(int32_t inputEventId, std::array timeline); - /* Returns true if there is a deferred event waiting. - * - * Should be called after calling consume() to determine whether the consumer - * has a deferred event to be processed. Deferred events are somewhat special in - * that they have already been removed from the input channel. If the input channel - * becomes empty, the client may need to do extra work to ensure that it processes - * the deferred event despite the fact that the input channel's file descriptor - * is not readable. - * - * One option is simply to call consume() in a loop until it returns WOULD_BLOCK. - * This guarantees that all deferred events will be processed. - * - * Alternately, the caller can call hasDeferredEvent() to determine whether there is - * a deferred event waiting and then ensure that its event loop wakes up at least - * one more time to consume the deferred event. - */ - bool hasDeferredEvent() const; - /* Returns true if there is a pending batch. * * Should be called after calling consume() with consumeBatches == false to determine diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index a065ce25f7..61950522ff 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -1318,10 +1318,6 @@ status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) return result; } -bool InputConsumer::hasDeferredEvent() const { - return mMsgDeferred; -} - bool InputConsumer::hasPendingBatch() const { return !mBatches.empty(); } diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp index b6a94764e5..1c8658b69a 100644 --- a/libs/input/tests/StructLayout_test.cpp +++ b/libs/input/tests/StructLayout_test.cpp @@ -115,12 +115,9 @@ void TestHeaderSize() { static_assert(sizeof(InputMessage::Header) == 8); } -/** - * We cannot use the Body::size() method here because it is not static for - * the Motion type, where "pointerCount" variable affects the size and can change at runtime. - */ void TestBodySize() { static_assert(sizeof(InputMessage::Body::Key) == 96); + static_assert(sizeof(InputMessage::Body::Motion::Pointer) == 136); static_assert(sizeof(InputMessage::Body::Motion) == offsetof(InputMessage::Body::Motion, pointers) + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); @@ -132,6 +129,38 @@ void TestBodySize() { // Timeline static_assert(GraphicsTimeline::SIZE == 2); static_assert(sizeof(InputMessage::Body::Timeline) == 24); + + /** + * We cannot use the Body::size() method here because it is not static for + * the Motion type, where "pointerCount" variable affects the size and can change at runtime. + */ + static_assert(sizeof(InputMessage::Body) == + offsetof(InputMessage::Body::Motion, pointers) + + sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS); + static_assert(sizeof(InputMessage::Body) == 160 + 136 * 16); + static_assert(sizeof(InputMessage::Body) == 2336); +} + +/** + * In general, we are sending a variable-length message across the socket, because the number of + * pointers varies. When we receive the message, we still need to allocate enough memory for the + * entire InputMessage struct. This size is, therefore, the worst case scenario. However, it is + * still helpful to compute to get an idea of the sizes that are involved. + */ +void TestWorstCaseInputMessageSize() { + static_assert(sizeof(InputMessage) == /*header*/ 8 + /*body*/ 2336); + static_assert(sizeof(InputMessage) == 2344); +} + +/** + * Assuming a single pointer, how big is the message that we are sending across the socket? + */ +void CalculateSinglePointerInputMessageSize() { + constexpr size_t pointerCount = 1; + constexpr size_t bodySize = offsetof(InputMessage::Body::Motion, pointers) + + sizeof(InputMessage::Body::Motion::Pointer) * pointerCount; + static_assert(bodySize == 160 + 136); + static_assert(bodySize == 296); // For the total message size, add the small header } // --- VerifiedInputEvent --- diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 3f3c0db2c9..297074a0b4 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -215,7 +215,7 @@ bool validateMotionEvent(int32_t action, int32_t actionButton, size_t pointerCou return false; } if (pointerCount < 1 || pointerCount > MAX_POINTERS) { - ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %d.", + ALOGE("Motion event has invalid pointer count %zu; value must be between 1 and %zu.", pointerCount, MAX_POINTERS); return false; } diff --git a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp index 4bd1cd8d12..ff3a592c0f 100644 --- a/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/MultiTouchInputMapper.cpp @@ -282,7 +282,7 @@ void MultiTouchInputMapper::syncTouch(nsecs_t when, RawState* outState) { if (outCount >= MAX_POINTERS) { if (DEBUG_POINTERS) { - ALOGD("MultiTouch device %s emitted more than maximum of %d pointers; " + ALOGD("MultiTouch device %s emitted more than maximum of %zu pointers; " "ignoring the rest.", getDeviceName().c_str(), MAX_POINTERS); } diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 0814bc2be8..aa2f8326c8 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -558,7 +558,7 @@ TEST_F(InputDispatcherTest, InjectInputEvent_ValidatesMotionEvents) { MotionEvent event; PointerProperties pointerProperties[MAX_POINTERS + 1]; PointerCoords pointerCoords[MAX_POINTERS + 1]; - for (int i = 0; i <= MAX_POINTERS; i++) { + for (size_t i = 0; i <= MAX_POINTERS; i++) { pointerProperties[i].clear(); pointerProperties[i].id = i; pointerCoords[i].clear(); -- cgit v1.2.3-59-g8ed1b From ba0a8758240241a852d7fd78603be5c10cb0f05c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 14 Sep 2021 14:43:25 -0700 Subject: Add ChromeOS palm rejection model This model will be used to block palm presses. It takes in a stream of evdev events, and reports back the pointers which should be considered palm. Bug: 198472780 Test: atest libpalmrejection_test inputflinger_tests Test: "adb shell device_config put input_native_boot palm_rejection_enabled 0" and make sure that "adb shell dumpsys input" shows that there aren't any palm rejectors inside UnwantedInteractionBlocker Change-Id: If979d335af29cf5e93b26336fea56a3a895cc562 --- include/input/Input.h | 2 +- services/inputflinger/Android.bp | 3 + services/inputflinger/InputClassifierConverter.cpp | 7 +- services/inputflinger/InputListener.cpp | 19 + services/inputflinger/InputManager.cpp | 12 +- services/inputflinger/InputManager.h | 22 +- .../inputflinger/UnwantedInteractionBlocker.cpp | 687 +++++++++++++++ services/inputflinger/UnwantedInteractionBlocker.h | 153 ++++ services/inputflinger/include/InputListener.h | 2 + .../include/UnwantedInteractionBlockerInterface.h | 53 ++ services/inputflinger/tests/Android.bp | 1 + .../tests/UnwantedInteractionBlocker_test.cpp | 938 +++++++++++++++++++++ 12 files changed, 1886 insertions(+), 13 deletions(-) create mode 100644 services/inputflinger/UnwantedInteractionBlocker.cpp create mode 100644 services/inputflinger/UnwantedInteractionBlocker.h create mode 100644 services/inputflinger/include/UnwantedInteractionBlockerInterface.h create mode 100644 services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 55ebb90ff6..e421dee275 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -534,7 +534,7 @@ public: inline int32_t getActionMasked() const { return getActionMasked(mAction); } - static int32_t getActionIndex(int32_t action) { + static uint8_t getActionIndex(int32_t action) { return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; } diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index ec5832515c..22a69e5a30 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -49,6 +49,7 @@ filegroup { srcs: [ "InputClassifier.cpp", "InputClassifierConverter.cpp", + "UnwantedInteractionBlocker.cpp", "InputManager.cpp", ], } @@ -60,6 +61,7 @@ cc_defaults { "android.hardware.input.classifier@1.0", "libbase", "libbinder", + "libchrome", "libcrypto", "libcutils", "libhidlbase", @@ -76,6 +78,7 @@ cc_defaults { ], static_libs: [ "libattestation", + "libpalmrejection", ], } diff --git a/services/inputflinger/InputClassifierConverter.cpp b/services/inputflinger/InputClassifierConverter.cpp index fc8c7c39f9..b58a188a82 100644 --- a/services/inputflinger/InputClassifierConverter.cpp +++ b/services/inputflinger/InputClassifierConverter.cpp @@ -325,11 +325,6 @@ static std::vector convertVideoFrames( return out; } -static uint8_t getActionIndex(int32_t action) { - return (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> - AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; -} - static void getHidlPropertiesAndCoords(const NotifyMotionArgs& args, std::vector* outPointerProperties, std::vector* outPointerCoords) { @@ -360,7 +355,7 @@ common::V1_0::MotionEvent notifyMotionArgsToHalMotionEvent(const NotifyMotionArg event.eventTime = args.eventTime; event.deviceTimestamp = 0; event.action = getAction(args.action & AMOTION_EVENT_ACTION_MASK); - event.actionIndex = getActionIndex(args.action); + event.actionIndex = MotionEvent::getActionIndex(args.action); event.actionButton = getActionButton(args.actionButton); event.flags = getFlags(args.flags); event.policyFlags = getPolicyFlags(args.policyFlags); diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 158d0ebd58..73b63e3141 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -188,6 +188,25 @@ bool NotifyMotionArgs::operator==(const NotifyMotionArgs& rhs) const { return true; } +std::string NotifyMotionArgs::dump() const { + std::string coords; + for (uint32_t i = 0; i < pointerCount; i++) { + if (!coords.empty()) { + coords += ", "; + } + coords += StringPrintf("{%" PRIu32 ": ", i); + coords += + StringPrintf("id=%" PRIu32 " x=%.1f y=%.1f, pressure=%.1f", pointerProperties[i].id, + pointerCoords[i].getX(), pointerCoords[i].getY(), + pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE)); + coords += "}"; + } + return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 + ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)", + id, eventTime, deviceId, inputEventSourceToString(source).c_str(), + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str()); +} + void NotifyMotionArgs::notify(InputListenerInterface& listener) const { listener.notifyMotion(this); } diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 221e193136..7a9862d065 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -21,6 +21,7 @@ #include "InputManager.h" #include "InputDispatcherFactory.h" #include "InputReaderFactory.h" +#include "UnwantedInteractionBlocker.h" #include @@ -54,12 +55,17 @@ static int32_t exceptionCodeFromStatusT(status_t status) { } } +/** + * The event flow is via the "InputListener" interface, as follows: + * InputReader -> UnwantedInteractionBlocker -> InputClassifier -> InputDispatcher + */ InputManager::InputManager( const sp& readerPolicy, const sp& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique(*mDispatcher); - mReader = createInputReader(readerPolicy, *mClassifier); + mUnwantedInteractionBlocker = std::make_unique(*mClassifier); + mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); } InputManager::~InputManager() { @@ -106,6 +112,10 @@ InputReaderInterface& InputManager::getReader() { return *mReader; } +UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { + return *mUnwantedInteractionBlocker; +} + InputClassifierInterface& InputManager::getClassifier() { return *mClassifier; } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e00028364a..35d2b0fa19 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -23,6 +23,7 @@ #include "InputClassifier.h" #include "InputReaderBase.h" +#include "include/UnwantedInteractionBlockerInterface.h" #include #include @@ -46,11 +47,16 @@ class InputDispatcherThread; * The input manager has three components. * * 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies - * policy, and posts messages to a queue managed by the InputClassifier. - * 2. The InputClassifier class starts a thread to communicate with the device-specific - * classifiers. It then waits on the queue of events from InputReader, applies a classification - * to them, and queues them for the InputDispatcher. - * 3. The InputDispatcher class starts a thread that waits for new events on the + * policy, and posts messages to a queue managed by the UnwantedInteractionBlocker. + * 2. The UnwantedInteractionBlocker is responsible for removing unwanted interactions. For example, + * this could be a palm on the screen. This stage would alter the event stream to remove either + * partially (some of the pointers) or fully (all touches) the unwanted interaction. The events + * are processed on the InputReader thread, without any additional queue. The events are then + * posted to the queue managed by the InputClassifier. + * 3. The InputClassifier class starts a thread to communicate with the device-specific + * classifiers. It then waits on the queue of events from UnwantedInteractionBlocker, applies + * a classification to them, and queues them for the InputDispatcher. + * 4. The InputDispatcher class starts a thread that waits for new events on the * previous queue and asynchronously dispatches them to applications. * * By design, none of these classes share any internal state. Moreover, all communication is @@ -76,6 +82,9 @@ public: /* Gets the input reader. */ virtual InputReaderInterface& getReader() = 0; + /* Gets the unwanted interaction blocker. */ + virtual UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() = 0; + /* Gets the input classifier */ virtual InputClassifierInterface& getClassifier() = 0; @@ -96,6 +105,7 @@ public: status_t stop() override; InputReaderInterface& getReader() override; + UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; @@ -107,6 +117,8 @@ public: private: std::unique_ptr mReader; + std::unique_ptr mUnwantedInteractionBlocker; + std::unique_ptr mClassifier; std::unique_ptr mDispatcher; diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp new file mode 100644 index 0000000000..64dbb8ceb4 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -0,0 +1,687 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "UnwantedInteractionBlocker" +#include "UnwantedInteractionBlocker.h" + +#include +#include +#include +#include +#include + +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter.h" +#include "ui/events/ozone/evdev/touch_filter/palm_model/onedevice_train_palm_detection_filter_model.h" + +using android::base::StringPrintf; + +namespace android { + +// Category (=namespace) name for the input settings that are applied at boot time +static const char* INPUT_NATIVE_BOOT = "input_native_boot"; +/** + * Feature flag name. This flag determines whether palm rejection is enabled. To enable, specify + * 'true' (not case sensitive) or '1'. To disable, specify any other value. + */ +static const char* PALM_REJECTION_ENABLED = "palm_rejection_enabled"; + +static std::string toLower(std::string s) { + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); + return s; +} + +static bool isFromTouchscreen(int32_t source) { + return isFromSource(source, AINPUT_SOURCE_TOUCHSCREEN); +} + +static ::base::TimeTicks toChromeTimestamp(nsecs_t eventTime) { + return ::base::TimeTicks::UnixEpoch() + + ::base::Milliseconds(static_cast(ns2ms(eventTime))); +} + +/** + * Return true if palm rejection is enabled via the server configurable flags. Return false + * otherwise. + */ +static bool isPalmRejectionEnabled() { + std::string value = toLower( + server_configurable_flags::GetServerConfigurableFlag(INPUT_NATIVE_BOOT, + PALM_REJECTION_ENABLED, "false")); + if (value == "true" || value == "1") { + return true; + } + return false; +} + +static int getLinuxToolType(int32_t toolType) { + switch (toolType) { + case AMOTION_EVENT_TOOL_TYPE_FINGER: + return MT_TOOL_FINGER; + case AMOTION_EVENT_TOOL_TYPE_STYLUS: + return MT_TOOL_PEN; + case AMOTION_EVENT_TOOL_TYPE_PALM: + return MT_TOOL_PALM; + } + ALOGW("Got tool type %" PRId32 ", converting to MT_TOOL_FINGER", toolType); + return MT_TOOL_FINGER; +} + +static std::string addPrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + +template +static std::string dumpSet(const std::set& v) { + static_assert(std::is_integral::value, "Only integral types can be printed."); + std::string out; + for (const T& entry : v) { + out += out.empty() ? "{" : ", "; + out += android::base::StringPrintf("%i", entry); + } + return out.empty() ? "{}" : (out + "}"); +} + +template +static std::string dumpMap(const std::map& map) { + static_assert(std::is_integral::value, "Keys should have integral type to be printed."); + static_assert(std::is_integral::value, "Values should have integral type to be printed."); + std::string out; + for (const auto& [k, v] : map) { + if (!out.empty()) { + out += "\n"; + } + out += android::base::StringPrintf("%i : %i", static_cast(k), static_cast(v)); + } + return out; +} + +static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { + std::string out; + out += StringPrintf("max_x = %.2f\n", info.max_x); + out += StringPrintf("max_y = %.2f\n", info.max_y); + out += StringPrintf("x_res = %.2f\n", info.x_res); + out += StringPrintf("y_res = %.2f\n", info.y_res); + out += StringPrintf("major_radius_res = %.2f\n", info.major_radius_res); + out += StringPrintf("minor_radius_res = %.2f\n", info.minor_radius_res); + out += StringPrintf("minor_radius_supported = %s\n", + info.minor_radius_supported ? "true" : "false"); + out += StringPrintf("touch_major_res = %" PRId32 "\n", info.touch_major_res); + out += StringPrintf("touch_minor_res = %" PRId32 "\n", info.touch_minor_res); + return out; +} + +static int32_t getActionUpForPointerId(const NotifyMotionArgs& args, int32_t pointerId) { + for (size_t i = 0; i < args.pointerCount; i++) { + if (pointerId == args.pointerProperties[i].id) { + return AMOTION_EVENT_ACTION_POINTER_UP | + (i << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + } + } + LOG_ALWAYS_FATAL("Can't find pointerId %" PRId32 " in %s", pointerId, args.dump().c_str()); +} + +/** + * Find the action for individual pointer at the given pointer index. + * This is always equal to MotionEvent::getActionMasked, except for + * POINTER_UP or POINTER_DOWN events. For example, in a POINTER_UP event, the action for + * the active pointer is ACTION_POINTER_UP, while the action for the other pointers is ACTION_MOVE. + */ +static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { + const int32_t actionMasked = MotionEvent::getActionMasked(action); + if (actionMasked != AMOTION_EVENT_ACTION_POINTER_DOWN && + actionMasked != AMOTION_EVENT_ACTION_POINTER_UP) { + return actionMasked; + } + // This is a POINTER_DOWN or POINTER_UP event + const uint8_t actionIndex = MotionEvent::getActionIndex(action); + if (pointerIndex == actionIndex) { + return actionMasked; + } + // When POINTER_DOWN or POINTER_UP happens, it's actually a MOVE for all of the other + // pointers + return AMOTION_EVENT_ACTION_MOVE; +} + +static const char* toString(bool value) { + return value ? "true" : "false"; +} + +std::string toString(const ::ui::InProgressTouchEvdev& touch) { + return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," + " pressure=%.1f, major=%i, minor=%i, " + "tool_type=%i, altered=%s, was_touching=%s, touching=%s", + touch.x, touch.y, touch.tracking_id, touch.slot, touch.pressure, + touch.major, touch.minor, touch.tool_type, toString(touch.altered), + toString(touch.was_touching), toString(touch.touching)); +} + +/** + * Remove the data for the provided pointers from the args. The pointers are identified by their + * pointerId, not by the index inside the array. + * Return the new NotifyMotionArgs struct that has the remaining pointers. + * The only fields that may be different in the returned args from the provided args are: + * - action + * - pointerCount + * - pointerProperties + * - pointerCoords + * Action might change because it contains a pointer index. If another pointer is removed, the + * active pointer index would be shifted. + * Do not call this function for events with POINTER_UP or POINTER_DOWN events when removed pointer + * id is the acting pointer id. + * + * @param args the args from which the pointers should be removed + * @param pointerIds the pointer ids of the pointers that should be removed + */ +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set& pointerIds) { + const uint8_t actionIndex = MotionEvent::getActionIndex(args.action); + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + const bool isPointerUpOrDownAction = actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN || + actionMasked == AMOTION_EVENT_ACTION_POINTER_UP; + + NotifyMotionArgs newArgs{args}; + newArgs.pointerCount = 0; + int32_t newActionIndex = 0; + for (uint32_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + if (pointerIds.find(pointerId) != pointerIds.end()) { + // skip this pointer + if (isPointerUpOrDownAction && i == actionIndex) { + // The active pointer is being removed, so the action is no longer valid. + // Set the action to 'UNKNOWN' here. The caller is responsible for updating this + // action later to a proper value. + newArgs.action = ACTION_UNKNOWN; + } + continue; + } + newArgs.pointerProperties[newArgs.pointerCount].copyFrom(args.pointerProperties[i]); + newArgs.pointerCoords[newArgs.pointerCount].copyFrom(args.pointerCoords[i]); + if (i == actionIndex) { + newActionIndex = newArgs.pointerCount; + } + newArgs.pointerCount++; + } + // Update POINTER_DOWN or POINTER_UP actions + if (isPointerUpOrDownAction && newArgs.action != ACTION_UNKNOWN) { + newArgs.action = + actionMasked | (newActionIndex << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); + // Convert POINTER_DOWN and POINTER_UP to DOWN and UP if there's only 1 pointer remaining + if (newArgs.pointerCount == 1) { + if (actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + newArgs.action = AMOTION_EVENT_ACTION_DOWN; + } else if (actionMasked == AMOTION_EVENT_ACTION_POINTER_UP) { + newArgs.action = AMOTION_EVENT_ACTION_UP; + } + } + } + return newArgs; +} + +std::optional createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo) { + if (!isFromTouchscreen(deviceInfo.getSources())) { + return std::nullopt; + } + AndroidPalmFilterDeviceInfo out; + const InputDeviceInfo::MotionRange* axisX = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN); + if (axisX != nullptr) { + out.max_x = axisX->max; + out.x_res = axisX->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_X is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisY = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN); + if (axisY != nullptr) { + out.max_y = axisY->max; + out.y_res = axisY->resolution; + } else { + ALOGW("Palm rejection is disabled for %s because AXIS_Y is not supported", + deviceInfo.getDisplayName().c_str()); + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMajor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMajor != nullptr) { + out.major_radius_res = axisMajor->resolution; + out.touch_major_res = axisMajor->resolution; + } else { + return std::nullopt; + } + const InputDeviceInfo::MotionRange* axisMinor = + deviceInfo.getMotionRange(AMOTION_EVENT_AXIS_TOUCH_MINOR, AINPUT_SOURCE_TOUCHSCREEN); + if (axisMinor != nullptr) { + out.minor_radius_res = axisMinor->resolution; + out.touch_minor_res = axisMinor->resolution; + out.minor_radius_supported = true; + } else { + out.minor_radius_supported = false; + } + + return out; +} + +/** + * Synthesize CANCEL events for any new pointers that should be canceled, while removing pointers + * that have already been canceled. + * The flow of the function is as follows: + * 1. Remove all already canceled pointers + * 2. Cancel all newly suppressed pointers + * 3. Decide what to do with the current event : keep it, or drop it + * The pointers can never be "unsuppressed": once a pointer is canceled, it will never become valid. + */ +std::vector cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set& oldSuppressedPointerIds, + const std::set& newSuppressedPointerIds) { + LOG_ALWAYS_FATAL_IF(args.pointerCount == 0, "0 pointers in %s", args.dump().c_str()); + + // First, let's remove the old suppressed pointers. They've already been canceled previously. + NotifyMotionArgs oldArgs = removePointerIds(args, oldSuppressedPointerIds); + + // Cancel any newly suppressed pointers. + std::vector out; + const int32_t activePointerId = + args.pointerProperties[MotionEvent::getActionIndex(args.action)].id; + const int32_t actionMasked = MotionEvent::getActionMasked(args.action); + // We will iteratively remove pointers from 'removedArgs'. + NotifyMotionArgs removedArgs{oldArgs}; + for (uint32_t i = 0; i < oldArgs.pointerCount; i++) { + const int32_t pointerId = oldArgs.pointerProperties[i].id; + if (newSuppressedPointerIds.find(pointerId) == newSuppressedPointerIds.end()) { + // This is a pointer that should not be canceled. Move on. + continue; + } + if (pointerId == activePointerId && actionMasked == AMOTION_EVENT_ACTION_POINTER_DOWN) { + // Remove this pointer, but don't cancel it. We'll just not send the POINTER_DOWN event + removedArgs = removePointerIds(removedArgs, {pointerId}); + continue; + } + + if (removedArgs.pointerCount == 1) { + // We are about to remove the last pointer, which means there will be no more gesture + // remaining. This is identical to canceling all pointers, so just send a single CANCEL + // event, without any of the preceding POINTER_UP with FLAG_CANCELED events. + oldArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + oldArgs.action = AMOTION_EVENT_ACTION_CANCEL; + return {oldArgs}; + } + // Cancel the current pointer + out.push_back(removedArgs); + out.back().flags |= AMOTION_EVENT_FLAG_CANCELED; + out.back().action = getActionUpForPointerId(out.back(), pointerId); + + // Remove the newly canceled pointer from the args + removedArgs = removePointerIds(removedArgs, {pointerId}); + } + + // Now 'removedArgs' contains only pointers that are valid. + if (removedArgs.pointerCount <= 0 || removedArgs.action == ACTION_UNKNOWN) { + return out; + } + out.push_back(removedArgs); + return out; +} + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener) + : UnwantedInteractionBlocker(listener, isPalmRejectionEnabled()){}; + +UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, + bool enablePalmRejection) + : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + +void UnwantedInteractionBlocker::notifyConfigurationChanged( + const NotifyConfigurationChangedArgs* args) { + mListener.notifyConfigurationChanged(args); +} + +void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { + mListener.notifyKey(args); +} + +void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); + if (!sendToPalmRejector) { + mListener.notifyMotion(args); + return; + } + + const std::vector newMotions = it->second.processMotion(*args); + for (const NotifyMotionArgs& newArgs : newMotions) { + mListener.notifyMotion(&newArgs); + } +} + +void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { + mListener.notifySwitch(args); +} + +void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { + mListener.notifySensor(args); +} + +void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { + mListener.notifyVibratorState(args); +} +void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mListener.notifyDeviceReset(args); +} + +void UnwantedInteractionBlocker::notifyPointerCaptureChanged( + const NotifyPointerCaptureChangedArgs* args) { + mListener.notifyPointerCaptureChanged(args); +} + +void UnwantedInteractionBlocker::notifyInputDevicesChanged( + const std::vector& inputDevices) { + if (!mEnablePalmRejection) { + // Palm rejection is disabled. Don't create any palm rejector objects. + return; + } + + // Let's see which of the existing devices didn't change, so that we can keep them + // and prevent event stream disruption + std::set devicesToKeep; + for (const InputDeviceInfo& device : inputDevices) { + std::optional info = createPalmFilterDeviceInfo(device); + if (!info) { + continue; + } + + auto [it, emplaced] = mPalmRejectors.try_emplace(device.getId(), *info); + if (!emplaced && *info != it->second.getPalmFilterDeviceInfo()) { + // Re-create the PalmRejector because the device info has changed. + mPalmRejectors.erase(it); + mPalmRejectors.emplace(device.getId(), *info); + } + devicesToKeep.insert(device.getId()); + } + // Delete all devices that we don't need to keep + std::erase_if(mPalmRejectors, [&devicesToKeep](const auto& item) { + auto const& [deviceId, _] = item; + return devicesToKeep.find(deviceId) == devicesToKeep.end(); + }); +} + +void UnwantedInteractionBlocker::dump(std::string& dump) { + dump += "UnwantedInteractionBlocker:\n"; + dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); + dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", + toString(isPalmRejectionEnabled())); + dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; + for (const auto& [deviceId, palmRejector] : mPalmRejectors) { + dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); + dump += addPrefix(palmRejector.dump(), " "); + } +} + +void UnwantedInteractionBlocker::monitor() {} + +UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} + +void SlotState::update(const NotifyMotionArgs& args) { + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + processPointerId(pointerId, resolvedAction); + } +} + +size_t SlotState::findUnusedSlot() const { + size_t unusedSlot = 0; + // Since the collection is ordered, we can rely on the in-order traversal + for (const auto& [slot, trackingId] : mPointerIdsBySlot) { + if (unusedSlot != slot) { + break; + } + unusedSlot++; + } + return unusedSlot; +} + +void SlotState::processPointerId(int pointerId, int32_t actionMasked) { + switch (MotionEvent::getActionMasked(actionMasked)) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_HOVER_ENTER: { + // New pointer going down + size_t newSlot = findUnusedSlot(); + mPointerIdsBySlot[newSlot] = pointerId; + mSlotsByPointerId[pointerId] = newSlot; + return; + } + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_HOVER_MOVE: { + return; + } + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_HOVER_EXIT: { + auto it = mSlotsByPointerId.find(pointerId); + LOG_ALWAYS_FATAL_IF(it == mSlotsByPointerId.end()); + size_t slot = it->second; + // Erase this pointer from both collections + mPointerIdsBySlot.erase(slot); + mSlotsByPointerId.erase(pointerId); + return; + } + } + LOG_ALWAYS_FATAL("Unhandled action : %s", MotionEvent::actionToString(actionMasked).c_str()); + return; +} + +std::optional SlotState::getSlotForPointerId(int32_t pointerId) const { + auto it = mSlotsByPointerId.find(pointerId); + if (it == mSlotsByPointerId.end()) { + return std::nullopt; + } + return it->second; +} + +std::string SlotState::dump() const { + std::string out = "mSlotsByPointerId:\n"; + out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += "mPointerIdsBySlot:\n"; + out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + return out; +} + +PalmRejector::PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter) + : mSharedPalmState(std::make_unique<::ui::SharedPalmDetectionFilterState>()), + mDeviceInfo(info), + mPalmDetectionFilter(std::move(filter)) { + if (mPalmDetectionFilter != nullptr) { + // This path is used for testing. Non-testing invocations should let this constructor + // create a real PalmDetectionFilter + return; + } + std::unique_ptr<::ui::NeuralStylusPalmDetectionFilterModel> model = + std::make_unique<::ui::OneDeviceTrainNeuralStylusPalmDetectionFilterModel>( + std::vector()); + mPalmDetectionFilter = + std::make_unique<::ui::NeuralStylusPalmDetectionFilter>(mDeviceInfo, std::move(model), + mSharedPalmState.get()); +} + +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState) { + std::vector<::ui::InProgressTouchEvdev> touches; + + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + touches.emplace_back(::ui::InProgressTouchEvdev()); + touches.back().major = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + touches.back().minor = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR); + touches.back().tool_type = getLinuxToolType(args.pointerProperties[i].toolType); + + // Whether there is new information for the touch. + touches.back().altered = true; + + // Whether the touch was cancelled. Touch events should be ignored till a + // new touch is initiated. + touches.back().was_cancelled = false; + + // Whether the touch is going to be canceled. + touches.back().cancelled = false; + + // Whether the touch is delayed at first appearance. Will not be reported yet. + touches.back().delayed = false; + + // Whether the touch was delayed before. + touches.back().was_delayed = false; + + // Whether the touch is held until end or no longer held. + touches.back().held = false; + + // Whether this touch was held before being sent. + touches.back().was_held = false; + + const int32_t resolvedAction = resolveActionForPointer(i, args.action); + const bool isDown = resolvedAction == AMOTION_EVENT_ACTION_POINTER_DOWN || + resolvedAction == AMOTION_EVENT_ACTION_DOWN; + touches.back().was_touching = !isDown; + + const bool isUpOrCancel = resolvedAction == AMOTION_EVENT_ACTION_CANCEL || + resolvedAction == AMOTION_EVENT_ACTION_UP || + resolvedAction == AMOTION_EVENT_ACTION_POINTER_UP; + + touches.back().x = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X); + touches.back().y = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y); + + std::optional slot = newSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = oldSlotState.getSlotForPointerId(pointerId); + } + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer %d", pointerId); + touches.back().slot = *slot; + touches.back().tracking_id = (!isUpOrCancel) ? pointerId : -1; + touches.back().touching = !isUpOrCancel; + + // The fields 'radius_x' and 'radius_x' are not used for palm rejection + touches.back().pressure = args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE); + touches.back().tool_code = BTN_TOOL_FINGER; + // The field 'orientation' is not used for palm rejection + // The fields 'tilt_x' and 'tilt_y' are not used for palm rejection + touches.back().reported_tool_type = ::ui::EventPointerType::kTouch; + touches.back().stylus_button = false; + } + return touches; +} + +std::vector PalmRejector::processMotion(const NotifyMotionArgs& args) { + if (mPalmDetectionFilter == nullptr) { + return {args}; + } + const bool skipThisEvent = args.action == AMOTION_EVENT_ACTION_HOVER_ENTER || + args.action == AMOTION_EVENT_ACTION_HOVER_MOVE || + args.action == AMOTION_EVENT_ACTION_HOVER_EXIT || + args.action == AMOTION_EVENT_ACTION_BUTTON_PRESS || + args.action == AMOTION_EVENT_ACTION_BUTTON_RELEASE || + args.action == AMOTION_EVENT_ACTION_SCROLL; + if (skipThisEvent) { + // Lets not process hover events, button events, or scroll for now. + return {args}; + } + if (args.action == AMOTION_EVENT_ACTION_DOWN) { + mSuppressedPointerIds.clear(); + } + std::bitset<::ui::kNumTouchEvdevSlots> slotsToHold; + std::bitset<::ui::kNumTouchEvdevSlots> slotsToSuppress; + + // Store the slot state before we call getTouches and update it. This way, we can find + // the slots that have been removed due to the incoming event. + SlotState oldSlotState = mSlotState; + mSlotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, mDeviceInfo, oldSlotState, mSlotState); + ::base::TimeTicks chromeTimestamp = toChromeTimestamp(args.eventTime); + + mPalmDetectionFilter->Filter(touches, chromeTimestamp, &slotsToHold, &slotsToSuppress); + + // Now that we know which slots should be suppressed, let's convert those to pointer id's. + std::set oldSuppressedIds; + std::swap(oldSuppressedIds, mSuppressedPointerIds); + for (size_t i = 0; i < args.pointerCount; i++) { + const int32_t pointerId = args.pointerProperties[i].id; + std::optional slot = oldSlotState.getSlotForPointerId(pointerId); + if (!slot) { + slot = mSlotState.getSlotForPointerId(pointerId); + LOG_ALWAYS_FATAL_IF(!slot, "Could not find slot for pointer id %" PRId32, pointerId); + } + if (slotsToSuppress.test(*slot)) { + mSuppressedPointerIds.insert(pointerId); + } + } + + std::vector argsWithoutUnwantedPointers = + cancelSuppressedPointers(args, oldSuppressedIds, mSuppressedPointerIds); + for (const NotifyMotionArgs& checkArgs : argsWithoutUnwantedPointers) { + LOG_ALWAYS_FATAL_IF(checkArgs.action == ACTION_UNKNOWN, "%s", checkArgs.dump().c_str()); + } + + if (mSuppressedPointerIds != oldSuppressedIds) { + if (argsWithoutUnwantedPointers.size() != 1 || + argsWithoutUnwantedPointers[0].pointerCount != args.pointerCount) { + ALOGI("Palm detected, removing pointer ids %s from %s", + dumpSet(mSuppressedPointerIds).c_str(), args.dump().c_str()); + } + } + + return argsWithoutUnwantedPointers; +} + +const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { + return mDeviceInfo; +} + +std::string PalmRejector::dump() const { + std::string out; + out += "mDeviceInfo:\n"; + out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += "mSlotState:\n"; + out += addPrefix(mSlotState.dump(), " "); + out += "mSuppressedPointerIds: "; + out += dumpSet(mSuppressedPointerIds) + "\n"; + return out; +} + +} // namespace android diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h new file mode 100644 index 0000000000..14068fd878 --- /dev/null +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "include/UnwantedInteractionBlockerInterface.h" +#include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" +#include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" + +namespace android { + +// --- Functions for manipulation of event streams + +struct AndroidPalmFilterDeviceInfo : ::ui::PalmFilterDeviceInfo { + // Additional fields from 'TouchEventConverterEvdev', added here for convenience + int32_t touch_major_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MAJOR).resolution; + int32_t touch_minor_res = 1; // info.GetAbsInfoByCode(ABS_MT_TOUCH_MINOR).resolution; + + auto operator<=>(const AndroidPalmFilterDeviceInfo&) const = default; +}; + +std::optional createPalmFilterDeviceInfo( + const InputDeviceInfo& deviceInfo); + +static constexpr int32_t ACTION_UNKNOWN = -1; + +NotifyMotionArgs removePointerIds(const NotifyMotionArgs& args, + const std::set& pointerIds); + +std::vector cancelSuppressedPointers( + const NotifyMotionArgs& args, const std::set& oldSuppressedPointerIds, + const std::set& newSuppressedPointerIds); + +std::string toString(const ::ui::InProgressTouchEvdev& touch); + +// --- Main classes and interfaces --- + +class PalmRejector; + +// --- Implementations --- + +/** + * Implementation of the UnwantedInteractionBlockerInterface. + * Represents a separate stage of input processing. All of the input events go through this stage. + * Acts as a passthrough for all input events except for motion events. + * + * The events of motion type are sent to PalmRejectors. PalmRejectors detect unwanted touches, + * and emit input streams with the bad pointers removed. + */ +class UnwantedInteractionBlocker : public UnwantedInteractionBlockerInterface { +public: + explicit UnwantedInteractionBlocker(InputListenerInterface& listener); + explicit UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection); + + void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; + void notifyKey(const NotifyKeyArgs* args) override; + void notifyMotion(const NotifyMotionArgs* args) override; + void notifySwitch(const NotifySwitchArgs* args) override; + void notifySensor(const NotifySensorArgs* args) override; + void notifyVibratorState(const NotifyVibratorStateArgs* args) override; + void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; + + void notifyInputDevicesChanged(const std::vector& inputDevices) override; + void dump(std::string& dump) override; + void monitor() override; + + ~UnwantedInteractionBlocker(); + +private: + // The next stage to pass input events to + InputListenerInterface& mListener; + const bool mEnablePalmRejection; + + // Detect and reject unwanted palms on screen + // Use a separate palm rejector for every touch device. + std::map mPalmRejectors; +}; + +class SlotState { +public: + /** + * Update the state using the new information provided in the NotifyMotionArgs + */ + void update(const NotifyMotionArgs& args); + std::optional getSlotForPointerId(int32_t pointerId) const; + std::string dump() const; + +private: + // Process a pointer with the provided action, and return the slot associated with it + void processPointerId(int32_t pointerId, int32_t action); + // The map from tracking id to slot state. Since the PalmRejectionFilter works close to the + // evdev level, the only way to tell it about UP or CANCEL events is by sending tracking id = -1 + // to the appropriate touch slot. So we need to reconstruct the original slot. + // The two collections below must always be in-sync. + // Use std::map instead of std::unordered_map because we rely on these collections being + // ordered. It also has better space efficiency than unordered_map because we only have a few + // pointers most of the time. + std::map mSlotsByPointerId; + std::map mPointerIdsBySlot; + + size_t findUnusedSlot() const; +}; + +/** + * Convert an Android event to a linux-like 'InProgressTouchEvdev'. The provided SlotState's + * are used to figure out which slot does each pointer belong to. + */ +std::vector<::ui::InProgressTouchEvdev> getTouches(const NotifyMotionArgs& args, + const AndroidPalmFilterDeviceInfo& deviceInfo, + const SlotState& oldSlotState, + const SlotState& newSlotState); + +class PalmRejector { +public: + explicit PalmRejector(const AndroidPalmFilterDeviceInfo& info, + std::unique_ptr<::ui::PalmDetectionFilter> filter = nullptr); + std::vector processMotion(const NotifyMotionArgs& args); + + // Get the device info of this device, for comparison purposes + const AndroidPalmFilterDeviceInfo& getPalmFilterDeviceInfo(); + std::string dump() const; + +private: + PalmRejector(const PalmRejector&) = delete; + PalmRejector& operator=(const PalmRejector&) = delete; + + std::unique_ptr<::ui::SharedPalmDetectionFilterState> mSharedPalmState; + AndroidPalmFilterDeviceInfo mDeviceInfo; + std::unique_ptr<::ui::PalmDetectionFilter> mPalmDetectionFilter; + std::set mSuppressedPointerIds; + + // Used to help convert an Android touch stream to Linux input stream. + SlotState mSlotState; +}; + +} // namespace android diff --git a/services/inputflinger/include/InputListener.h b/services/inputflinger/include/InputListener.h index db6310478c..dff58949a7 100644 --- a/services/inputflinger/include/InputListener.h +++ b/services/inputflinger/include/InputListener.h @@ -142,6 +142,8 @@ struct NotifyMotionArgs : public NotifyArgs { bool operator==(const NotifyMotionArgs& rhs) const; void notify(InputListenerInterface& listener) const override; + + std::string dump() const; }; /* Describes a sensor event. */ diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h new file mode 100644 index 0000000000..2327266563 --- /dev/null +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "InputListener.h" + +namespace android { + +/** + * Base interface for an InputListener stage. + * Blocks unintentional input events. Not thread safe. Must be called from the same + * thread. All work is performed on the calling threads. + */ +class UnwantedInteractionBlockerInterface : public InputListenerInterface { +public: + /* Notifies the input reader policy that some input devices have changed + * and provides information about all current input devices. + * Important! This call should happen on the same thread as the calls to the + * InputListenerInterface methods. + * That is, same thread should call 'notifyMotion' and 'notifyInputDevicesChanged' and + * 'notifyDeviceReset'. If this architecture changes, we will need to make the implementation + * of this interface thread-safe. + */ + virtual void notifyInputDevicesChanged(const std::vector& inputDevices) = 0; + + /** + * Dump the state of the interaction blocker. + * This method may be called on any thread (usually by the input manager). + */ + virtual void dump(std::string& dump) = 0; + + /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + virtual void monitor() = 0; + + UnwantedInteractionBlockerInterface() {} + ~UnwantedInteractionBlockerInterface() override {} +}; + +} // namespace android diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp index d19dbaf009..f734e599b0 100644 --- a/services/inputflinger/tests/Android.bp +++ b/services/inputflinger/tests/Android.bp @@ -49,6 +49,7 @@ cc_test { "LatencyTracker_test.cpp", "TestInputListener.cpp", "UinputDevice.cpp", + "UnwantedInteractionBlocker_test.cpp", ], aidl: { include_dirs: [ diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp new file mode 100644 index 0000000000..a3220cc63a --- /dev/null +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../UnwantedInteractionBlocker.h" +#include +#include +#include +#include + +#include "TestInputListener.h" + +namespace android { + +constexpr int32_t DEVICE_ID = 3; +constexpr int32_t X_RESOLUTION = 11; +constexpr int32_t Y_RESOLUTION = 11; +constexpr int32_t MAJOR_RESOLUTION = 1; + +constexpr int POINTER_0_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_1_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int POINTER_2_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN; +constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; +constexpr int UP = AMOTION_EVENT_ACTION_UP; +constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; + +struct PointerData { + float x; + float y; + float major; +}; + +static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, int32_t action, + const std::vector& points) { + size_t pointerCount = points.size(); + if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) { + EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer"; + } + + PointerProperties pointerProperties[pointerCount]; + PointerCoords pointerCoords[pointerCount]; + + for (size_t i = 0; i < pointerCount; i++) { + pointerProperties[i].clear(); + pointerProperties[i].id = i; + pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + pointerCoords[i].clear(); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y); + pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR, points[i].major); + } + + // Define a valid motion event. + NotifyMotionArgs args(/* id */ 0, eventTime, 0 /*readTime*/, DEVICE_ID, + AINPUT_SOURCE_TOUCHSCREEN, 0 /*displayId*/, POLICY_FLAG_PASS_TO_USER, + action, /* actionButton */ 0, + /* flags */ 0, AMETA_NONE, /* buttonState */ 0, + MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, + pointerProperties, pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0, + AMOTION_EVENT_INVALID_CURSOR_POSITION, + AMOTION_EVENT_INVALID_CURSOR_POSITION, downTime, /* videoFrames */ {}); + + return args; +} + +static InputDeviceInfo generateTestDeviceInfo() { + InputDeviceIdentifier identifier; + + auto info = InputDeviceInfo(); + info.initialize(DEVICE_ID, /*generation*/ 1, /*controllerNumber*/ 1, identifier, "alias", + /*isExternal*/ false, /*hasMic*/ false); + info.addSource(AINPUT_SOURCE_TOUCHSCREEN); + info.addMotionRange(AMOTION_EVENT_AXIS_X, AINPUT_SOURCE_TOUCHSCREEN, 0, 1599, /*flat*/ 0, + /*fuzz*/ 0, X_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_Y, AINPUT_SOURCE_TOUCHSCREEN, 0, 2559, /*flat*/ 0, + /*fuzz*/ 0, Y_RESOLUTION); + info.addMotionRange(AMOTION_EVENT_AXIS_TOUCH_MAJOR, AINPUT_SOURCE_TOUCHSCREEN, 0, 255, + /*flat*/ 0, /*fuzz*/ 0, MAJOR_RESOLUTION); + + return info; +} + +static AndroidPalmFilterDeviceInfo generatePalmFilterDeviceInfo() { + InputDeviceInfo androidInfo = generateTestDeviceInfo(); + std::optional info = createPalmFilterDeviceInfo(androidInfo); + if (!info) { + ADD_FAILURE() << "Could not convert android device info to ::ui version"; + return {}; + } + return *info; +} + +TEST(DeviceInfoConversionTest, TabletDeviceTest) { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + ASSERT_EQ(X_RESOLUTION, info.x_res); + ASSERT_EQ(Y_RESOLUTION, info.y_res); + ASSERT_EQ(MAJOR_RESOLUTION, info.touch_major_res); + ASSERT_EQ(1599, info.max_x); + ASSERT_EQ(2559, info.max_y); +} + +static void assertArgs(const NotifyMotionArgs& args, int32_t action, + const std::vector>& pointers) { + ASSERT_EQ(action, args.action); + ASSERT_EQ(pointers.size(), args.pointerCount); + for (size_t i = 0; i < args.pointerCount; i++) { + const auto& [pointerId, pointerData] = pointers[i]; + ASSERT_EQ(pointerId, args.pointerProperties[i].id); + ASSERT_EQ(pointerData.x, args.pointerCoords[i].getX()); + ASSERT_EQ(pointerData.y, args.pointerCoords[i].getY()); + ASSERT_EQ(pointerData.major, + args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR)); + } +} + +TEST(RemovePointerIdsTest, RemoveOnePointer) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); + + NotifyMotionArgs pointer0Only = removePointerIds(args, {1}); + assertArgs(pointer0Only, AMOTION_EVENT_ACTION_MOVE, {{0, {1, 2, 3}}}); +} + +/** + * Remove 2 out of 3 pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveTwoPointers) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, AMOTION_EVENT_ACTION_MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1Only = removePointerIds(args, {0, 2}); + assertArgs(pointer1Only, AMOTION_EVENT_ACTION_MOVE, {{1, {4, 5, 6}}}); +} + +/** + * Remove an active pointer during a POINTER_DOWN event, and also remove a non-active + * pointer during a POINTER_DOWN event. + */ +TEST(RemovePointerIdsTest, ActionPointerDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointers0And2 = removePointerIds(args, {1}); + assertArgs(pointers0And2, ACTION_UNKNOWN, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); + + NotifyMotionArgs pointers1And2 = removePointerIds(args, {0}); + assertArgs(pointers1And2, POINTER_0_DOWN, {{1, {4, 5, 6}}, {2, {7, 8, 9}}}); +} + +/** + * Remove all pointers during a MOVE event. + */ +TEST(RemovePointerIdsTest, RemoveAllPointersDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, + AMOTION_EVENT_ACTION_MOVE, {{1, 2, 3}, {4, 5, 6}}); + + NotifyMotionArgs noPointers = removePointerIds(args, {0, 1}); + ASSERT_EQ(0u, noPointers.pointerCount); +} + +/** + * If we have ACTION_POINTER_DOWN, and we remove all pointers except for the active pointer, + * then we should just have ACTION_DOWN. Likewise, a POINTER_UP event should become an UP event. + */ +TEST(RemovePointerIdsTest, PointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + + NotifyMotionArgs pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, DOWN, {{1, {4, 5, 6}}}); + + args.action = POINTER_1_UP; + pointer1 = removePointerIds(args, {0, 2}); + assertArgs(pointer1, UP, {{1, {4, 5, 6}}}); +} + +/** + * If a pointer that is now going down is canceled, then we can just drop the POINTER_DOWN event. + */ +TEST(CancelSuppressedPointersTest, CanceledPointerDownIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, the POINTER_UP event for this pointer should be dropped + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerUpIsDropped) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_TRUE(result.empty()); +} + +/** + * If a pointer is already suppressed, it should be removed from a MOVE event. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointerIsRemovedDuringMove) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If a pointer just got canceled during a MOVE event, we should see two events: + * 1) ACTION_POINTER_UP with FLAG_CANCELED so that this pointer is lifted + * 2) A MOVE event without this pointer + */ +TEST(CancelSuppressedPointersTest, NewlySuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(2u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); + assertArgs(result[1], MOVE, {{0, {1, 2, 3}}, {2, {7, 8, 9}}}); +} + +/** + * If we have a single pointer that gets canceled during a MOVE, the entire gesture + * should be canceled with ACTION_CANCEL. + */ +TEST(CancelSuppressedPointersTest, SingleSuppressedPointerIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If one of 3 pointers gets canceled during a POINTER_UP event, we should proceed with POINTER_UP, + * but this event should also have FLAG_CANCELED to indicate that this pointer was unintentional. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer1GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_1_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}, {2, {7, 8, 9}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Same test as above, but we change the pointer's index to 0 instead of 1. This helps detect + * errors with handling pointer index inside the action. + */ +TEST(CancelSuppressedPointersTest, SuppressedPointer0GoingUpIsCanceled) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_0_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], POINTER_0_UP, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * If two pointers are canceled simultaneously during MOVE, we should see a single ACTION_CANCEL + * event. This event would cancel the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoNewlySuppressedPointersAreBothCanceled) { + NotifyMotionArgs args = + generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, MOVE, {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}, {1, {4, 5, 6}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * Similar test to above. During a POINTER_UP event, both pointers are detected as 'palm' and + * therefore should be removed. In this case, we should send a single ACTION_CANCEL that + * would undo the entire gesture. + */ +TEST(CancelSuppressedPointersTest, TwoPointersAreCanceledDuringPointerUp) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_1_UP, + {{1, 2, 3}, {4, 5, 6}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], CANCEL, {{0, {1, 2, 3}}}); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, result[0].flags); +} + +/** + * When all pointers have been removed from the touch stream, and we have a new POINTER_DOWN, + * this should become a regular DOWN event because it's the only pointer that will be valid now. + */ +TEST(CancelSuppressedPointersTest, NewPointerDownBecomesDown) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, POINTER_2_DOWN, + {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + std::vector result = + cancelSuppressedPointers(args, /*oldSuppressedPointerIds*/ {0, 1}, + /*newSuppressedPointerIds*/ {0, 1}); + ASSERT_EQ(1u, result.size()); + assertArgs(result[0], DOWN, {{2, {7, 8, 9}}}); + ASSERT_EQ(0, result[0].flags); +} + +/** + * Call 'getTouches' for a DOWN event and check that the resulting 'InProgressTouchEvdev' + * struct is populated as expected. + */ +TEST(GetTouchesTest, ConvertDownEvent) { + NotifyMotionArgs args = generateMotionArgs(/*downTime*/ 0, /*eventTime*/ 0, DOWN, {{1, 2, 3}}); + AndroidPalmFilterDeviceInfo deviceInfo = generatePalmFilterDeviceInfo(); + SlotState slotState; + SlotState oldSlotState = slotState; + slotState.update(args); + std::vector<::ui::InProgressTouchEvdev> touches = + getTouches(args, deviceInfo, oldSlotState, slotState); + ASSERT_EQ(1u, touches.size()); + ::ui::InProgressTouchEvdev expected; + + expected.major = 3; + expected.minor = 0; + expected.tool_type = MT_TOOL_FINGER; + expected.altered = true; + expected.was_cancelled = false; + expected.cancelled = false; + expected.delayed = false; + expected.was_delayed = false; + expected.held = false; + expected.was_held = false; + expected.was_touching = false; + expected.touching = true; + expected.x = 1; + expected.y = 2; + expected.tracking_id = 0; + std::optional slot = slotState.getSlotForPointerId(0); + ASSERT_TRUE(slot); + expected.slot = *slot; + expected.pressure = 0; + expected.tool_code = BTN_TOOL_FINGER; + expected.reported_tool_type = ::ui::EventPointerType::kTouch; + expected.stylus_button = false; + + ASSERT_EQ(expected, touches[0]) << toString(touches[0]); +} + +// --- UnwantedInteractionBlockerTest --- + +class UnwantedInteractionBlockerTest : public testing::Test { +protected: + TestInputListener mTestListener; + std::unique_ptr mBlocker; + + void SetUp() override { + mBlocker = std::make_unique(mTestListener, + /*enablePalmRejection*/ true); + } +}; + +/** + * Create a basic configuration change and send it to input classifier. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, ConfigurationChangedIsPassedToNextListener) { + // Create a basic configuration change and send to classifier + NotifyConfigurationChangedArgs args(1 /*sequenceNum*/, 2 /*eventTime*/); + + mBlocker->notifyConfigurationChanged(&args); + NotifyConfigurationChangedArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyConfigurationChangedWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Keys are not handled in 'UnwantedInteractionBlocker' and should be passed + * to next stage unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, KeyIsPassedToNextListener) { + // Create a basic key event and send to classifier + NotifyKeyArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 21 /*readTime*/, 3 /*deviceId*/, + AINPUT_SOURCE_KEYBOARD, ADISPLAY_ID_DEFAULT, 0 /*policyFlags*/, + AKEY_EVENT_ACTION_DOWN, 4 /*flags*/, AKEYCODE_HOME, 5 /*scanCode*/, + AMETA_NONE, 6 /*downTime*/); + + mBlocker->notifyKey(&args); + NotifyKeyArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyKeyWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic motion event. Since it's just a DOWN event, it should not + * be detected as palm and should be sent to the next listener stage + * unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DownEventIsPassedToNextListener) { + NotifyMotionArgs motionArgs = + generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&motionArgs); + NotifyMotionArgs args; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyMotionWasCalled(&args)); + ASSERT_EQ(motionArgs, args); +} + +/** + * Create a basic switch event and send it to the UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, SwitchIsPassedToNextListener) { + NotifySwitchArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, 3 /*policyFlags*/, 4 /*switchValues*/, + 5 /*switchMask*/); + + mBlocker->notifySwitch(&args); + NotifySwitchArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifySwitchWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * Create a basic device reset event and send it to UnwantedInteractionBlocker. + * Expect that the event is received by the next input stage, unmodified. + */ +TEST_F(UnwantedInteractionBlockerTest, DeviceResetIsPassedToNextListener) { + NotifyDeviceResetArgs args(1 /*sequenceNum*/, 2 /*eventTime*/, DEVICE_ID); + + mBlocker->notifyDeviceReset(&args); + NotifyDeviceResetArgs outArgs; + ASSERT_NO_FATAL_FAILURE(mTestListener.assertNotifyDeviceResetWasCalled(&outArgs)); + ASSERT_EQ(args, outArgs); +} + +/** + * The state should be reset when device reset happens. That means, we can reset in the middle of a + * gesture, and start a new stream. There should be no crash. If the state wasn't reset correctly, + * a crash due to inconsistent event stream could have occurred. + */ +TEST_F(UnwantedInteractionBlockerTest, NoCrashWhenResetHappens) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Start a new gesture with a DOWN event, even though the previous event stream was incomplete. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, DOWN, {{7, 8, 9}}))); +} + +/** + * If input devices have changed, but the important device info that's used by the + * UnwantedInteractionBlocker has not changed, there should not be a reset. + */ +TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + + // Now pretend the device changed, even though nothing is different for DEVICE_ID in practice. + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + + // The MOVE event continues the gesture that started before 'devices changed', so it should not + // cause a crash. + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}}))); +} + +using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; + +/** + * The state should be reset when device reset happens. If we receive an inconsistent event after + * the reset happens, crash should occur. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, InconsistentEventAfterResetCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2, 3}}))); + mBlocker->notifyMotion( + &(args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, MOVE, {{4, 5, 6}}))); + NotifyDeviceResetArgs resetArgs(1 /*sequenceNum*/, 3 /*eventTime*/, DEVICE_ID); + mBlocker->notifyDeviceReset(&resetArgs); + // Sending MOVE without a DOWN -> should crash! + ASSERT_DEATH( + { + mBlocker->notifyMotion(&(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, + MOVE, {{7, 8, 9}}))); + }, + "Could not find slot"); +} + +/** + * There should be a crash when an inconsistent event is received. + */ +TEST_F(UnwantedInteractionBlockerTestDeathTest, WhenMoveWithoutDownCausesACrash) { + ScopedSilentDeath _silentDeath; + NotifyMotionArgs args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 2, 3}}); + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + ASSERT_DEATH({ mBlocker->notifyMotion(&args); }, "Could not find slot"); +} + +class PalmRejectorTest : public testing::Test { +protected: + std::unique_ptr mPalmRejector; + + void SetUp() override { + AndroidPalmFilterDeviceInfo info = generatePalmFilterDeviceInfo(); + mPalmRejector = std::make_unique(info); + } +}; + +using PalmRejectorTestDeathTest = PalmRejectorTest; + +TEST_F(PalmRejectorTestDeathTest, InconsistentEventCausesACrash) { + ScopedSilentDeath _silentDeath; + constexpr nsecs_t downTime = 0; + NotifyMotionArgs args = + generateMotionArgs(downTime, 2 /*eventTime*/, MOVE, {{1406.0, 650.0, 52.0}}); + ASSERT_DEATH({ mPalmRejector->processMotion(args); }, "Could not find slot"); +} + +/** + * Use PalmRejector with actual touchscreen data and real model. + * Two pointers that should both be classified as palms. + */ +TEST_F(PalmRejectorTest, TwoPointersAreCanceled) { + std::vector argsList; + constexpr nsecs_t downTime = 255955749837000; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955759313000, MOVE, {{1406.0, 650.0, 52.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955766361000, MOVE, {{1429.0, 672.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, MOVE, {{1417.0, 685.0, 41.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955775989000, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955792536000, MOVE, + {{1415.0, 719.0, 44.0}, {1060.0, 760.0, 11.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955799474000, MOVE, + {{1421.0, 733.0, 42.0}, {1065.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955809177000, MOVE, + {{1426.0, 742.0, 43.0}, {1068.0, 771.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955816131000, MOVE, + {{1430.0, 748.0, 45.0}, {1069.0, 772.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955825907000, MOVE, + {{1432.0, 750.0, 44.0}, {1069.0, 772.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(0 /* No FLAG_CANCELED */, argsList[0].flags); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955832736000, MOVE, + {{1433.0, 751.0, 44.0}, {1070.0, 771.0, 13.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_0_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(1u, argsList[1].pointerCount); + ASSERT_EQ(0, argsList[1].flags); + + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955842432000, MOVE, + {{1433.0, 751.0, 42.0}, {1071.0, 770.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955849380000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 769.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955859046000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 768.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955869823000, MOVE, + {{1433.0, 751.0, 45.0}, {1072.0, 767.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(AMOTION_EVENT_ACTION_CANCEL, argsList[0].action); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955875641000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955882693000, MOVE, + {{1433.0, 750.0, 44.0}, {1072.0, 765.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955892324000, MOVE, + {{1433.0, 750.0, 42.0}, {1072.0, 763.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955899425000, MOVE, + {{1434.0, 750.0, 44.0}, {1073.0, 761.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955909400000, MOVE, + {{1435.0, 750.0, 43.0}, {1073.0, 759.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955915885000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 757.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955925607000, MOVE, + {{1436.0, 750.0, 44.0}, {1074.0, 755.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955932580000, MOVE, + {{1436.0, 750.0, 45.0}, {1074.0, 753.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955942231000, MOVE, + {{1436.0, 749.0, 44.0}, {1074.0, 751.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955949204000, MOVE, + {{1435.0, 748.0, 45.0}, {1074.0, 749.0, 15.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955959103000, MOVE, + {{1434.0, 746.0, 44.0}, {1074.0, 747.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955965884000, MOVE, + {{1433.0, 744.0, 44.0}, {1075.0, 745.0, 14.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955975649000, MOVE, + {{1431.0, 741.0, 43.0}, {1075.0, 742.0, 13.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955982537000, MOVE, + {{1428.0, 738.0, 43.0}, {1076.0, 739.0, 12.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955992284000, MOVE, + {{1400.0, 726.0, 54.0}, {1076.0, 739.0, 13.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, POINTER_1_UP, + {{1362.0, 716.0, 55.0}, {1076.0, 739.0, 13.0}})); + ASSERT_TRUE(argsList.empty()); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955999348000, MOVE, {{1362.0, 716.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956008885000, MOVE, {{1347.0, 707.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956015791000, MOVE, {{1340.0, 698.0, 54.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956025804000, MOVE, {{1338.0, 694.0, 55.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956032314000, MOVE, {{1336.0, 690.0, 53.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956042329000, MOVE, {{1334.0, 685.0, 47.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956048979000, MOVE, {{1333.0, 679.0, 46.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956058813000, MOVE, {{1332.0, 672.0, 45.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956065592000, MOVE, {{1333.0, 666.0, 40.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956075276000, MOVE, {{1336.0, 661.0, 24.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956082198000, MOVE, {{1338.0, 656.0, 16.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956092059000, MOVE, {{1341.0, 649.0, 1.0}})); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255956098764000, UP, {{1341.0, 649.0, 1.0}})); + ASSERT_TRUE(argsList.empty()); +} + +/** + * A test implementation of PalmDetectionFilter that allows you to specify which pointer you want + * the model to consider 'suppressed'. The pointer is specified using its position (x, y). + * Current limitation: + * Pointers may not cross each other in space during motion. Otherwise, any pointer with the + * position matching the suppressed position will be considered "palm". + */ +class TestFilter : public ::ui::PalmDetectionFilter { +public: + TestFilter(::ui::SharedPalmDetectionFilterState* state, + std::vector>& suppressedPointers) + : ::ui::PalmDetectionFilter(state), mSuppressedPointers(suppressedPointers) {} + + void Filter(const std::vector<::ui::InProgressTouchEvdev>& touches, ::base::TimeTicks time, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_hold, + std::bitset<::ui::kNumTouchEvdevSlots>* slots_to_suppress) override { + updateSuppressedSlots(touches); + *slots_to_suppress = mSuppressedSlots; + } + + std::string FilterNameForTesting() const override { return "test filter"; } + +private: + void updateSuppressedSlots(const std::vector<::ui::InProgressTouchEvdev>& touches) { + for (::ui::InProgressTouchEvdev touch : touches) { + for (const auto& [x, y] : mSuppressedPointers) { + const float dx = (touch.x - x); + const float dy = (touch.y - y); + const float distanceSquared = dx * dx + dy * dy; + if (distanceSquared < 1) { + mSuppressedSlots.set(touch.slot, true); + } + } + } + } + + std::bitset<::ui::kNumTouchEvdevSlots> mSuppressedSlots; + std::vector>& mSuppressedPointers; +}; + +class PalmRejectorFakeFilterTest : public testing::Test { +protected: + std::unique_ptr mPalmRejector; + + void SetUp() override { + std::unique_ptr<::ui::PalmDetectionFilter> filter = + std::make_unique(&mSharedPalmState, /*byref*/ mSuppressedPointers); + mPalmRejector = + std::make_unique(generatePalmFilterDeviceInfo(), std::move(filter)); + } + + void suppressPointerAtPosition(float x, float y) { mSuppressedPointers.push_back({x, y}); } + +private: + std::vector> mSuppressedPointers; + ::ui::SharedPalmDetectionFilterState mSharedPalmState; // unused, but we must retain ownership +}; + +/** + * When a MOVE event happens, the model identifies the pointer as palm. At that time, the palm + * rejector should send a POINTER_UP event for this pointer with FLAG_CANCELED, and subsequent + * events should have this pointer removed. + */ +TEST_F(PalmRejectorFakeFilterTest, OneOfTwoPointersIsCanceled) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel the second pointer + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(2u, argsList.size()); + // First event - cancel pointer 1 + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + // Second event - send MOVE for the remaining pointer + ASSERT_EQ(MOVE, argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // Future move events only contain 1 pointer, because the second pointer will continue + // to be suppressed + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(MOVE, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(1433, argsList[0].pointerCoords[0].getX()); + ASSERT_EQ(751, argsList[0].pointerCoords[0].getY()); +} + +/** + * Send two pointers, and suppress both of them. Check that ACTION_CANCEL is generated. + * Afterwards: + * 1) Future MOVE events are ignored. + * 2) When a new pointer goes down, ACTION_DOWN is generated + */ +TEST_F(PalmRejectorFakeFilterTest, NewDownEventAfterCancel) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Cancel both pointers + suppressPointerAtPosition(1059, 731); + suppressPointerAtPosition(1400, 680); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 1, MOVE, {{1400, 680, 41}, {1059, 731, 10}})); + ASSERT_EQ(1u, argsList.size()); + // Cancel all + ASSERT_EQ(CANCEL, argsList[0].action); + ASSERT_EQ(2u, argsList[0].pointerCount); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events are ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, MOVE, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}})); + ASSERT_EQ(0u, argsList.size()); + + // When a new pointer goes down, a new DOWN event is generated + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_2_DOWN, + {{1433.0, 751.0, 43.0}, {1072.0, 766.0, 13.0}, {1000, 700, 10}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(DOWN, argsList[0].action); + ASSERT_EQ(1u, argsList[0].pointerCount); + ASSERT_EQ(2, argsList[0].pointerProperties[0].id); +} + +/** + * 2 pointers are classified as palm simultaneously. When they are later + * released by Android, make sure that we drop both of these POINTER_UP events. + * Since they are classified as palm at the same time, we just need to receive a single CANCEL + * event. From MotionEvent docs: """A pointer id remains valid until the pointer eventually goes up + * (indicated by ACTION_UP or ACTION_POINTER_UP) or when the gesture is canceled (indicated by + * ACTION_CANCEL).""" + * This means that generating additional POINTER_UP events is not necessary. + * The risk here is that "oldSuppressedPointerIds" will not be correct, because it will update after + * each motion, but pointers are canceled one at a time by Android. + */ +TEST_F(PalmRejectorFakeFilterTest, TwoPointersCanceledWhenOnePointerGoesUp) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + // Suppress both pointers!! + suppressPointerAtPosition(1414, 702); + suppressPointerAtPosition(1059, 731); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783039000, POINTER_1_UP, + {{1414.0, 702.0, 41.0}, {1059.0, 731.0, 12.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action) << MotionEvent::actionToString(argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + // Future move events should not go to the listener. + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783049000, MOVE, {{1435.0, 755.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); + + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, 255955783059000, UP, {{1436.0, 756.0, 43.0}})); + ASSERT_EQ(0u, argsList.size()); +} + +/** + * Send 3 pointers, and then cancel one of them during a MOVE event. We should see ACTION_POINTER_UP + * generated for that. Next, another pointer is canceled during ACTION_POINTER_DOWN. For that + * pointer, we simply shouldn't send the event. + */ +TEST_F(PalmRejectorFakeFilterTest, CancelTwoPointers) { + std::vector argsList; + constexpr nsecs_t downTime = 0; + + mPalmRejector->processMotion( + generateMotionArgs(downTime, downTime, DOWN, {{1342.0, 613.0, 79.0}})); + mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + + // Suppress second pointer (pointer 1) + suppressPointerAtPosition(1060, 700); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, MOVE, + {{1417.0, 685.0, 41.0}, {1060, 700, 10.0}})); + ASSERT_EQ(2u, argsList.size()); + ASSERT_EQ(POINTER_1_UP, argsList[0].action); + ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, argsList[0].flags); + + ASSERT_EQ(MOVE, argsList[1].action) << MotionEvent::actionToString(argsList[1].action); + ASSERT_EQ(0, argsList[1].flags); + + // A new pointer goes down and gets suppressed right away. It should just be dropped + suppressPointerAtPosition(1001, 601); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_DOWN, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + + ASSERT_EQ(0u, argsList.size()); + // Likewise, pointer that's already canceled should be ignored + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_2_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}, {1001, 601, 5}})); + ASSERT_EQ(0u, argsList.size()); + + // Cancel all pointers when pointer 1 goes up. Pointer 1 was already canceled earlier. + suppressPointerAtPosition(1417, 685); + argsList = mPalmRejector->processMotion( + generateMotionArgs(downTime, /*eventTime*/ 1, POINTER_1_UP, + {{1417.0, 685.0, 41.0}, {1062.0, 697.0, 10.0}})); + ASSERT_EQ(1u, argsList.size()); + ASSERT_EQ(CANCEL, argsList[0].action); +} + +} // namespace android -- cgit v1.2.3-59-g8ed1b From 8576dd703e41f0d05a4f4635c675e1aedb77561a Mon Sep 17 00:00:00 2001 From: Vaibhav Date: Fri, 11 Feb 2022 18:19:06 +0530 Subject: Add new NDK function definitions in input.h This CL adds 2 new functions to get ActionButton and Classification from a MotionEvent. Test: atest android.view.cts.MotionEventTest Bug: 213266814 Change-Id: Id264a5d110c6c65a7daae89194ab234f64832e13 --- include/android/input.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/input/Input.h | 8 +++----- 2 files changed, 47 insertions(+), 5 deletions(-) (limited to 'include/input') diff --git a/include/android/input.h b/include/android/input.h index e6ad943f55..fb5e204450 100644 --- a/include/android/input.h +++ b/include/android/input.h @@ -807,6 +807,33 @@ enum { AMOTION_EVENT_TOOL_TYPE_PALM = 5, }; +/** + * Constants that identify different gesture classification types. + */ +enum { + /** + * Classification constant: None. + * + * No additional information is available about the current motion event stream. + */ + AMOTION_EVENT_CLASSIFICATION_NONE = 0, + /** + * Classification constant: Ambiguous gesture. + * + * The user's intent with respect to the current event stream is not yet determined. + * Gestural actions, such as scrolling, should be inhibited until the classification resolves + * to another value or the event stream ends. + */ + AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE = 1, + /** + * Classification constant: Deep press. + * + * The current event stream represents the user intentionally pressing harder on the screen. + * This classification type should be used to accelerate the long press behaviour. + */ + AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS = 2, +}; + /** * Input source masks. * @@ -1326,6 +1353,23 @@ float AMotionEvent_getHistoricalOrientation(const AInputEvent* motion_event, siz float AMotionEvent_getHistoricalAxisValue(const AInputEvent* motion_event, int32_t axis, size_t pointer_index, size_t history_index); +/** + * Get the action button for the motion event. Returns a valid action button when the + * event is associated with a button press or button release action. For other actions + * the return value is undefined. + */ +int32_t AMotionEvent_getActionButton(const AInputEvent* motion_event); + +/** + * Returns the classification for the current gesture. + * The classification may change as more events become available for the same gesture. + * + * @see #AMOTION_EVENT_CLASSIFICATION_NONE + * @see #AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE + * @see #AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS +*/ +int32_t AMotionEvent_getClassification(const AInputEvent* motion_event); + /** * Creates a native AInputEvent* object that is a copy of the specified Java * android.view.MotionEvent. The result may be used with generic and MotionEvent-specific diff --git a/include/input/Input.h b/include/input/Input.h index e421dee275..2837add22c 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -275,23 +275,21 @@ enum { /** * Classifications of the current gesture, if available. - * - * The following values must be kept in sync with MotionEvent.java */ enum class MotionClassification : uint8_t { /** * No classification is available. */ - NONE = 0, + NONE = AMOTION_EVENT_CLASSIFICATION_NONE, /** * Too early to classify the current gesture. Need more events. Look for changes in the * upcoming motion events. */ - AMBIGUOUS_GESTURE = 1, + AMBIGUOUS_GESTURE = AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE, /** * The current gesture likely represents a user intentionally exerting force on the touchscreen. */ - DEEP_PRESS = 2, + DEEP_PRESS = AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS, }; /** -- cgit v1.2.3-59-g8ed1b From 8e6ce224398316a809ac21ba661ba2c4ed9704c7 Mon Sep 17 00:00:00 2001 From: Prabir Pradhan Date: Thu, 24 Feb 2022 09:08:54 -0800 Subject: Fix input injection with zero coords In the native MotionEvent class, setting an axis value to 0 is equivalent to removing the axis from the bitfield of valid axes. This is because getting an axis value that is not set in the bitfield will return 0 by default. This means that we cannot rely on the bitfield of valid axes to know exactly which axes are valid, since all axes are always valid with a default value of 0. Rather than transforming only the axies that are set in the bitfield, we add a helper function to MotionEvent to transform the entire PointerCoords. Bug: 219711163 Test: manual, see bug: adb shell input draganddrop 665 531 0 531 1000 Change-Id: I335beebf8263a38f180f2f4c6a788fbd69d15a6f Merged-In: I335beebf8263a38f180f2f4c6a788fbd69d15a6f (cherry picked from commit 890532e49c0e329c0c462fb2733c5ae27a395b7b) --- include/input/Input.h | 2 ++ libs/input/Input.cpp | 29 ++++++++++++++++++++++ .../inputflinger/dispatcher/InputDispatcher.cpp | 16 +++--------- 3 files changed, 34 insertions(+), 13 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index 2837add22c..b23a9518f7 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -814,6 +814,8 @@ public: static vec2 calculateTransformedXY(uint32_t source, const ui::Transform&, const vec2& xy); static float calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform&, const PointerCoords&); + static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&, + const PointerCoords&); protected: int32_t mAction; diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index 3073d94dbe..fe1754c78b 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -846,6 +846,7 @@ vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& t return calculateTransformedXYUnchecked(source, transform, xy); } +// Keep in sync with calculateTransformedCoords. float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, const ui::Transform& transform, const PointerCoords& coords) { @@ -874,6 +875,34 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, return coords.getAxisValue(axis); } +// Keep in sync with calculateTransformedAxisValue. This is an optimization of +// calculateTransformedAxisValue for all PointerCoords axes. +PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, + const ui::Transform& transform, + const PointerCoords& coords) { + if (shouldDisregardTransformation(source)) { + return coords; + } + PointerCoords out = coords; + + const vec2 xy = calculateTransformedXYUnchecked(source, transform, coords.getXYValue()); + out.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + out.setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); + + const vec2 relativeXy = + transformWithoutTranslation(transform, + {coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), + coords.getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)}); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); + out.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); + + out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + transformAngle(transform, + coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION))); + + return out; +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus) { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 58c93036bb..2eb9bd35fc 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -4426,19 +4426,9 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( const auto& transformToDisplay = it->second.transform.inverse() * injectedTransform; for (uint32_t i = 0; i < entry.pointerCount; i++) { - PointerCoords& pc = entry.pointerCoords[i]; - // Make a copy of the injected coords. We cannot change them in place because some of them - // are interdependent (for example, X coordinate might depend on the Y coordinate). - PointerCoords injectedCoords = entry.pointerCoords[i]; - - BitSet64 bits(injectedCoords.bits); - while (!bits.isEmpty()) { - const auto axis = static_cast(bits.clearFirstMarkedBit()); - const float value = - MotionEvent::calculateTransformedAxisValue(axis, entry.source, - transformToDisplay, injectedCoords); - pc.setAxisValue(axis, value); - } + entry.pointerCoords[i] = + MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay, + entry.pointerCoords[i]); } } -- cgit v1.2.3-59-g8ed1b From 814ace3f256109b59ee9dc1d881887c4cb8b17ae Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Fri, 4 Mar 2022 15:12:16 -0800 Subject: Add PreferStylusOverTouchBlocker and handle multiple devices We removed PreferStylusOverTouchBlocker previously in order to avoid a crash. In this CL, we are adding it back in, and handling the case of input device having "SOURCE_STYLUS", but reporting "finger" tool type. If there's a stylus event with one of the pointers labeled as 'finger', let's assume that the device supports simultaneous touch and stylus. For this situation, simply disable PreferStylusOverTouchBlocker going forward for these devices, and pass through any events coming from there. Currently, this happens on emulator. In their touch driver, they configure stylus properties as well as touch properties, but most of the events that they send are TOOL_TYPE_FINGER. Previously, this triggered a crash in PreferStylusOverTouchBlocker. Bug: 222531989 Test: atest inputflinger_tests Change-Id: Ifbb08858a4dfebc95c30ca19d6e68533855db7e4 (cherry picked from commit a6a660fc0aa74ea4f5930b74523cf1893b2f9282) --- include/input/PrintTools.h | 61 +++++ libs/input/Android.bp | 4 + libs/input/PrintTools.cpp | 27 ++ services/inputflinger/InputListener.cpp | 6 +- .../inputflinger/PreferStylusOverTouchBlocker.cpp | 207 +++++++++++---- .../inputflinger/PreferStylusOverTouchBlocker.h | 62 ++--- .../inputflinger/UnwantedInteractionBlocker.cpp | 12 + services/inputflinger/UnwantedInteractionBlocker.h | 7 + .../tests/PreferStylusOverTouch_test.cpp | 294 +++++++++++++++++++-- .../tests/UnwantedInteractionBlocker_test.cpp | 28 ++ 10 files changed, 597 insertions(+), 111 deletions(-) create mode 100644 include/input/PrintTools.h create mode 100644 libs/input/PrintTools.cpp (limited to 'include/input') diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h new file mode 100644 index 0000000000..7c3b29b55f --- /dev/null +++ b/include/input/PrintTools.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +namespace android { + +template +std::string constToString(const T& v) { + return std::to_string(v); +} + +/** + * Convert a set of integral types to string. + */ +template +std::string dumpSet(const std::set& v, std::string (*toString)(const T&) = constToString) { + std::string out; + for (const T& entry : v) { + out += out.empty() ? "{" : ", "; + out += toString(entry); + } + return out.empty() ? "{}" : (out + "}"); +} + +/** + * Convert a map to string. Both keys and values of the map should be integral type. + */ +template +std::string dumpMap(const std::map& map, std::string (*keyToString)(const K&) = constToString, + std::string (*valueToString)(const V&) = constToString) { + std::string out; + for (const auto& [k, v] : map) { + if (!out.empty()) { + out += "\n"; + } + out += keyToString(k) + ":" + valueToString(v); + } + return out; +} + +const char* toString(bool value); + +} // namespace android \ No newline at end of file diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 930d8194d5..b188ea2921 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -49,6 +49,7 @@ cc_library { "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "PrintTools.cpp", "PropertyMap.cpp", "TouchVideoFrame.cpp", "VelocityControl.cpp", @@ -104,6 +105,9 @@ cc_library { sanitize: { misc_undefined: ["integer"], + diag: { + misc_undefined: ["integer"], + }, }, }, host: { diff --git a/libs/input/PrintTools.cpp b/libs/input/PrintTools.cpp new file mode 100644 index 0000000000..5d6ae4ed91 --- /dev/null +++ b/libs/input/PrintTools.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "PrintTools" + +#include + +namespace android { + +const char* toString(bool value) { + return value ? "true" : "false"; +} + +} // namespace android diff --git a/services/inputflinger/InputListener.cpp b/services/inputflinger/InputListener.cpp index 73b63e3141..48cc413098 100644 --- a/services/inputflinger/InputListener.cpp +++ b/services/inputflinger/InputListener.cpp @@ -202,9 +202,11 @@ std::string NotifyMotionArgs::dump() const { coords += "}"; } return StringPrintf("NotifyMotionArgs(id=%" PRId32 ", eventTime=%" PRId64 ", deviceId=%" PRId32 - ", source=%s, action=%s, pointerCount=%" PRIu32 " pointers=%s)", + ", source=%s, action=%s, pointerCount=%" PRIu32 + " pointers=%s, flags=0x%08x)", id, eventTime, deviceId, inputEventSourceToString(source).c_str(), - MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str()); + MotionEvent::actionToString(action).c_str(), pointerCount, coords.c_str(), + flags); } void NotifyMotionArgs::notify(InputListenerInterface& listener) const { diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.cpp b/services/inputflinger/PreferStylusOverTouchBlocker.cpp index ad639b4ef8..beec2e162e 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.cpp +++ b/services/inputflinger/PreferStylusOverTouchBlocker.cpp @@ -15,78 +15,163 @@ */ #include "PreferStylusOverTouchBlocker.h" +#include -#include +namespace android { -using android::base::StringPrintf; +static std::pair checkToolType(const NotifyMotionArgs& args) { + bool hasStylus = false; + bool hasTouch = false; + for (size_t i = 0; i < args.pointerCount; i++) { + // Make sure we are canceling stylus pointers + const int32_t toolType = args.pointerProperties[i].toolType; + if (toolType == AMOTION_EVENT_TOOL_TYPE_STYLUS || + toolType == AMOTION_EVENT_TOOL_TYPE_ERASER) { + hasStylus = true; + } + if (toolType == AMOTION_EVENT_TOOL_TYPE_FINGER) { + hasTouch = true; + } + } + return std::make_pair(hasTouch, hasStylus); +} -static const char* toString(bool value) { - return value ? "true" : "false"; +/** + * Intersect two sets in-place, storing the result in 'set1'. + * Find elements in set1 that are not present in set2 and delete them, + * relying on the fact that the two sets are ordered. + */ +template +static void intersectInPlace(std::set& set1, const std::set& set2) { + typename std::set::iterator it1 = set1.begin(); + typename std::set::const_iterator it2 = set2.begin(); + while (it1 != set1.end() && it2 != set2.end()) { + const T& element1 = *it1; + const T& element2 = *it2; + if (element1 < element2) { + // This element is not present in set2. Remove it from set1. + it1 = set1.erase(it1); + continue; + } + if (element2 < element1) { + it2++; + } + if (element1 == element2) { + it1++; + it2++; + } + } + // Remove the rest of the elements in set1 because set2 is already exhausted. + set1.erase(it1, set1.end()); } -namespace android { +/** + * Same as above, but prune a map + */ +template +static void intersectInPlace(std::map& map, const std::set& set2) { + typename std::map::iterator it1 = map.begin(); + typename std::set::const_iterator it2 = set2.begin(); + while (it1 != map.end() && it2 != set2.end()) { + const auto& [key, _] = *it1; + const K& element2 = *it2; + if (key < element2) { + // This element is not present in set2. Remove it from map. + it1 = map.erase(it1); + continue; + } + if (element2 < key) { + it2++; + } + if (key == element2) { + it1++; + it2++; + } + } + // Remove the rest of the elements in map because set2 is already exhausted. + map.erase(it1, map.end()); +} + +// -------------------------------- PreferStylusOverTouchBlocker ----------------------------------- -ftl::StaticVector PreferStylusOverTouchBlocker::processMotion( +std::vector PreferStylusOverTouchBlocker::processMotion( const NotifyMotionArgs& args) { - const bool isStylusEvent = isFromSource(args.source, AINPUT_SOURCE_STYLUS); - if (isStylusEvent) { - for (size_t i = 0; i < args.pointerCount; i++) { - // Make sure we are canceling stylus pointers - const int32_t toolType = args.pointerProperties[i].toolType; - LOG_ALWAYS_FATAL_IF(toolType != AMOTION_EVENT_TOOL_TYPE_STYLUS && - toolType != AMOTION_EVENT_TOOL_TYPE_ERASER, - "The pointer %zu has toolType=%i, but the source is STYLUS. If " - "simultaneous touch and stylus is supported, " - "'PreferStylusOverTouchBlocker' should be disabled.", - i, toolType); + const auto [hasTouch, hasStylus] = checkToolType(args); + const bool isUpOrCancel = + args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL; + + if (hasTouch && hasStylus) { + mDevicesWithMixedToolType.insert(args.deviceId); + } + // Handle the case where mixed touch and stylus pointers are reported. Add this device to the + // ignore list, since it clearly supports simultaneous touch and stylus. + if (mDevicesWithMixedToolType.find(args.deviceId) != mDevicesWithMixedToolType.end()) { + // This event comes from device with mixed stylus and touch event. Ignore this device. + if (mCanceledDevices.find(args.deviceId) != mCanceledDevices.end()) { + // If we started to cancel events from this device, continue to do so to keep + // the stream consistent. It should happen at most once per "mixed" device. + if (isUpOrCancel) { + mCanceledDevices.erase(args.deviceId); + mLastTouchEvents.erase(args.deviceId); + } + return {}; } + return {args}; } + + const bool isStylusEvent = hasStylus; const bool isDown = args.action == AMOTION_EVENT_ACTION_DOWN; - const bool isUpOrCancel = - args.action == AMOTION_EVENT_ACTION_UP || args.action == AMOTION_EVENT_ACTION_CANCEL; + if (isStylusEvent) { if (isDown) { // Reject all touch while stylus is down - mIsStylusDown = true; - if (mIsTouchDown && !mCurrentTouchIsCanceled) { - // Cancel touch! - mCurrentTouchIsCanceled = true; - mLastTouchEvent.action = AMOTION_EVENT_ACTION_CANCEL; - mLastTouchEvent.flags |= AMOTION_EVENT_FLAG_CANCELED; - mLastTouchEvent.eventTime = systemTime(SYSTEM_TIME_MONOTONIC); - return {mLastTouchEvent, args}; + mActiveStyli.insert(args.deviceId); + + // Cancel all current touch! + std::vector result; + for (auto& [deviceId, lastTouchEvent] : mLastTouchEvents) { + if (mCanceledDevices.find(deviceId) != mCanceledDevices.end()) { + // Already canceled, go to next one. + continue; + } + // Not yet canceled. Cancel it. + lastTouchEvent.action = AMOTION_EVENT_ACTION_CANCEL; + lastTouchEvent.flags |= AMOTION_EVENT_FLAG_CANCELED; + lastTouchEvent.eventTime = systemTime(SYSTEM_TIME_MONOTONIC); + result.push_back(lastTouchEvent); + mCanceledDevices.insert(deviceId); } + result.push_back(args); + return result; } if (isUpOrCancel) { - mIsStylusDown = false; + mActiveStyli.erase(args.deviceId); } // Never drop stylus events return {args}; } - const bool isTouchEvent = - isFromSource(args.source, AINPUT_SOURCE_TOUCHSCREEN) && !isStylusEvent; + const bool isTouchEvent = hasTouch; if (isTouchEvent) { - if (mIsStylusDown) { - mCurrentTouchIsCanceled = true; + // Suppress the current gesture if any stylus is still down + if (!mActiveStyli.empty()) { + mCanceledDevices.insert(args.deviceId); + } + + const bool shouldDrop = mCanceledDevices.find(args.deviceId) != mCanceledDevices.end(); + if (isUpOrCancel) { + mCanceledDevices.erase(args.deviceId); + mLastTouchEvents.erase(args.deviceId); } + // If we already canceled the current gesture, then continue to drop events from it, even if // the stylus has been lifted. - if (mCurrentTouchIsCanceled) { - if (isUpOrCancel) { - mCurrentTouchIsCanceled = false; - } + if (shouldDrop) { return {}; } - // Update state - mLastTouchEvent = args; - if (isDown) { - mIsTouchDown = true; - } - if (isUpOrCancel) { - mIsTouchDown = false; - mCurrentTouchIsCanceled = false; + if (!isUpOrCancel) { + mLastTouchEvents[args.deviceId] = args; } return {args}; } @@ -95,12 +180,36 @@ ftl::StaticVector PreferStylusOverTouchBlocker::processMoti return {args}; } -std::string PreferStylusOverTouchBlocker::dump() { +void PreferStylusOverTouchBlocker::notifyInputDevicesChanged( + const std::vector& inputDevices) { + std::set presentDevices; + for (const InputDeviceInfo& device : inputDevices) { + presentDevices.insert(device.getId()); + } + // Only keep the devices that are still present. + intersectInPlace(mDevicesWithMixedToolType, presentDevices); + intersectInPlace(mLastTouchEvents, presentDevices); + intersectInPlace(mCanceledDevices, presentDevices); + intersectInPlace(mActiveStyli, presentDevices); +} + +void PreferStylusOverTouchBlocker::notifyDeviceReset(const NotifyDeviceResetArgs& args) { + mDevicesWithMixedToolType.erase(args.deviceId); + mLastTouchEvents.erase(args.deviceId); + mCanceledDevices.erase(args.deviceId); + mActiveStyli.erase(args.deviceId); +} + +static std::string dumpArgs(const NotifyMotionArgs& args) { + return args.dump(); +} + +std::string PreferStylusOverTouchBlocker::dump() const { std::string out; - out += StringPrintf("mIsTouchDown: %s\n", toString(mIsTouchDown)); - out += StringPrintf("mIsStylusDown: %s\n", toString(mIsStylusDown)); - out += StringPrintf("mLastTouchEvent: %s\n", mLastTouchEvent.dump().c_str()); - out += StringPrintf("mCurrentTouchIsCanceled: %s\n", toString(mCurrentTouchIsCanceled)); + out += "mActiveStyli: " + dumpSet(mActiveStyli) + "\n"; + out += "mLastTouchEvents: " + dumpMap(mLastTouchEvents, constToString, dumpArgs) + "\n"; + out += "mDevicesWithMixedToolType: " + dumpSet(mDevicesWithMixedToolType) + "\n"; + out += "mCanceledDevices: " + dumpSet(mCanceledDevices) + "\n"; return out; } diff --git a/services/inputflinger/PreferStylusOverTouchBlocker.h b/services/inputflinger/PreferStylusOverTouchBlocker.h index 3f5616190b..716dc4d351 100644 --- a/services/inputflinger/PreferStylusOverTouchBlocker.h +++ b/services/inputflinger/PreferStylusOverTouchBlocker.h @@ -16,66 +16,50 @@ #pragma once -#include #include +#include #include "InputListener.h" namespace android { /** - * When stylus is down, we ignore all touch. + * When stylus is down, all touch is ignored. * TODO(b/210159205): delete this when simultaneous stylus and touch is supported */ class PreferStylusOverTouchBlocker { public: /** - * Process the provided event and emit up to 2 events in response. + * Process the provided event and emit 0 or more events that should be used instead of it. * In the majority of cases, the returned result will just be the provided args (array with * only 1 element), unmodified. * * If the gesture should be blocked, the returned result may be: * * a) An empty array, if the current event should just be ignored completely - * b) An array of 2 elements, containing an event with ACTION_CANCEL and the current event. + * b) An array of N elements, containing N-1 events with ACTION_CANCEL and the current event. * - * bool is set to 'true'. - * NotifyMotionArgs potentially contains an event that should be used to cancel the existing - * gesture. - * - * If the event should not be blocked, bool contains 'false'. + * The returned result is intended to be reinjected into the original event stream in + * replacement of the incoming event. */ - ftl::StaticVector processMotion(const NotifyMotionArgs& args); - std::string dump(); + std::vector processMotion(const NotifyMotionArgs& args); + std::string dump() const; + + void notifyInputDevicesChanged(const std::vector& inputDevices); + + void notifyDeviceReset(const NotifyDeviceResetArgs& args); private: - bool mIsTouchDown = false; - bool mIsStylusDown = false; - // Provide some default values for the stored MotionEvent to allow printint the event before - // any real event is received. - NotifyMotionArgs mLastTouchEvent{0 /*id*/, - 0 /*eventTime*/, - 0 /*readTime*/, - 0 /*deviceId*/, - AINPUT_SOURCE_TOUCHSCREEN, - 0 /*displayId*/, - 0 /*policyFlags*/, - 0 /*action*/, - 0 /*actionButton*/, - 0 /*flags*/, - 0 /*metaState*/, - 0 /*buttonState*/, - MotionClassification::NONE, - AMOTION_EVENT_EDGE_FLAG_NONE, - 0 /*pointerCount*/, - nullptr /*properties*/, - nullptr /*coords*/, - 0. /*xPrecision*/, - 0. /*yPrecision*/, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - AMOTION_EVENT_INVALID_CURSOR_POSITION, - 0 /*downTime*/, - {}}; - bool mCurrentTouchIsCanceled = false; + // Stores the device id's of styli that are currently down. + std::set mActiveStyli; + // For each device, store the last touch event as long as the touch is down. Upon liftoff, + // the entry is erased. + std::map mLastTouchEvents; + // Device ids of devices for which the current touch gesture is canceled. + std::set mCanceledDevices; + + // Device ids of input devices where we encountered simultaneous touch and stylus + // events. For these devices, we don't do any event processing (nothing is blocked or altered). + std::set mDevicesWithMixedToolType; }; } // namespace android \ No newline at end of file diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index fb3962e544..b69e16ac85 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -368,6 +368,14 @@ void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { } void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { + const std::vector processedArgs = + mPreferStylusOverTouchBlocker.processMotion(*args); + for (const NotifyMotionArgs& loopArgs : processedArgs) { + notifyMotionInner(&loopArgs); + } +} + +void UnwantedInteractionBlocker::notifyMotionInner(const NotifyMotionArgs* args) { auto it = mPalmRejectors.find(args->deviceId); const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); if (!sendToPalmRejector) { @@ -401,6 +409,7 @@ void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* mPalmRejectors.emplace(args->deviceId, info); } mListener.notifyDeviceReset(args); + mPreferStylusOverTouchBlocker.notifyDeviceReset(*args); } void UnwantedInteractionBlocker::notifyPointerCaptureChanged( @@ -437,10 +446,13 @@ void UnwantedInteractionBlocker::notifyInputDevicesChanged( auto const& [deviceId, _] = item; return devicesToKeep.find(deviceId) == devicesToKeep.end(); }); + mPreferStylusOverTouchBlocker.notifyInputDevicesChanged(inputDevices); } void UnwantedInteractionBlocker::dump(std::string& dump) { dump += "UnwantedInteractionBlocker:\n"; + dump += " mPreferStylusOverTouchBlocker:\n"; + dump += addPrefix(mPreferStylusOverTouchBlocker.dump(), " "); dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", toString(isPalmRejectionEnabled())); diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h index 14068fd878..8a1cd7265e 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.h +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -23,6 +23,8 @@ #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" #include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" +#include "PreferStylusOverTouchBlocker.h" + namespace android { // --- Functions for manipulation of event streams @@ -88,9 +90,14 @@ private: InputListenerInterface& mListener; const bool mEnablePalmRejection; + // When stylus is down, ignore touch + PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker; + // Detect and reject unwanted palms on screen // Use a separate palm rejector for every touch device. std::map mPalmRejectors; + // TODO(b/210159205): delete this when simultaneous stylus and touch is supported + void notifyMotionInner(const NotifyMotionArgs* args); }; class SlotState { diff --git a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp index 70f40aa028..8e2ab88e80 100644 --- a/services/inputflinger/tests/PreferStylusOverTouch_test.cpp +++ b/services/inputflinger/tests/PreferStylusOverTouch_test.cpp @@ -20,12 +20,16 @@ namespace android { constexpr int32_t TOUCH_DEVICE_ID = 3; -constexpr int32_t STYLUS_DEVICE_ID = 4; +constexpr int32_t SECOND_TOUCH_DEVICE_ID = 4; +constexpr int32_t STYLUS_DEVICE_ID = 5; +constexpr int32_t SECOND_STYLUS_DEVICE_ID = 6; constexpr int DOWN = AMOTION_EVENT_ACTION_DOWN; constexpr int MOVE = AMOTION_EVENT_ACTION_MOVE; constexpr int UP = AMOTION_EVENT_ACTION_UP; constexpr int CANCEL = AMOTION_EVENT_ACTION_CANCEL; +static constexpr int32_t POINTER_1_DOWN = + AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); constexpr int32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN; constexpr int32_t STYLUS = AINPUT_SOURCE_STYLUS; @@ -78,29 +82,30 @@ static NotifyMotionArgs generateMotionArgs(nsecs_t downTime, nsecs_t eventTime, class PreferStylusOverTouchTest : public testing::Test { protected: - void assertNotBlocked(const NotifyMotionArgs& args) { - ftl::StaticVector processedArgs = mBlocker.processMotion(args); - ASSERT_EQ(1u, processedArgs.size()); - ASSERT_EQ(args, processedArgs[0]); + void assertNotBlocked(const NotifyMotionArgs& args) { assertResponse(args, {args}); } + + void assertDropped(const NotifyMotionArgs& args) { assertResponse(args, {}); } + + void assertResponse(const NotifyMotionArgs& args, + const std::vector& expected) { + std::vector receivedArgs = mBlocker.processMotion(args); + ASSERT_EQ(expected.size(), receivedArgs.size()); + for (size_t i = 0; i < expected.size(); i++) { + // The 'eventTime' of CANCEL events is dynamically generated. Don't check this field. + if (expected[i].action == CANCEL && receivedArgs[i].action == CANCEL) { + receivedArgs[i].eventTime = expected[i].eventTime; + } + + ASSERT_EQ(expected[i], receivedArgs[i]) + << expected[i].dump() << " vs " << receivedArgs[i].dump(); + } } - void assertDropped(const NotifyMotionArgs& args) { - ftl::StaticVector processedArgs = mBlocker.processMotion(args); - ASSERT_TRUE(processedArgs.empty()); + void notifyInputDevicesChanged(const std::vector& devices) { + mBlocker.notifyInputDevicesChanged(devices); } - void assertCanceled(const NotifyMotionArgs& args, - std::optional canceledArgs) { - ftl::StaticVector processedArgs = mBlocker.processMotion(args); - ASSERT_EQ(2u, processedArgs.size()); - NotifyMotionArgs& cancelEvent = processedArgs[0]; - ASSERT_EQ(CANCEL, cancelEvent.action); - ASSERT_EQ(AMOTION_EVENT_FLAG_CANCELED, cancelEvent.flags & AMOTION_EVENT_FLAG_CANCELED); - ASSERT_TRUE(isFromSource(cancelEvent.source, TOUCHSCREEN)); - ASSERT_FALSE(isFromSource(cancelEvent.source, STYLUS)); - - ASSERT_EQ(args, processedArgs[1]); - } + void dump() const { ALOGI("Blocker: \n%s\n", mBlocker.dump().c_str()); } private: PreferStylusOverTouchBlocker mBlocker; @@ -148,7 +153,8 @@ TEST_F(PreferStylusOverTouchTest, TouchIsCanceledWhenStylusGoesDown) { args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS); NotifyMotionArgs cancelArgs = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, CANCEL, {{1, 3}}, TOUCHSCREEN); - assertCanceled(args, cancelArgs); + cancelArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + assertResponse(args, {cancelArgs, args}); // Both stylus and touch events continue. Stylus should be not blocked, and touch should be // blocked @@ -159,6 +165,26 @@ TEST_F(PreferStylusOverTouchTest, TouchIsCanceledWhenStylusGoesDown) { assertDropped(args); } +/** + * Stylus goes down after touch gesture. + */ +TEST_F(PreferStylusOverTouchTest, StylusDownAfterTouch) { + NotifyMotionArgs args; + + args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + assertNotBlocked(args); + + args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{1, 3}}, TOUCHSCREEN); + assertNotBlocked(args); + + args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{1, 3}}, TOUCHSCREEN); + assertNotBlocked(args); + + // Stylus goes down + args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS); + assertNotBlocked(args); +} + /** * New touch events should be simply blocked (dropped) when stylus is down. No CANCEL event should * be generated. @@ -247,4 +273,230 @@ TEST_F(PreferStylusOverTouchTest, AfterStylusIsLiftedCurrentTouchIsBlocked) { assertNotBlocked(args); } +/** + * If an event with mixed stylus and touch pointers is encountered, it should be ignored. Touches + * from such should pass, even if stylus from the same device goes down. + */ +TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchPointersAreIgnored) { + NotifyMotionArgs args; + + // Event from a stylus device, but with finger tool type + args = generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, STYLUS); + // Keep source stylus, but make the tool type touch + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + assertNotBlocked(args); + + // Second pointer (stylus pointer) goes down, from the same device + args = generateMotionArgs(1 /*downTime*/, 2 /*eventTime*/, POINTER_1_DOWN, {{1, 2}, {10, 20}}, + STYLUS); + // Keep source stylus, but make the tool type touch + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + assertNotBlocked(args); + + // Second pointer (stylus pointer) goes down, from the same device + args = generateMotionArgs(1 /*downTime*/, 3 /*eventTime*/, MOVE, {{2, 3}, {11, 21}}, STYLUS); + // Keep source stylus, but make the tool type touch + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + assertNotBlocked(args); +} + +/** + * When there are two touch devices, stylus down should cancel all current touch streams. + */ +TEST_F(PreferStylusOverTouchTest, TouchFromTwoDevicesAndStylus) { + NotifyMotionArgs touch1Down = + generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + assertNotBlocked(touch1Down); + + NotifyMotionArgs touch2Down = + generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{3, 4}}, TOUCHSCREEN); + touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID; + assertNotBlocked(touch2Down); + + NotifyMotionArgs stylusDown = + generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 30}}, STYLUS); + NotifyMotionArgs cancelArgs1 = touch1Down; + cancelArgs1.action = CANCEL; + cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED; + NotifyMotionArgs cancelArgs2 = touch2Down; + cancelArgs2.action = CANCEL; + cancelArgs2.flags |= AMOTION_EVENT_FLAG_CANCELED; + assertResponse(stylusDown, {cancelArgs1, cancelArgs2, stylusDown}); +} + +/** + * Touch should be canceled when stylus goes down. After the stylus lifts up, the touch from that + * device should continue to be canceled. + * If one of the devices is already canceled, it should remain canceled, but new touches from a + * different device should go through. + */ +TEST_F(PreferStylusOverTouchTest, AllTouchMustLiftAfterCanceledByStylus) { + // First device touches down + NotifyMotionArgs touch1Down = + generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + assertNotBlocked(touch1Down); + + // Stylus goes down - touch should be canceled + NotifyMotionArgs stylusDown = + generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{10, 30}}, STYLUS); + NotifyMotionArgs cancelArgs1 = touch1Down; + cancelArgs1.action = CANCEL; + cancelArgs1.flags |= AMOTION_EVENT_FLAG_CANCELED; + assertResponse(stylusDown, {cancelArgs1, stylusDown}); + + // Stylus goes up + NotifyMotionArgs stylusUp = + generateMotionArgs(2 /*downTime*/, 3 /*eventTime*/, UP, {{10, 30}}, STYLUS); + assertNotBlocked(stylusUp); + + // Touch from the first device remains blocked + NotifyMotionArgs touch1Move = + generateMotionArgs(1 /*downTime*/, 4 /*eventTime*/, MOVE, {{2, 3}}, TOUCHSCREEN); + assertDropped(touch1Move); + + // Second touch goes down. It should not be blocked because stylus has already lifted. + NotifyMotionArgs touch2Down = + generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{31, 32}}, TOUCHSCREEN); + touch2Down.deviceId = SECOND_TOUCH_DEVICE_ID; + assertNotBlocked(touch2Down); + + // First device is lifted up. It's already been canceled, so the UP event should be dropped. + NotifyMotionArgs touch1Up = + generateMotionArgs(1 /*downTime*/, 6 /*eventTime*/, UP, {{2, 3}}, TOUCHSCREEN); + assertDropped(touch1Up); + + // Touch from second device touch should continue to work + NotifyMotionArgs touch2Move = + generateMotionArgs(5 /*downTime*/, 7 /*eventTime*/, MOVE, {{32, 33}}, TOUCHSCREEN); + touch2Move.deviceId = SECOND_TOUCH_DEVICE_ID; + assertNotBlocked(touch2Move); + + // Second touch lifts up + NotifyMotionArgs touch2Up = + generateMotionArgs(5 /*downTime*/, 8 /*eventTime*/, UP, {{32, 33}}, TOUCHSCREEN); + touch2Up.deviceId = SECOND_TOUCH_DEVICE_ID; + assertNotBlocked(touch2Up); + + // Now that all touch has been lifted, new touch from either first or second device should work + NotifyMotionArgs touch3Down = + generateMotionArgs(9 /*downTime*/, 9 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + assertNotBlocked(touch3Down); + + NotifyMotionArgs touch4Down = + generateMotionArgs(10 /*downTime*/, 10 /*eventTime*/, DOWN, {{100, 200}}, TOUCHSCREEN); + touch4Down.deviceId = SECOND_TOUCH_DEVICE_ID; + assertNotBlocked(touch4Down); +} + +/** + * When we don't know that a specific device does both stylus and touch, and we only see touch + * pointers from it, we should treat it as a touch device. That means, the device events should be + * canceled when stylus from another device goes down. When we detect simultaneous touch and stylus + * from this device though, we should just pass this device through without canceling anything. + * + * In this test: + * 1. Start by touching down with device 1 + * 2. Device 2 has stylus going down + * 3. Device 1 should be canceled. + * 4. When we add stylus pointers to the device 1, they should continue to be canceled. + * 5. Device 1 lifts up. + * 6. Subsequent events from device 1 should not be canceled even if stylus is down. + * 7. If a reset happens, and such device is no longer there, then we should + * Therefore, the device 1 is "ignored" and does not participate into "prefer stylus over touch" + * behaviour. + */ +TEST_F(PreferStylusOverTouchTest, MixedStylusAndTouchDeviceIsCanceledAtFirst) { + // Touch from device 1 goes down + NotifyMotionArgs touchDown = + generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + touchDown.source = STYLUS; + assertNotBlocked(touchDown); + + // Stylus from device 2 goes down. Touch should be canceled. + NotifyMotionArgs args = + generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{10, 20}}, STYLUS); + NotifyMotionArgs cancelTouchArgs = touchDown; + cancelTouchArgs.action = CANCEL; + cancelTouchArgs.flags |= AMOTION_EVENT_FLAG_CANCELED; + assertResponse(args, {cancelTouchArgs, args}); + + // Introduce a stylus pointer into the device 1 stream. It should be ignored. + args = generateMotionArgs(1 /*downTime*/, 3 /*eventTime*/, POINTER_1_DOWN, {{1, 2}, {3, 4}}, + TOUCHSCREEN); + args.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + args.source = STYLUS; + assertDropped(args); + + // Lift up touch from the mixed touch/stylus device + args = generateMotionArgs(1 /*downTime*/, 4 /*eventTime*/, CANCEL, {{1, 2}, {3, 4}}, + TOUCHSCREEN); + args.pointerProperties[1].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + args.source = STYLUS; + assertDropped(args); + + // Stylus from device 2 is still down. Since the device 1 is now identified as a mixed + // touch/stylus device, its events should go through, even if they are touch. + args = generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{21, 22}}, TOUCHSCREEN); + touchDown.source = STYLUS; + assertResponse(args, {args}); + + // Reconfigure such that only the stylus device remains + InputDeviceInfo stylusDevice; + stylusDevice.initialize(STYLUS_DEVICE_ID, 1 /*generation*/, 1 /*controllerNumber*/, + {} /*identifier*/, "stylus device", false /*external*/, + false /*hasMic*/); + notifyInputDevicesChanged({stylusDevice}); + // The touchscreen device was removed, so we no longer remember anything about it. We should + // again start blocking touch events from it. + args = generateMotionArgs(6 /*downTime*/, 6 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + args.source = STYLUS; + assertDropped(args); +} + +/** + * If two styli are active at the same time, touch should be blocked until both of them are lifted. + * If one of them lifts, touch should continue to be blocked. + */ +TEST_F(PreferStylusOverTouchTest, TouchIsBlockedWhenTwoStyliAreUsed) { + NotifyMotionArgs args; + + // First stylus is down + assertNotBlocked(generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{10, 30}}, STYLUS)); + + // Second stylus is down + args = generateMotionArgs(1 /*downTime*/, 1 /*eventTime*/, DOWN, {{20, 40}}, STYLUS); + args.deviceId = SECOND_STYLUS_DEVICE_ID; + assertNotBlocked(args); + + // Touch goes down. It should be ignored. + args = generateMotionArgs(2 /*downTime*/, 2 /*eventTime*/, DOWN, {{1, 2}}, TOUCHSCREEN); + assertDropped(args); + + // Lift the first stylus + args = generateMotionArgs(0 /*downTime*/, 3 /*eventTime*/, UP, {{10, 30}}, STYLUS); + assertNotBlocked(args); + + // Touch should continue to be blocked + args = generateMotionArgs(2 /*downTime*/, 4 /*eventTime*/, UP, {{1, 2}}, TOUCHSCREEN); + assertDropped(args); + + // New touch should be blocked because second stylus is still down + args = generateMotionArgs(5 /*downTime*/, 5 /*eventTime*/, DOWN, {{5, 6}}, TOUCHSCREEN); + assertDropped(args); + + // Second stylus goes up + args = generateMotionArgs(1 /*downTime*/, 6 /*eventTime*/, UP, {{20, 40}}, STYLUS); + args.deviceId = SECOND_STYLUS_DEVICE_ID; + assertNotBlocked(args); + + // Current touch gesture should continue to be blocked + // Touch should continue to be blocked + args = generateMotionArgs(5 /*downTime*/, 7 /*eventTime*/, UP, {{5, 6}}, TOUCHSCREEN); + assertDropped(args); + + // Now that all styli were lifted, new touch should go through + args = generateMotionArgs(8 /*downTime*/, 8 /*eventTime*/, DOWN, {{7, 8}}, TOUCHSCREEN); + assertNotBlocked(args); +} + } // namespace android diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index b2f8eb37f0..e378096df5 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -519,6 +519,34 @@ TEST_F(UnwantedInteractionBlockerTest, NoResetIfDeviceInfoChanges) { &(args = generateMotionArgs(0 /*downTime*/, 4 /*eventTime*/, MOVE, {{7, 8, 9}}))); } +/** + * Send a touch event, and then a stylus event. Make sure that both work. + */ +TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) { + NotifyMotionArgs args; + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + args = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&args); + args = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{4, 5, 6}}); + mBlocker->notifyMotion(&args); + args = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{4, 5, 6}}); + mBlocker->notifyMotion(&args); + + // Now touch down stylus + args = generateMotionArgs(3 /*downTime*/, 3 /*eventTime*/, DOWN, {{10, 20, 30}}); + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + args.source |= AINPUT_SOURCE_STYLUS; + mBlocker->notifyMotion(&args); + args = generateMotionArgs(3 /*downTime*/, 4 /*eventTime*/, MOVE, {{40, 50, 60}}); + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + args.source |= AINPUT_SOURCE_STYLUS; + mBlocker->notifyMotion(&args); + args = generateMotionArgs(3 /*downTime*/, 5 /*eventTime*/, UP, {{40, 50, 60}}); + args.pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_STYLUS; + args.source |= AINPUT_SOURCE_STYLUS; + mBlocker->notifyMotion(&args); +} + using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; /** -- cgit v1.2.3-59-g8ed1b From a91d8576d9fbb5c2fecbcad8a4f3eef0d2fcc30f Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Tue, 17 May 2022 05:03:42 -0700 Subject: Add lock to protect UnwantedInteractionBlocker The call to 'dump' may come from any thread, and therefore could cause a crash. Add a lock to protect this input stage. To run the test: adb shell -t "/data/nativetest64/inputflinger_tests/inputflinger_tests --gtest_filter='*Dump*' --gtest_repeat=100000 --gtest_break_on_failure" Before this patch, the test failed after ~5K - ~13K iterations (took 10-20 seconds to crash). Bug: 232645962 Test: m inputflinger_tests && adb sync data && Change-Id: I2a199690450bc5bb4a8576aa59075e99d37a531b (cherry picked from commit 9f330c542b48dc6edba9aeaff3b3f4bf305713f3) --- include/input/PrintTools.h | 9 ++ libs/input/PrintTools.cpp | 17 +++ services/inputflinger/InputClassifier.cpp | 62 ++++++---- services/inputflinger/InputClassifier.h | 6 +- services/inputflinger/InputManager.cpp | 13 +- services/inputflinger/InputManager.h | 6 +- .../inputflinger/UnwantedInteractionBlocker.cpp | 133 +++++++++------------ services/inputflinger/UnwantedInteractionBlocker.h | 11 +- .../include/UnwantedInteractionBlockerInterface.h | 4 +- .../tests/UnwantedInteractionBlocker_test.cpp | 22 ++++ 10 files changed, 171 insertions(+), 112 deletions(-) (limited to 'include/input') diff --git a/include/input/PrintTools.h b/include/input/PrintTools.h index 7c3b29b55f..0a75278494 100644 --- a/include/input/PrintTools.h +++ b/include/input/PrintTools.h @@ -58,4 +58,13 @@ std::string dumpMap(const std::map& map, std::string (*keyToString)(const const char* toString(bool value); +/** + * Add "prefix" to the beginning of each line in the provided string + * "str". + * The string 'str' is typically multi-line. + * The most common use case for this function is to add some padding + * when dumping state. + */ +std::string addLinePrefix(std::string str, const std::string& prefix); + } // namespace android \ No newline at end of file diff --git a/libs/input/PrintTools.cpp b/libs/input/PrintTools.cpp index 5d6ae4ed91..01f6bf514b 100644 --- a/libs/input/PrintTools.cpp +++ b/libs/input/PrintTools.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "PrintTools" #include +#include namespace android { @@ -24,4 +25,20 @@ const char* toString(bool value) { return value ? "true" : "false"; } +std::string addLinePrefix(std::string str, const std::string& prefix) { + std::stringstream ss; + bool newLineStarted = true; + for (const auto& ch : str) { + if (newLineStarted) { + ss << prefix; + newLineStarted = false; + } + if (ch == '\n') { + newLineStarted = true; + } + ss << ch; + } + return ss.str(); +} + } // namespace android diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 3ea0986d41..8ce2f35d7b 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -367,7 +367,7 @@ void MotionClassifier::dump(std::string& dump) { // --- InputClassifier --- -InputClassifier::InputClassifier(InputListenerInterface& listener) : mListener(listener) {} +InputClassifier::InputClassifier(InputListenerInterface& listener) : mQueuedListener(listener) {} void InputClassifier::onBinderDied(void* cookie) { InputClassifier* classifier = static_cast(cookie); @@ -417,55 +417,67 @@ void InputClassifier::setMotionClassifierEnabled(bool enabled) { void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through - mListener.notifyConfigurationChanged(args); + mQueuedListener.notifyConfigurationChanged(args); + mQueuedListener.flush(); } void InputClassifier::notifyKey(const NotifyKeyArgs* args) { // pass through - mListener.notifyKey(args); + mQueuedListener.notifyKey(args); + mQueuedListener.flush(); } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { - std::scoped_lock lock(mLock); - // MotionClassifier is only used for touch events, for now - const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); - if (!sendToMotionClassifier) { - mListener.notifyMotion(args); - return; - } - - NotifyMotionArgs newArgs(*args); - newArgs.classification = mMotionClassifier->classify(newArgs); - mListener.notifyMotion(&newArgs); + { // acquire lock + std::scoped_lock lock(mLock); + // MotionClassifier is only used for touch events, for now + const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); + if (!sendToMotionClassifier) { + mQueuedListener.notifyMotion(args); + } else { + NotifyMotionArgs newArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + mQueuedListener.notifyMotion(&newArgs); + } + } // release lock + mQueuedListener.flush(); } void InputClassifier::notifySensor(const NotifySensorArgs* args) { // pass through - mListener.notifySensor(args); + mQueuedListener.notifySensor(args); + mQueuedListener.flush(); } void InputClassifier::notifyVibratorState(const NotifyVibratorStateArgs* args) { // pass through - mListener.notifyVibratorState(args); + mQueuedListener.notifyVibratorState(args); + mQueuedListener.flush(); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { // pass through - mListener.notifySwitch(args); + mQueuedListener.notifySwitch(args); + mQueuedListener.flush(); } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - std::scoped_lock lock(mLock); - if (mMotionClassifier) { - mMotionClassifier->reset(*args); - } + { // acquire lock + std::scoped_lock lock(mLock); + if (mMotionClassifier) { + mMotionClassifier->reset(*args); + } + } // release lock + // continue to next stage - mListener.notifyDeviceReset(args); + mQueuedListener.notifyDeviceReset(args); + mQueuedListener.flush(); } void InputClassifier::notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) { // pass through - mListener.notifyPointerCaptureChanged(args); + mQueuedListener.notifyPointerCaptureChanged(args); + mQueuedListener.flush(); } void InputClassifier::setMotionClassifierLocked( @@ -490,6 +502,10 @@ void InputClassifier::dump(std::string& dump) { dump += "\n"; } +void InputClassifier::monitor() { + std::scoped_lock lock(mLock); +} + InputClassifier::~InputClassifier() { } diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index e2a0bc26f6..56cf760256 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -96,6 +96,9 @@ public: */ virtual void dump(std::string& dump) = 0; + /* Called by the heatbeat to ensures that the classifier has not deadlocked. */ + virtual void monitor() = 0; + InputClassifierInterface() { } virtual ~InputClassifierInterface() { } }; @@ -247,6 +250,7 @@ public: void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs* args) override; void dump(std::string& dump) override; + void monitor() override; ~InputClassifier(); @@ -257,7 +261,7 @@ private: // Protect access to mMotionClassifier, since it may become null via a hidl callback std::mutex mLock; // The next stage to pass input events to - InputListenerInterface& mListener; + QueuedInputListener mQueuedListener; std::unique_ptr mMotionClassifier GUARDED_BY(mLock); std::future mInitializeMotionClassifier GUARDED_BY(mLock); diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index 7b03631e1f..9767cd9b71 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -62,8 +62,8 @@ InputManager::InputManager( const sp& dispatcherPolicy) { mDispatcher = createInputDispatcher(dispatcherPolicy); mClassifier = std::make_unique(*mDispatcher); - mUnwantedInteractionBlocker = std::make_unique(*mClassifier); - mReader = createInputReader(readerPolicy, *mUnwantedInteractionBlocker); + mBlocker = std::make_unique(*mClassifier); + mReader = createInputReader(readerPolicy, *mBlocker); } InputManager::~InputManager() { @@ -111,7 +111,7 @@ InputReaderInterface& InputManager::getReader() { } UnwantedInteractionBlockerInterface& InputManager::getUnwantedInteractionBlocker() { - return *mUnwantedInteractionBlocker; + return *mBlocker; } InputClassifierInterface& InputManager::getClassifier() { @@ -122,6 +122,13 @@ InputDispatcherInterface& InputManager::getDispatcher() { return *mDispatcher; } +void InputManager::monitor() { + mReader->monitor(); + mBlocker->monitor(); + mClassifier->monitor(); + mDispatcher->monitor(); +} + // Used by tests only. binder::Status InputManager::createInputChannel(const std::string& name, InputChannel* outChannel) { IPCThreadState* ipc = IPCThreadState::self(); diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index 35d2b0fa19..8aad35bf1e 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -90,6 +90,9 @@ public: /* Gets the input dispatcher. */ virtual InputDispatcherInterface& getDispatcher() = 0; + + /* Check that the input stages have not deadlocked. */ + virtual void monitor() = 0; }; class InputManager : public InputManagerInterface, public BnInputFlinger { @@ -108,6 +111,7 @@ public: UnwantedInteractionBlockerInterface& getUnwantedInteractionBlocker() override; InputClassifierInterface& getClassifier() override; InputDispatcherInterface& getDispatcher() override; + void monitor() override; status_t dump(int fd, const Vector& args) override; binder::Status createInputChannel(const std::string& name, InputChannel* outChannel) override; @@ -117,7 +121,7 @@ public: private: std::unique_ptr mReader; - std::unique_ptr mUnwantedInteractionBlocker; + std::unique_ptr mBlocker; std::unique_ptr mClassifier; diff --git a/services/inputflinger/UnwantedInteractionBlocker.cpp b/services/inputflinger/UnwantedInteractionBlocker.cpp index b69e16ac85..f57ff33d50 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.cpp +++ b/services/inputflinger/UnwantedInteractionBlocker.cpp @@ -18,6 +18,7 @@ #include "UnwantedInteractionBlocker.h" #include +#include #include #include #include @@ -80,47 +81,6 @@ static int getLinuxToolType(int32_t toolType) { return MT_TOOL_FINGER; } -static std::string addPrefix(std::string str, const std::string& prefix) { - std::stringstream ss; - bool newLineStarted = true; - for (const auto& ch : str) { - if (newLineStarted) { - ss << prefix; - newLineStarted = false; - } - if (ch == '\n') { - newLineStarted = true; - } - ss << ch; - } - return ss.str(); -} - -template -static std::string dumpSet(const std::set& v) { - static_assert(std::is_integral::value, "Only integral types can be printed."); - std::string out; - for (const T& entry : v) { - out += out.empty() ? "{" : ", "; - out += android::base::StringPrintf("%i", entry); - } - return out.empty() ? "{}" : (out + "}"); -} - -template -static std::string dumpMap(const std::map& map) { - static_assert(std::is_integral::value, "Keys should have integral type to be printed."); - static_assert(std::is_integral::value, "Values should have integral type to be printed."); - std::string out; - for (const auto& [k, v] : map) { - if (!out.empty()) { - out += "\n"; - } - out += android::base::StringPrintf("%i : %i", static_cast(k), static_cast(v)); - } - return out; -} - static std::string dumpDeviceInfo(const AndroidPalmFilterDeviceInfo& info) { std::string out; out += StringPrintf("max_x = %.2f\n", info.max_x); @@ -168,10 +128,6 @@ static int32_t resolveActionForPointer(uint8_t pointerIndex, int32_t action) { return AMOTION_EVENT_ACTION_MOVE; } -static const char* toString(bool value) { - return value ? "true" : "false"; -} - std::string toString(const ::ui::InProgressTouchEvdev& touch) { return StringPrintf("x=%.1f, y=%.1f, tracking_id=%i, slot=%zu," " pressure=%.1f, major=%i, minor=%i, " @@ -356,69 +312,87 @@ UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& l UnwantedInteractionBlocker::UnwantedInteractionBlocker(InputListenerInterface& listener, bool enablePalmRejection) - : mListener(listener), mEnablePalmRejection(enablePalmRejection) {} + : mQueuedListener(listener), mEnablePalmRejection(enablePalmRejection) {} void UnwantedInteractionBlocker::notifyConfigurationChanged( const NotifyConfigurationChangedArgs* args) { - mListener.notifyConfigurationChanged(args); + mQueuedListener.notifyConfigurationChanged(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyKey(const NotifyKeyArgs* args) { - mListener.notifyKey(args); + mQueuedListener.notifyKey(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyMotion(const NotifyMotionArgs* args) { - const std::vector processedArgs = - mPreferStylusOverTouchBlocker.processMotion(*args); - for (const NotifyMotionArgs& loopArgs : processedArgs) { - notifyMotionInner(&loopArgs); - } + { // acquire lock + std::scoped_lock lock(mLock); + const std::vector processedArgs = + mPreferStylusOverTouchBlocker.processMotion(*args); + for (const NotifyMotionArgs& loopArgs : processedArgs) { + notifyMotionLocked(&loopArgs); + } + } // release lock + + // Call out to the next stage without holding the lock + mQueuedListener.flush(); } -void UnwantedInteractionBlocker::notifyMotionInner(const NotifyMotionArgs* args) { +void UnwantedInteractionBlocker::notifyMotionLocked(const NotifyMotionArgs* args) { auto it = mPalmRejectors.find(args->deviceId); const bool sendToPalmRejector = it != mPalmRejectors.end() && isFromTouchscreen(args->source); if (!sendToPalmRejector) { - mListener.notifyMotion(args); + mQueuedListener.notifyMotion(args); return; } - const std::vector newMotions = it->second.processMotion(*args); - for (const NotifyMotionArgs& newArgs : newMotions) { - mListener.notifyMotion(&newArgs); + std::vector processedArgs = it->second.processMotion(*args); + for (const NotifyMotionArgs& loopArgs : processedArgs) { + mQueuedListener.notifyMotion(&loopArgs); } } void UnwantedInteractionBlocker::notifySwitch(const NotifySwitchArgs* args) { - mListener.notifySwitch(args); + mQueuedListener.notifySwitch(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifySensor(const NotifySensorArgs* args) { - mListener.notifySensor(args); + mQueuedListener.notifySensor(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyVibratorState(const NotifyVibratorStateArgs* args) { - mListener.notifyVibratorState(args); + mQueuedListener.notifyVibratorState(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyDeviceReset(const NotifyDeviceResetArgs* args) { - auto it = mPalmRejectors.find(args->deviceId); - if (it != mPalmRejectors.end()) { - AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); - // Re-create the object instead of resetting it - mPalmRejectors.erase(it); - mPalmRejectors.emplace(args->deviceId, info); - } - mListener.notifyDeviceReset(args); - mPreferStylusOverTouchBlocker.notifyDeviceReset(*args); + { // acquire lock + std::scoped_lock lock(mLock); + auto it = mPalmRejectors.find(args->deviceId); + if (it != mPalmRejectors.end()) { + AndroidPalmFilterDeviceInfo info = it->second.getPalmFilterDeviceInfo(); + // Re-create the object instead of resetting it + mPalmRejectors.erase(it); + mPalmRejectors.emplace(args->deviceId, info); + } + mQueuedListener.notifyDeviceReset(args); + mPreferStylusOverTouchBlocker.notifyDeviceReset(*args); + } // release lock + // Send events to the next stage without holding the lock + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyPointerCaptureChanged( const NotifyPointerCaptureChangedArgs* args) { - mListener.notifyPointerCaptureChanged(args); + mQueuedListener.notifyPointerCaptureChanged(args); + mQueuedListener.flush(); } void UnwantedInteractionBlocker::notifyInputDevicesChanged( const std::vector& inputDevices) { + std::scoped_lock lock(mLock); if (!mEnablePalmRejection) { // Palm rejection is disabled. Don't create any palm rejector objects. return; @@ -450,20 +424,23 @@ void UnwantedInteractionBlocker::notifyInputDevicesChanged( } void UnwantedInteractionBlocker::dump(std::string& dump) { + std::scoped_lock lock(mLock); dump += "UnwantedInteractionBlocker:\n"; dump += " mPreferStylusOverTouchBlocker:\n"; - dump += addPrefix(mPreferStylusOverTouchBlocker.dump(), " "); + dump += addLinePrefix(mPreferStylusOverTouchBlocker.dump(), " "); dump += StringPrintf(" mEnablePalmRejection: %s\n", toString(mEnablePalmRejection)); dump += StringPrintf(" isPalmRejectionEnabled (flag value): %s\n", toString(isPalmRejectionEnabled())); dump += mPalmRejectors.empty() ? " mPalmRejectors: None\n" : " mPalmRejectors:\n"; for (const auto& [deviceId, palmRejector] : mPalmRejectors) { dump += StringPrintf(" deviceId = %" PRId32 ":\n", deviceId); - dump += addPrefix(palmRejector.dump(), " "); + dump += addLinePrefix(palmRejector.dump(), " "); } } -void UnwantedInteractionBlocker::monitor() {} +void UnwantedInteractionBlocker::monitor() { + std::scoped_lock lock(mLock); +} UnwantedInteractionBlocker::~UnwantedInteractionBlocker() {} @@ -529,9 +506,9 @@ std::optional SlotState::getSlotForPointerId(int32_t pointerId) const { std::string SlotState::dump() const { std::string out = "mSlotsByPointerId:\n"; - out += addPrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; + out += addLinePrefix(dumpMap(mSlotsByPointerId), " ") + "\n"; out += "mPointerIdsBySlot:\n"; - out += addPrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; + out += addLinePrefix(dumpMap(mPointerIdsBySlot), " ") + "\n"; return out; } @@ -689,9 +666,9 @@ const AndroidPalmFilterDeviceInfo& PalmRejector::getPalmFilterDeviceInfo() { std::string PalmRejector::dump() const { std::string out; out += "mDeviceInfo:\n"; - out += addPrefix(dumpDeviceInfo(mDeviceInfo), " "); + out += addLinePrefix(dumpDeviceInfo(mDeviceInfo), " "); out += "mSlotState:\n"; - out += addPrefix(mSlotState.dump(), " "); + out += addLinePrefix(mSlotState.dump(), " "); out += "mSuppressedPointerIds: "; out += dumpSet(mSuppressedPointerIds) + "\n"; return out; diff --git a/services/inputflinger/UnwantedInteractionBlocker.h b/services/inputflinger/UnwantedInteractionBlocker.h index 8a1cd7265e..a43376419f 100644 --- a/services/inputflinger/UnwantedInteractionBlocker.h +++ b/services/inputflinger/UnwantedInteractionBlocker.h @@ -19,6 +19,7 @@ #include #include +#include #include "include/UnwantedInteractionBlockerInterface.h" #include "ui/events/ozone/evdev/touch_filter/neural_stylus_palm_detection_filter_util.h" #include "ui/events/ozone/evdev/touch_filter/palm_detection_filter.h" @@ -86,18 +87,20 @@ public: ~UnwantedInteractionBlocker(); private: + std::mutex mLock; // The next stage to pass input events to - InputListenerInterface& mListener; + + QueuedInputListener mQueuedListener; const bool mEnablePalmRejection; // When stylus is down, ignore touch - PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker; + PreferStylusOverTouchBlocker mPreferStylusOverTouchBlocker GUARDED_BY(mLock); // Detect and reject unwanted palms on screen // Use a separate palm rejector for every touch device. - std::map mPalmRejectors; + std::map mPalmRejectors GUARDED_BY(mLock); // TODO(b/210159205): delete this when simultaneous stylus and touch is supported - void notifyMotionInner(const NotifyMotionArgs* args); + void notifyMotionLocked(const NotifyMotionArgs* args) REQUIRES(mLock); }; class SlotState { diff --git a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h index 2327266563..1a6f8472a5 100644 --- a/services/inputflinger/include/UnwantedInteractionBlockerInterface.h +++ b/services/inputflinger/include/UnwantedInteractionBlockerInterface.h @@ -39,11 +39,11 @@ public: /** * Dump the state of the interaction blocker. - * This method may be called on any thread (usually by the input manager). + * This method may be called on any thread (usually by the input manager on a binder thread). */ virtual void dump(std::string& dump) = 0; - /* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */ + /* Called by the heatbeat to ensures that the blocker has not deadlocked. */ virtual void monitor() = 0; UnwantedInteractionBlockerInterface() {} diff --git a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp index e378096df5..0062f426d7 100644 --- a/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp +++ b/services/inputflinger/tests/UnwantedInteractionBlocker_test.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "TestInputListener.h" @@ -547,6 +548,27 @@ TEST_F(UnwantedInteractionBlockerTest, StylusAfterTouchWorks) { mBlocker->notifyMotion(&args); } +/** + * Call dump, and on another thread, try to send some motions. The blocker should + * not crash. On 2022 hardware, this test requires ~ 13K executions (about 20 seconds) to reproduce + * the original bug. This is meant to be run with "--gtest_repeat=100000 --gtest_break_on_failure" + * options + */ +TEST_F(UnwantedInteractionBlockerTest, DumpCanBeAccessedOnAnotherThread) { + mBlocker->notifyInputDevicesChanged({generateTestDeviceInfo()}); + NotifyMotionArgs args1 = generateMotionArgs(0 /*downTime*/, 0 /*eventTime*/, DOWN, {{1, 2, 3}}); + mBlocker->notifyMotion(&args1); + std::thread dumpThread([this]() { + std::string dump; + mBlocker->dump(dump); + }); + NotifyMotionArgs args2 = generateMotionArgs(0 /*downTime*/, 1 /*eventTime*/, MOVE, {{4, 5, 6}}); + mBlocker->notifyMotion(&args2); + NotifyMotionArgs args3 = generateMotionArgs(0 /*downTime*/, 2 /*eventTime*/, UP, {{4, 5, 6}}); + mBlocker->notifyMotion(&args3); + dumpThread.join(); +} + using UnwantedInteractionBlockerTestDeathTest = UnwantedInteractionBlockerTest; /** -- cgit v1.2.3-59-g8ed1b From 4ded0b06035d9e69ef88c5ccd0a794eb3104ad5c Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Thu, 26 May 2022 00:36:48 +0000 Subject: Validate indices when requesting PointerCoords We are investigating a crash in PointerCoords. To further understand what kind of MotionEvents we are dealing with, let's add some assertions to the PointerCoords that we are producing. It's possible that pointerCount is zero, for example. This would help us further narrow down the issue. In this CL, we are also adding a way to dump MotionEvent. This was loosely following MotionEvent.java::toString() method. At some point, we should remove the Java method implementation and replace it with a single jni call to native. That work is out of scope for this CL. In the jni layer, we can't remove the error checking/exception raising, because someone might be catching an exception and moving on, and a native crash would break this pattern. We can consider doing that in the future under an experiment flag, though. Bug: 233163975 Test: printed MotionEvent in log to see the formatting Change-Id: I4f641c0cb89526a06146e4c0cf3a5fab2faa42f8 --- include/input/Input.h | 2 ++ libs/input/Input.cpp | 71 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 2 deletions(-) (limited to 'include/input') diff --git a/include/input/Input.h b/include/input/Input.h index b23a9518f7..e7d68fc349 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -837,6 +837,8 @@ protected: std::vector mSamplePointerCoords; }; +std::ostream& operator<<(std::ostream& out, const MotionEvent& event); + /* * Focus events. */ diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index fe1754c78b..13ca9ecd35 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -22,7 +22,9 @@ #include #include +#include #include +#include #include #include #include @@ -542,7 +544,14 @@ void MotionEvent::setCursorPosition(float x, float y) { } const PointerCoords* MotionEvent::getRawPointerCoords(size_t pointerIndex) const { - return &mSamplePointerCoords[getHistorySize() * getPointerCount() + pointerIndex]; + if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) { + LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this; + } + const size_t position = getHistorySize() * getPointerCount() + pointerIndex; + if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) { + LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this; + } + return &mSamplePointerCoords[position]; } float MotionEvent::getRawAxisValue(int32_t axis, size_t pointerIndex) const { @@ -555,7 +564,18 @@ float MotionEvent::getAxisValue(int32_t axis, size_t pointerIndex) const { const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( size_t pointerIndex, size_t historicalIndex) const { - return &mSamplePointerCoords[historicalIndex * getPointerCount() + pointerIndex]; + if (CC_UNLIKELY(pointerIndex < 0 || pointerIndex >= getPointerCount())) { + LOG(FATAL) << __func__ << ": Invalid pointer index " << pointerIndex << " for " << *this; + } + if (CC_UNLIKELY(historicalIndex < 0 || historicalIndex > getHistorySize())) { + LOG(FATAL) << __func__ << ": Invalid historical index " << historicalIndex << " for " + << *this; + } + const size_t position = historicalIndex * getPointerCount() + pointerIndex; + if (CC_UNLIKELY(position < 0 || position >= mSamplePointerCoords.size())) { + LOG(FATAL) << __func__ << ": Invalid array index " << position << " for " << *this; + } + return &mSamplePointerCoords[position]; } float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, @@ -903,6 +923,53 @@ PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, return out; } +std::ostream& operator<<(std::ostream& out, const MotionEvent& event) { + out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction()); + if (event.getActionButton() != 0) { + out << ", actionButton=" << std::to_string(event.getActionButton()); + } + const size_t pointerCount = event.getPointerCount(); + for (size_t i = 0; i < pointerCount; i++) { + out << ", id[" << i << "]=" << event.getPointerId(i); + float x = event.getX(i); + float y = event.getY(i); + if (x != 0 || y != 0) { + out << ", x[" << i << "]=" << x; + out << ", y[" << i << "]=" << y; + } + int toolType = event.getToolType(i); + if (toolType != AMOTION_EVENT_TOOL_TYPE_FINGER) { + out << ", toolType[" << i << "]=" << toolType; + } + } + if (event.getButtonState() != 0) { + out << ", buttonState=" << event.getButtonState(); + } + if (event.getClassification() != MotionClassification::NONE) { + out << ", classification=" << motionClassificationToString(event.getClassification()); + } + if (event.getMetaState() != 0) { + out << ", metaState=" << event.getMetaState(); + } + if (event.getEdgeFlags() != 0) { + out << ", edgeFlags=" << event.getEdgeFlags(); + } + if (pointerCount != 1) { + out << ", pointerCount=" << pointerCount; + } + if (event.getHistorySize() != 0) { + out << ", historySize=" << event.getHistorySize(); + } + out << ", eventTime=" << event.getEventTime(); + out << ", downTime=" << event.getDownTime(); + out << ", deviceId=" << event.getDeviceId(); + out << ", source=" << inputEventSourceToString(event.getSource()); + out << ", displayId=" << event.getDisplayId(); + out << ", eventId=" << event.getId(); + out << "}"; + return out; +} + // --- FocusEvent --- void FocusEvent::initialize(int32_t id, bool hasFocus) { -- cgit v1.2.3-59-g8ed1b From ac7f2e7be3759808b09a7e64d9e9510c4fc21e10 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 18 May 2022 12:30:16 -0700 Subject: Refactor input code for require_kernel_config parameter As part of the next commit to allow kl files to specify a required kernel config, some small refactorings were done. Move those here to a separate CL to make it easier to review. Bug: 228005926 Test: atest libinput_tests Merged-In: Iab06bb6ef44807308ee2b3e6b8a0c780bb748f09 Change-Id: Iab06bb6ef44807308ee2b3e6b8a0c780bb748f09 (cherry picked from commit 5ed8eaab67ad7ec3ac1110d696f5d89596a5a887) --- include/input/KeyLayoutMap.h | 4 +- libs/input/KeyLayoutMap.cpp | 115 +++++++++++++++------------------- libs/input/tests/InputDevice_test.cpp | 4 +- 3 files changed, 52 insertions(+), 71 deletions(-) (limited to 'include/input') diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index b2bd535cbf..d1925f4eee 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -21,7 +21,6 @@ #include #include #include -#include #include #include @@ -66,7 +65,6 @@ struct AxisInfo { class KeyLayoutMap { public: static base::Result> load(const std::string& filename); - static base::Result> load(Tokenizer* tokenizer); static base::Result> loadContents(const std::string& filename, const char* contents); @@ -84,6 +82,8 @@ public: virtual ~KeyLayoutMap(); private: + static base::Result> load(Tokenizer* tokenizer); + struct Key { int32_t keyCode; uint32_t flags; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 7c25cda9ac..17c3bb36e3 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -21,8 +21,8 @@ #include #include #include +#include #include -#include #include #include @@ -30,15 +30,22 @@ #include #include -// Enables debug output for the parser. -#define DEBUG_PARSER 0 +/** + * Log debug output for the parser. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapParser DEBUG" (requires restart) + */ +const bool DEBUG_PARSER = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Parser", ANDROID_LOG_INFO); // Enables debug output for parser performance. #define DEBUG_PARSER_PERFORMANCE 0 -// Enables debug output for mapping. -#define DEBUG_MAPPING 0 - +/** + * Log debug output for mapping. + * Enable this via "adb shell setprop log.tag.KeyLayoutMapMapping DEBUG" (requires restart) + */ +const bool DEBUG_MAPPING = + __android_log_is_loggable(ANDROID_LOG_DEBUG, LOG_TAG "Mapping", ANDROID_LOG_INFO); namespace android { namespace { @@ -134,9 +141,8 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const { const Key* key = getKey(scanCode, usageCode); if (!key) { -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, usageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, + usageCode); *outKeyCode = AKEYCODE_UNKNOWN; *outFlags = 0; return NAME_NOT_FOUND; @@ -145,10 +151,9 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, *outKeyCode = key->keyCode; *outFlags = key->flags; -#if DEBUG_MAPPING - ALOGD("mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", + scanCode, usageCode, *outKeyCode, *outFlags); return NO_ERROR; } @@ -156,17 +161,12 @@ status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, base::Result> KeyLayoutMap::mapSensor(int32_t absCode) { auto it = mSensorsByAbsCode.find(absCode); if (it == mSensorsByAbsCode.end()) { -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, ~ Failed.", absCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, ~ Failed.", absCode); return Errorf("Can't find abs code {}.", absCode); } const Sensor& sensor = it->second; - -#if DEBUG_MAPPING - ALOGD("mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, - ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapSensor: absCode=%d, sensorType=%s, sensorDataIndex=0x%x.", absCode, + ftl::enum_string(sensor.sensorType).c_str(), sensor.sensorDataIndex); return std::make_pair(sensor.sensorType, sensor.sensorDataIndex); } @@ -200,21 +200,18 @@ status_t KeyLayoutMap::findScanCodesForKey( status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { ssize_t index = mAxes.indexOfKey(scanCode); if (index < 0) { -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Failed.", scanCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); return NAME_NOT_FOUND; } *outAxisInfo = mAxes.valueAt(index); -#if DEBUG_MAPPING - ALOGD("mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); -#endif + ALOGD_IF(DEBUG_MAPPING, + "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, + outAxisInfo->splitValue, outAxisInfo->flatOverride); + return NO_ERROR; } @@ -223,15 +220,12 @@ status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) for (size_t i = 0; i < N; i++) { if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { *outScanCode = mLedsByScanCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, *outScanCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, + *outScanCode); return NO_ERROR; } } -#if DEBUG_MAPPING - ALOGD("findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); return NAME_NOT_FOUND; } @@ -240,15 +234,12 @@ status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCod for (size_t i = 0; i < N; i++) { if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { *outUsageCode = mLedsByUsageCode.keyAt(i); -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d, usage=%x.", ledCode, *outUsageCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode); return NO_ERROR; } } -#if DEBUG_MAPPING - ALOGD("findUsageForLed: ledCode=%d ~ Not found.", ledCode); -#endif + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return NAME_NOT_FOUND; } @@ -264,10 +255,8 @@ KeyLayoutMap::Parser::~Parser() { status_t KeyLayoutMap::Parser::parse() { while (!mTokenizer->isEof()) { -#if DEBUG_PARSER - ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), - mTokenizer->peekRemainderOfLine().string()); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); mTokenizer->skipDelimiters(WHITESPACE); @@ -361,10 +350,9 @@ status_t KeyLayoutMap::Parser::parseKey() { flags |= flag; } -#if DEBUG_PARSER - ALOGD("Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", code, keyCode, flags); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", + mapUsage ? "usage" : "scan code", code, keyCode, flags); + Key key; key.keyCode = keyCode; key.flags = flags; @@ -462,13 +450,12 @@ status_t KeyLayoutMap::Parser::parseAxis() { } } -#if DEBUG_PARSER - ALOGD("Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, - axisInfo.mode, axisInfo.axis, axisInfo.highAxis, - axisInfo.splitValue, axisInfo.flatOverride); -#endif + ALOGD_IF(DEBUG_PARSER, + "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " + "splitValue=%d, flatOverride=%d.", + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + mMap->mAxes.add(scanCode, axisInfo); return NO_ERROR; } @@ -505,10 +492,8 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } -#if DEBUG_PARSER - ALOGD("Parsed led %s: code=%d, ledCode=%d.", - mapUsage ? "usage" : "scan code", code, ledCode); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed led %s: code=%d, ledCode=%d.", mapUsage ? "usage" : "scan code", + code, ledCode); Led led; led.ledCode = ledCode; @@ -584,10 +569,8 @@ status_t KeyLayoutMap::Parser::parseSensor() { } int32_t sensorDataIndex = indexOpt.value(); -#if DEBUG_PARSER - ALOGD("Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, - ftl::enum_string(sensorType).c_str(), sensorDataIndex); -#endif + ALOGD_IF(DEBUG_PARSER, "Parsed sensor: abs code=%d, sensorType=%s, sensorDataIndex=%d.", code, + ftl::enum_string(sensorType).c_str(), sensorDataIndex); Sensor sensor; sensor.sensorType = sensorType; diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index 61e88df11d..6b695c4581 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -64,13 +64,11 @@ protected: mKeyMap.keyCharacterMapFile = path; } - virtual void SetUp() override { + void SetUp() override { loadKeyLayout("Generic"); loadKeyCharacterMap("Generic"); } - virtual void TearDown() override {} - KeyMap mKeyMap; }; -- cgit v1.2.3-59-g8ed1b From 53f0a5e01c0b24d0ed18dd926b7959e21a883540 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 18 May 2022 09:45:54 -0700 Subject: Avoid using KeyedVector in KeyLayoutMap There's a supported alternative, std::unordered_map, that we should be using instead. Bug: 228005926 Test: atest libinput_tests inputflinger_tests Merged-In: Ia1f41d17d7ee912534edffe1a1e7655866fa54c6 Change-Id: Ia1f41d17d7ee912534edffe1a1e7655866fa54c6 (cherry picked from commit 577962e532f235c67ea037f0fd7754834b1ff662) --- include/input/KeyLayoutMap.h | 19 +++---- libs/input/KeyLayoutMap.cpp | 95 ++++++++++++++----------------- services/inputflinger/reader/EventHub.cpp | 55 ++++++++---------- 3 files changed, 77 insertions(+), 92 deletions(-) (limited to 'include/input') diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index d1925f4eee..006c0684e3 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -70,11 +69,11 @@ public: status_t mapKey(int32_t scanCode, int32_t usageCode, int32_t* outKeyCode, uint32_t* outFlags) const; - status_t findScanCodesForKey(int32_t keyCode, std::vector* outScanCodes) const; - status_t findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const; - status_t findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const; + std::vector findScanCodesForKey(int32_t keyCode) const; + std::optional findScanCodeForLed(int32_t ledCode) const; + std::optional findUsageCodeForLed(int32_t ledCode) const; - status_t mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const; + std::optional mapAxis(int32_t scanCode) const; const std::string getLoadFileName() const; // Return pair of sensor type and sensor data index, for the input device abs code base::Result> mapSensor(int32_t absCode); @@ -98,11 +97,11 @@ private: int32_t sensorDataIndex; }; - KeyedVector mKeysByScanCode; - KeyedVector mKeysByUsageCode; - KeyedVector mAxes; - KeyedVector mLedsByScanCode; - KeyedVector mLedsByUsageCode; + std::unordered_map mKeysByScanCode; + std::unordered_map mKeysByUsageCode; + std::unordered_map mAxes; + std::unordered_map mLedsByScanCode; + std::unordered_map mLedsByUsageCode; std::unordered_map mSensorsByAbsCode; std::string mLoadFileName; diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 17c3bb36e3..00fc051cea 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -172,78 +172,68 @@ base::Result> KeyLayoutMap::mapSensor( const KeyLayoutMap::Key* KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { if (usageCode) { - ssize_t index = mKeysByUsageCode.indexOfKey(usageCode); - if (index >= 0) { - return &mKeysByUsageCode.valueAt(index); + auto it = mKeysByUsageCode.find(usageCode); + if (it != mKeysByUsageCode.end()) { + return &it->second; } } if (scanCode) { - ssize_t index = mKeysByScanCode.indexOfKey(scanCode); - if (index >= 0) { - return &mKeysByScanCode.valueAt(index); + auto it = mKeysByScanCode.find(scanCode); + if (it != mKeysByScanCode.end()) { + return &it->second; } } return nullptr; } -status_t KeyLayoutMap::findScanCodesForKey( - int32_t keyCode, std::vector* outScanCodes) const { - const size_t N = mKeysByScanCode.size(); - for (size_t i=0; ipush_back(mKeysByScanCode.keyAt(i)); +std::vector KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { + std::vector scanCodes; + for (const auto& [scanCode, key] : mKeysByScanCode) { + if (keyCode == key.keyCode) { + scanCodes.push_back(scanCode); } } - return NO_ERROR; + return scanCodes; } -status_t KeyLayoutMap::mapAxis(int32_t scanCode, AxisInfo* outAxisInfo) const { - ssize_t index = mAxes.indexOfKey(scanCode); - if (index < 0) { +std::optional KeyLayoutMap::mapAxis(int32_t scanCode) const { + auto it = mAxes.find(scanCode); + if (it == mAxes.end()) { ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); - return NAME_NOT_FOUND; + return std::nullopt; } - *outAxisInfo = mAxes.valueAt(index); - + const AxisInfo& axisInfo = it->second; ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " "splitValue=%d, flatOverride=%d.", - scanCode, outAxisInfo->mode, outAxisInfo->axis, outAxisInfo->highAxis, - outAxisInfo->splitValue, outAxisInfo->flatOverride); - - return NO_ERROR; + scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, + axisInfo.flatOverride); + return axisInfo; } -status_t KeyLayoutMap::findScanCodeForLed(int32_t ledCode, int32_t* outScanCode) const { - const size_t N = mLedsByScanCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByScanCode.valueAt(i).ledCode == ledCode) { - *outScanCode = mLedsByScanCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d, scanCode=%d.", ledCode, - *outScanCode); - return NO_ERROR; +std::optional KeyLayoutMap::findScanCodeForLed(int32_t ledCode) const { + for (const auto& [scanCode, led] : mLedsByScanCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, scanCode=%d.", __func__, ledCode, scanCode); + return scanCode; } } - ALOGD_IF(DEBUG_MAPPING, "findScanCodeForLed: ledCode=%d ~ Not found.", ledCode); - return NAME_NOT_FOUND; + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); + return std::nullopt; } -status_t KeyLayoutMap::findUsageCodeForLed(int32_t ledCode, int32_t* outUsageCode) const { - const size_t N = mLedsByUsageCode.size(); - for (size_t i = 0; i < N; i++) { - if (mLedsByUsageCode.valueAt(i).ledCode == ledCode) { - *outUsageCode = mLedsByUsageCode.keyAt(i); - ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, *outUsageCode); - return NO_ERROR; +std::optional KeyLayoutMap::findUsageCodeForLed(int32_t ledCode) const { + for (const auto& [usageCode, led] : mLedsByUsageCode) { + if (led.ledCode == ledCode) { + ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d, usage=%x.", __func__, ledCode, usageCode); + return usageCode; } } ALOGD_IF(DEBUG_MAPPING, "%s: ledCode=%d ~ Not found.", __func__, ledCode); - - return NAME_NOT_FOUND; + return std::nullopt; } - // --- KeyLayoutMap::Parser --- KeyLayoutMap::Parser::Parser(KeyLayoutMap* map, Tokenizer* tokenizer) : @@ -314,8 +304,9 @@ status_t KeyLayoutMap::Parser::parseKey() { mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; } - KeyedVector& map = mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map& map = + mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -356,7 +347,7 @@ status_t KeyLayoutMap::Parser::parseKey() { Key key; key.keyCode = keyCode; key.flags = flags; - map.add(code, key); + map.insert({code, key}); return NO_ERROR; } @@ -369,7 +360,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { scanCodeToken.string()); return BAD_VALUE; } - if (mMap->mAxes.indexOfKey(scanCode) >= 0) { + if (mMap->mAxes.find(scanCode) != mMap->mAxes.end()) { ALOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().string(), scanCodeToken.string()); return BAD_VALUE; @@ -455,8 +446,7 @@ status_t KeyLayoutMap::Parser::parseAxis() { "splitValue=%d, flatOverride=%d.", scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, axisInfo.flatOverride); - - mMap->mAxes.add(scanCode, axisInfo); + mMap->mAxes.insert({scanCode, axisInfo}); return NO_ERROR; } @@ -476,8 +466,9 @@ status_t KeyLayoutMap::Parser::parseLed() { return BAD_VALUE; } - KeyedVector& map = mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; - if (map.indexOfKey(code) >= 0) { + std::unordered_map& map = + mapUsage ? mMap->mLedsByUsageCode : mMap->mLedsByScanCode; + if (map.find(code) != map.end()) { ALOGE("%s: Duplicate entry for led %s '%s'.", mTokenizer->getLocation().string(), mapUsage ? "usage" : "scan code", codeToken.string()); return BAD_VALUE; @@ -497,7 +488,7 @@ status_t KeyLayoutMap::Parser::parseLed() { Led led; led.ledCode = ledCode; - map.add(code, led); + map.insert({code, led}); return NO_ERROR; } diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp index d6a6bd214e..669d2e1833 100644 --- a/services/inputflinger/reader/EventHub.cpp +++ b/services/inputflinger/reader/EventHub.cpp @@ -452,8 +452,7 @@ bool EventHub::Device::hasKeycodeLocked(int keycode) const { return false; } - std::vector scanCodes; - keyMap.keyLayoutMap->findScanCodesForKey(keycode, &scanCodes); + std::vector scanCodes = keyMap.keyLayoutMap->findScanCodesForKey(keycode); const size_t N = scanCodes.size(); for (size_t i = 0; i < N && i <= KEY_MAX; i++) { int32_t sc = scanCodes[i]; @@ -548,10 +547,10 @@ status_t EventHub::Device::mapLed(int32_t led, int32_t* outScanCode) const { return NAME_NOT_FOUND; } - int32_t scanCode; - if (keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { - if (scanCode >= 0 && scanCode <= LED_MAX && ledBitmask.test(scanCode)) { - *outScanCode = scanCode; + std::optional scanCode = keyMap.keyLayoutMap->findScanCodeForLed(led); + if (scanCode.has_value()) { + if (*scanCode >= 0 && *scanCode <= LED_MAX && ledBitmask.test(*scanCode)) { + *outScanCode = *scanCode; return NO_ERROR; } } @@ -865,8 +864,7 @@ int32_t EventHub::getKeyCodeState(int32_t deviceId, int32_t keyCode) const { Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->hasValidFd() && device->keyMap.haveKeyLayout()) { - std::vector scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode, &scanCodes); + std::vector scanCodes = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCode); if (scanCodes.size() != 0) { if (device->readDeviceBitMask(EVIOCGKEY(0), device->keyState) >= 0) { for (size_t i = 0; i < scanCodes.size(); i++) { @@ -890,8 +888,8 @@ int32_t EventHub::getKeyCodeForKeyLocation(int32_t deviceId, int32_t locationKey device->keyMap.keyLayoutMap == nullptr) { return AKEYCODE_UNKNOWN; } - std::vector scanCodes; - device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode, &scanCodes); + std::vector scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(locationKeyCode); if (scanCodes.empty()) { ALOGW("Failed to get key code for key location: no scan code maps to key code %d for input" "device %d", @@ -960,20 +958,16 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, const in Device* device = getDeviceLocked(deviceId); if (device != nullptr && device->keyMap.haveKeyLayout()) { - std::vector scanCodes; for (size_t codeIndex = 0; codeIndex < numCodes; codeIndex++) { - scanCodes.clear(); - - status_t err = device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex], - &scanCodes); - if (!err) { - // check the possible scan codes identified by the layout map against the - // map of codes actually emitted by the driver - for (size_t sc = 0; sc < scanCodes.size(); sc++) { - if (device->keyBitmask.test(scanCodes[sc])) { - outFlags[codeIndex] = 1; - break; - } + std::vector scanCodes = + device->keyMap.keyLayoutMap->findScanCodesForKey(keyCodes[codeIndex]); + + // check the possible scan codes identified by the layout map against the + // map of codes actually emitted by the driver + for (size_t sc = 0; sc < scanCodes.size(); sc++) { + if (device->keyBitmask.test(scanCodes[sc])) { + outFlags[codeIndex] = 1; + break; } } } @@ -1027,14 +1021,15 @@ status_t EventHub::mapAxis(int32_t deviceId, int32_t scanCode, AxisInfo* outAxis std::scoped_lock _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device != nullptr && device->keyMap.haveKeyLayout()) { - status_t err = device->keyMap.keyLayoutMap->mapAxis(scanCode, outAxisInfo); - if (err == NO_ERROR) { - return NO_ERROR; - } + if (device == nullptr || !device->keyMap.haveKeyLayout()) { + return NAME_NOT_FOUND; } - - return NAME_NOT_FOUND; + std::optional info = device->keyMap.keyLayoutMap->mapAxis(scanCode); + if (!info.has_value()) { + return NAME_NOT_FOUND; + } + *outAxisInfo = *info; + return NO_ERROR; } base::Result> EventHub::mapSensor(int32_t deviceId, -- cgit v1.2.3-59-g8ed1b From d945d3e55ecd98d6554a9fdf6dfa1beb1c64a189 Mon Sep 17 00:00:00 2001 From: Siarhei Vishniakou Date: Wed, 18 May 2022 09:42:52 -0700 Subject: Do not load keylayout if required kernel module is missing Some key layouts require the presence of a specific kernel module. If the kernel module is not present, the layout should not be loaded. In this CL, we add an option to specify which kernel modules / configs are needed inside the kl file. Bug: 228005926 Test: atest libinput_tests Change-Id: I0d2ab6298bd41df6dc56120bf0385e10da6c3bfe Merged-In: I0d2ab6298bd41df6dc56120bf0385e10da6c3bfe --- include/input/InputDevice.h | 6 +- include/input/KeyLayoutMap.h | 6 +- include/input/Keyboard.h | 4 +- libs/input/Android.bp | 1 + libs/input/InputDevice.cpp | 30 ++++---- libs/input/KeyLayoutMap.cpp | 82 +++++++++++++++++----- libs/input/Keyboard.cpp | 39 ++++++---- libs/input/tests/Android.bp | 3 +- libs/input/tests/InputDevice_test.cpp | 16 +++++ .../tests/data/kl_with_required_fake_config.kl | 20 ++++++ .../tests/data/kl_with_required_real_config.kl | 21 ++++++ 11 files changed, 180 insertions(+), 48 deletions(-) create mode 100644 libs/input/tests/data/kl_with_required_fake_config.kl create mode 100644 libs/input/tests/data/kl_with_required_real_config.kl (limited to 'include/input') diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index c4f03c9119..3585392c2b 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -300,6 +300,8 @@ enum class InputDeviceConfigurationFileType : int32_t { /* * Gets the path of an input device configuration file, if one is available. * Considers both system provided and user installed configuration files. + * The optional suffix is appended to the end of the file name (before the + * extension). * * The device identifier is used to construct several default configuration file * names to try based on the device name, vendor, product, and version. @@ -307,8 +309,8 @@ enum class InputDeviceConfigurationFileType : int32_t { * Returns an empty string if not found. */ extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type); + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix = ""); /* * Gets the path of an input device configuration file, if one is available. diff --git a/include/input/KeyLayoutMap.h b/include/input/KeyLayoutMap.h index 006c0684e3..1da78aa0c1 100644 --- a/include/input/KeyLayoutMap.h +++ b/include/input/KeyLayoutMap.h @@ -21,6 +21,7 @@ #include #include #include +#include #include @@ -63,7 +64,8 @@ struct AxisInfo { */ class KeyLayoutMap { public: - static base::Result> load(const std::string& filename); + static base::Result> load(const std::string& filename, + const char* contents = nullptr); static base::Result> loadContents(const std::string& filename, const char* contents); @@ -103,6 +105,7 @@ private: std::unordered_map mLedsByScanCode; std::unordered_map mLedsByUsageCode; std::unordered_map mSensorsByAbsCode; + std::set mRequiredKernelConfigs; std::string mLoadFileName; KeyLayoutMap(); @@ -123,6 +126,7 @@ private: status_t parseAxis(); status_t parseLed(); status_t parseSensor(); + status_t parseRequiredKernelConfig(); }; }; diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 08ad8c6e5a..9a3e15f1cd 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -61,9 +61,7 @@ private: bool probeKeyMap(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, const std::string& name); status_t loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name); - std::string getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type); + const std::string& name); }; /** diff --git a/libs/input/Android.bp b/libs/input/Android.bp index de42b058e5..b2fec7917b 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -65,6 +65,7 @@ cc_library { "libbase", "liblog", "libcutils", + "libvintf", ], static_libs: [ diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 0bee1b6f2a..a9089690b0 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -53,33 +53,39 @@ static void appendInputDeviceConfigurationFileRelativePath(std::string& path, } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier& deviceIdentifier, - InputDeviceConfigurationFileType type) { + const InputDeviceIdentifier& deviceIdentifier, InputDeviceConfigurationFileType type, + const char* suffix) { if (deviceIdentifier.vendor !=0 && deviceIdentifier.product != 0) { if (deviceIdentifier.version != 0) { // Try vendor product version. - std::string versionPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x_Version_%04x", - deviceIdentifier.vendor, deviceIdentifier.product, - deviceIdentifier.version), - type); + std::string versionPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%" + "04x_Version_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + deviceIdentifier.version, + suffix), + type); if (!versionPath.empty()) { return versionPath; } } // Try vendor product. - std::string productPath = getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x", - deviceIdentifier.vendor, deviceIdentifier.product), - type); + std::string productPath = + getInputDeviceConfigurationFilePathByName(StringPrintf("Vendor_%04x_Product_%04x%s", + deviceIdentifier.vendor, + deviceIdentifier.product, + suffix), + type); if (!productPath.empty()) { return productPath; } } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName() + suffix, + type); } std::string getInputDeviceConfigurationFilePathByName( diff --git a/libs/input/KeyLayoutMap.cpp b/libs/input/KeyLayoutMap.cpp index 00fc051cea..59cc7d1dcd 100644 --- a/libs/input/KeyLayoutMap.cpp +++ b/libs/input/KeyLayoutMap.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -76,6 +78,29 @@ static const std::unordered_map SENSOR_ sensorPair(), sensorPair()}; +bool kernelConfigsArePresent(const std::set& configs) { + std::shared_ptr runtimeInfo = + android::vintf::VintfObject::GetInstance()->getRuntimeInfo( + vintf::RuntimeInfo::FetchFlag::CONFIG_GZ); + LOG_ALWAYS_FATAL_IF(runtimeInfo == nullptr, "Kernel configs could not be fetched"); + + const std::map& kernelConfigs = runtimeInfo->kernelConfigs(); + for (const std::string& requiredConfig : configs) { + const auto configIt = kernelConfigs.find(requiredConfig); + if (configIt == kernelConfigs.end()) { + ALOGI("Required kernel config %s is not found", requiredConfig.c_str()); + return false; + } + const std::string& option = configIt->second; + if (option != "y" && option != "m") { + ALOGI("Required kernel config %s has option %s", requiredConfig.c_str(), + option.c_str()); + return false; + } + } + return true; +} + } // namespace KeyLayoutMap::KeyLayoutMap() = default; @@ -83,32 +108,34 @@ KeyLayoutMap::~KeyLayoutMap() = default; base::Result> KeyLayoutMap::loadContents(const std::string& filename, const char* contents) { - Tokenizer* tokenizer; - status_t status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - if (status) { - ALOGE("Error %d opening key layout map.", status); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr t(tokenizer); - auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; - } - return ret; + return load(filename, contents); } -base::Result> KeyLayoutMap::load(const std::string& filename) { +base::Result> KeyLayoutMap::load(const std::string& filename, + const char* contents) { Tokenizer* tokenizer; - status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + status_t status; + if (contents == nullptr) { + status = Tokenizer::open(String8(filename.c_str()), &tokenizer); + } else { + status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); + } if (status) { ALOGE("Error %d opening key layout map file %s.", status, filename.c_str()); return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); } std::unique_ptr t(tokenizer); auto ret = load(t.get()); - if (ret.ok()) { - (*ret)->mLoadFileName = filename; + if (!ret.ok()) { + return ret; + } + const std::shared_ptr& map = *ret; + LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); + if (!kernelConfigsArePresent(map->mRequiredKernelConfigs)) { + ALOGI("Not loading %s because the required kernel configs are not set", filename.c_str()); + return Errorf("Missing kernel config"); } + map->mLoadFileName = filename; return ret; } @@ -268,6 +295,10 @@ status_t KeyLayoutMap::Parser::parse() { mTokenizer->skipDelimiters(WHITESPACE); status_t status = parseSensor(); if (status) return status; + } else if (keywordToken == "requires_kernel_config") { + mTokenizer->skipDelimiters(WHITESPACE); + status_t status = parseRequiredKernelConfig(); + if (status) return status; } else { ALOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().string(), keywordToken.string()); @@ -570,4 +601,23 @@ status_t KeyLayoutMap::Parser::parseSensor() { return NO_ERROR; } +// Parse the name of a required kernel config. +// The layout won't be used if the specified kernel config is not present +// Examples: +// requires_kernel_config CONFIG_HID_PLAYSTATION +status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { + String8 codeToken = mTokenizer->nextToken(WHITESPACE); + std::string configName = codeToken.string(); + + const auto result = mMap->mRequiredKernelConfigs.emplace(configName); + if (!result.second) { + ALOGE("%s: Duplicate entry for required kernel config %s.", + mTokenizer->getLocation().string(), configName.c_str()); + return BAD_VALUE; + } + + ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); + return NO_ERROR; +} + } // namespace android diff --git a/libs/input/Keyboard.cpp b/libs/input/Keyboard.cpp index f0895b32ef..c3f5151fd1 100644 --- a/libs/input/Keyboard.cpp +++ b/libs/input/Keyboard.cpp @@ -20,16 +20,23 @@ #include #include -#include +#include #include -#include #include -#include +#include +#include +#include #include -#include namespace android { +static std::string getPath(const InputDeviceIdentifier& deviceIdentifier, const std::string& name, + InputDeviceConfigurationFileType type) { + return name.empty() + ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) + : getInputDeviceConfigurationFilePathByName(name, type); +} + // --- KeyMap --- KeyMap::KeyMap() { @@ -111,11 +118,25 @@ status_t KeyMap::loadKeyLayout(const InputDeviceIdentifier& deviceIdentifier, } base::Result> ret = KeyLayoutMap::load(path); + if (ret.ok()) { + keyLayoutMap = *ret; + keyLayoutFile = path; + return OK; + } + + // Try to load fallback layout if the regular layout could not be loaded due to missing + // kernel modules + std::string fallbackPath( + getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, + InputDeviceConfigurationFileType:: + KEY_LAYOUT, + "_fallback")); + ret = KeyLayoutMap::load(fallbackPath); if (!ret.ok()) { return ret.error().code(); } keyLayoutMap = *ret; - keyLayoutFile = path; + keyLayoutFile = fallbackPath; return OK; } @@ -137,14 +158,6 @@ status_t KeyMap::loadKeyCharacterMap(const InputDeviceIdentifier& deviceIdentifi return OK; } -std::string KeyMap::getPath(const InputDeviceIdentifier& deviceIdentifier, - const std::string& name, InputDeviceConfigurationFileType type) { - return name.empty() - ? getInputDeviceConfigurationFilePathByDeviceIdentifier(deviceIdentifier, type) - : getInputDeviceConfigurationFilePathByName(name, type); -} - - // --- Global functions --- bool isKeyboardSpecialFunction(const PropertyMap* config) { diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 6ffe8518b6..d947cd99e8 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -36,8 +36,9 @@ cc_test { "liblog", "libui", "libutils", + "libvintf", ], - data: ["data/*.kcm"], + data: ["data/*"], test_suites: ["device-tests"], } diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp index 6b695c4581..e872fa442b 100644 --- a/libs/input/tests/InputDevice_test.cpp +++ b/libs/input/tests/InputDevice_test.cpp @@ -130,4 +130,20 @@ TEST_F(InputDeviceKeyMapTest, keyCharacteMapApplyMultipleOverlaysTest) { ASSERT_EQ(*mKeyMap.keyCharacterMap, *frenchOverlaidKeyCharacterMap); } +TEST(InputDeviceKeyLayoutTest, DoesNotLoadWhenRequiredKernelConfigIsMissing) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_fake_config.kl"; + base::Result> ret = KeyLayoutMap::load(klPath); + ASSERT_FALSE(ret.ok()) << "Should not be able to load KeyLayout at " << klPath; + // We assert error message here because it's used by 'validatekeymaps' tool + ASSERT_EQ("Missing kernel config", ret.error().message()); +} + +TEST(InputDeviceKeyLayoutTest, LoadsWhenRequiredKernelConfigIsPresent) { + std::string klPath = base::GetExecutableDirectory() + "/data/kl_with_required_real_config.kl"; + base::Result> ret = KeyLayoutMap::load(klPath); + ASSERT_TRUE(ret.ok()) << "Cannot load KeyLayout at " << klPath; + const std::shared_ptr& map = *ret; + ASSERT_NE(nullptr, map) << "Map should be valid because CONFIG_UHID should always be present"; +} + } // namespace android diff --git a/libs/input/tests/data/kl_with_required_fake_config.kl b/libs/input/tests/data/kl_with_required_fake_config.kl new file mode 100644 index 0000000000..2d0a507fbd --- /dev/null +++ b/libs/input/tests/data/kl_with_required_fake_config.kl @@ -0,0 +1,20 @@ +# Copyright (C) 2022 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. + +# This KL should not be loaded unless the below config is present in the kernel +# This config will never exist, and therefore this KL should never be loaded +requires_kernel_config CONFIG_HID_FAKEMODULE + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X \ No newline at end of file diff --git a/libs/input/tests/data/kl_with_required_real_config.kl b/libs/input/tests/data/kl_with_required_real_config.kl new file mode 100644 index 0000000000..303b23e48a --- /dev/null +++ b/libs/input/tests/data/kl_with_required_real_config.kl @@ -0,0 +1,21 @@ +# Copyright (C) 2022 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. + +# This KL should not be loaded unless the below config is present in the kernel +# The CONFIG_UHID option has been required for a while, and therefore it's safe +# to assume that this will always be loaded +requires_kernel_config CONFIG_UHID + +# An arbitrary mapping taken from another file +key 0x130 BUTTON_X \ No newline at end of file -- cgit v1.2.3-59-g8ed1b