diff options
24 files changed, 468 insertions, 172 deletions
diff --git a/include/ftl/expected.h b/include/ftl/expected.h index 57448dc1e0..7e765c5681 100644 --- a/include/ftl/expected.h +++ b/include/ftl/expected.h @@ -69,6 +69,36 @@ exp_.value(); \ }) +// Given an expression `expr` that evaluates to an ftl::Expected<T, E> result (R for short), +// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E. +// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not +// need to propagate R because T is not relevant to the caller. +// +// Example usage: +// +// using StringExp = ftl::Expected<std::string, std::errc>; +// +// std::errc repeat(StringExp exp, std::string& out) { +// const std::string str = FTL_EXPECT(exp); +// out = str + str; +// return std::errc::operation_in_progress; +// } +// +// std::string str; +// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str)); +// assert("haha"s == str); +// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str)); +// assert("haha"s == str); +// +#define FTL_EXPECT(expr) \ + ({ \ + auto exp_ = (expr); \ + if (!exp_.has_value()) { \ + return std::move(exp_.error()); \ + } \ + exp_.value(); \ + }) + namespace android::ftl { // Superset of base::expected<T, E> with monadic operations. diff --git a/include/ftl/function.h b/include/ftl/function.h index 3538ca4eae..bda5b759bb 100644 --- a/include/ftl/function.h +++ b/include/ftl/function.h @@ -123,7 +123,7 @@ namespace android::ftl { // // Create a typedef to give a more meaningful name and bound the size. // using MyFunction = ftl::Function<int(std::string_view), 2>; // int* ptr = nullptr; -// auto f1 = MyFunction::make_function( +// auto f1 = MyFunction::make( // [cls = &cls, ptr](std::string_view sv) { // return cls->on_string(ptr, sv); // }); diff --git a/include/input/Input.h b/include/input/Input.h index dee3e7dcd0..ec08cdd163 100644 --- a/include/input/Input.h +++ b/include/input/Input.h @@ -99,6 +99,18 @@ enum { /* Motion event is inconsistent with previously sent motion events. */ AMOTION_EVENT_FLAG_TAINTED = android::os::IInputConstants::INPUT_EVENT_FLAG_TAINTED, + + /** Private flag, not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = + android::os::IInputConstants::MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION, + + /** Private flag, not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = android::os::IInputConstants:: + MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + + /** Mask for all private flags that are not used in Java. */ + AMOTION_EVENT_PRIVATE_FLAG_MASK = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, }; /** @@ -209,8 +221,12 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) * Transform an angle on the x-y plane. An angle of 0 radians corresponds to "north" or * pointing upwards in the negative Y direction, a positive angle points towards the right, and a * negative angle points towards the left. + * + * If the angle represents a direction that needs to be preserved, set isDirectional to true to get + * an output range of [-pi, pi]. If the angle's direction does not need to be preserved, set + * isDirectional to false to get an output range of [-pi/2, pi/2]. */ -float transformAngle(const ui::Transform& transform, float angleRadians); +float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional); /** * The type of the InputEvent. @@ -472,8 +488,6 @@ struct PointerCoords { // axes, however the window scaling will not. void scale(float globalScale, float windowXScale, float windowYScale); - void transform(const ui::Transform& transform); - inline float getX() const { return getAxisValue(AMOTION_EVENT_AXIS_X); } @@ -940,10 +954,12 @@ public: // 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&); - static PointerCoords calculateTransformedCoords(uint32_t source, const ui::Transform&, - const PointerCoords&); + static float calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags, + const ui::Transform&, const PointerCoords&); + static void calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source, + int32_t flags, const ui::Transform&); + static PointerCoords calculateTransformedCoords(uint32_t source, int32_t flags, + const ui::Transform&, const PointerCoords&); // The rounding precision for transformed motion events. static constexpr float ROUNDING_PRECISION = 0.001f; diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 52edae4a38..41b30a0a0f 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -30,7 +30,11 @@ enum AServiceManager_AddServiceFlag : uint32_t { * Services with methods that perform file IO, web socket creation or ways to egress data must * not be added with this flag for privacy concerns. */ - ADD_SERVICE_ALLOW_ISOLATED = 1, + ADD_SERVICE_ALLOW_ISOLATED = 1 << 0, + ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL = 1 << 1, + ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH = 1 << 2, + ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL = 1 << 3, + ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT = 1 << 4, }; /** diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 5529455cc6..4436dbeed7 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -49,7 +49,25 @@ binder_exception_t AServiceManager_addServiceWithFlags(AIBinder* binder, const c sp<IServiceManager> sm = defaultServiceManager(); bool allowIsolated = flags & AServiceManager_AddServiceFlag::ADD_SERVICE_ALLOW_ISOLATED; - status_t exception = sm->addService(String16(instance), binder->getBinder(), allowIsolated); + int dumpFlags = 0; + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_CRITICAL) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_HIGH) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_HIGH; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_NORMAL) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_NORMAL; + } + if (flags & AServiceManager_AddServiceFlag::ADD_SERVICE_DUMP_FLAG_PRIORITY_DEFAULT) { + dumpFlags |= IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; + } + if (dumpFlags == 0) { + dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT; + } + status_t exception = + sm->addService(String16(instance), binder->getBinder(), allowIsolated, dumpFlags); + return PruneException(exception); } diff --git a/libs/binder/tests/binderRpcWireProtocolTest.cpp b/libs/binder/tests/binderRpcWireProtocolTest.cpp index e59dc82b2b..91145f0c13 100644 --- a/libs/binder/tests/binderRpcWireProtocolTest.cpp +++ b/libs/binder/tests/binderRpcWireProtocolTest.cpp @@ -14,14 +14,15 @@ * limitations under the License. */ -#include <android-base/logging.h> -#include <android-base/properties.h> -#include <android-base/strings.h> #include <binder/Parcel.h> #include <binder/RpcSession.h> #include <binder/Status.h> #include <gtest/gtest.h> +#ifdef __ANDROID__ +#include <android-base/properties.h> +#endif + #include "../Debug.h" #include "../Utils.h" @@ -69,8 +70,8 @@ static const std::vector<std::function<void(Parcel* p)>> kFillFuns { [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"a"))); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeString16(String16(u"baba"))); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeStrongBinder(nullptr)); }, - [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(arraysize(kInt32Array), kInt32Array)); }, - [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(arraysize(kByteArray), kByteArray)); }, + [](Parcel* p) { ASSERT_EQ(OK, p->writeInt32Array(countof(kInt32Array), kInt32Array)); }, + [](Parcel* p) { ASSERT_EQ(OK, p->writeByteArray(countof(kByteArray), kByteArray)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(true)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeBool(false)); }, [](Parcel* p) { ASSERT_EQ(OK, p->writeChar('a')); }, @@ -162,8 +163,8 @@ static const std::vector<std::function<void(Parcel* p)>> kFillFuns { static void setParcelForRpc(Parcel* p, uint32_t version) { auto session = RpcSession::make(); - CHECK(session->setProtocolVersion(version)); - CHECK_EQ(OK, session->addNullDebuggingClient()); + EXPECT_TRUE(session->setProtocolVersion(version)); + EXPECT_EQ(OK, session->addNullDebuggingClient()); p->markForRpc(session); } @@ -180,13 +181,25 @@ static std::string buildRepr(uint32_t version) { return result; } +// To be replaced with std::views::split (and std::views::zip) once C++ compilers catch up. +static std::vector<std::string> split(std::string_view s, char delimiter) { + std::vector<std::string> result; + size_t pos = 0; + while (true) { + const auto found = s.find(delimiter, pos); + result.emplace_back(s.substr(pos, found - pos)); + if (found == s.npos) return result; + pos = found + 1; + } +} + static void checkRepr(const std::string& repr, uint32_t version) { const std::string actualRepr = buildRepr(version); - auto expected = base::Split(repr, "|"); + auto expected = split(repr, '|'); ASSERT_EQ(expected.size(), kFillFuns.size()); - auto actual = base::Split(actualRepr, "|"); + auto actual = split(actualRepr, '|'); ASSERT_EQ(actual.size(), kFillFuns.size()); for (size_t i = 0; i < kFillFuns.size(); i++) { @@ -257,8 +270,13 @@ TEST(RpcWire, NextIsPlusOneReminder) { TEST(RpcWire, ReleaseBranchHasFrozenRpcWireProtocol) { if (RPC_WIRE_PROTOCOL_VERSION == RPC_WIRE_PROTOCOL_VERSION_EXPERIMENTAL) { - EXPECT_FALSE(base::GetProperty("ro.build.version.codename", "") == "REL") - << "Binder RPC wire protocol must be frozen on a release branch!"; +#ifdef __ANDROID__ + bool isRelease = base::GetProperty("ro.build.version.codename", "") == "REL"; +#else + bool isRelease = true; +#endif + EXPECT_FALSE(isRelease) + << "Binder RPC wire protocol must be frozen in release configuration!"; } } diff --git a/libs/ftl/expected_test.cpp b/libs/ftl/expected_test.cpp index 9b7f017df3..d5b1d7ea8f 100644 --- a/libs/ftl/expected_test.cpp +++ b/libs/ftl/expected_test.cpp @@ -79,16 +79,28 @@ TEST(Expected, ValueOpt) { namespace { -IntExp increment(IntExp exp) { +IntExp increment_try(IntExp exp) { const int i = FTL_TRY(exp); return IntExp(i + 1); } -StringExp repeat(StringExp exp) { +std::errc increment_expect(IntExp exp, int& out) { + const int i = FTL_EXPECT(exp); + out = i + 1; + return std::errc::operation_in_progress; +} + +StringExp repeat_try(StringExp exp) { const std::string str = FTL_TRY(exp); return StringExp(str + str); } +std::errc repeat_expect(StringExp exp, std::string& out) { + const std::string str = FTL_EXPECT(exp); + out = str + str; + return std::errc::operation_in_progress; +} + void uppercase(char& c, ftl::Optional<char> opt) { c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit()))); } @@ -97,13 +109,13 @@ void uppercase(char& c, ftl::Optional<char> opt) { // Keep in sync with example usage in header file. TEST(Expected, Try) { - EXPECT_EQ(IntExp(100), increment(IntExp(99))); - EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) { + EXPECT_EQ(IntExp(100), increment_try(IntExp(99))); + EXPECT_TRUE(increment_try(ftl::Unexpected(std::errc::value_too_large)).has_error([](std::errc e) { return e == std::errc::value_too_large; })); - EXPECT_EQ(StringExp("haha"s), repeat(StringExp("ha"s))); - EXPECT_TRUE(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) { + EXPECT_EQ(StringExp("haha"s), repeat_try(StringExp("ha"s))); + EXPECT_TRUE(repeat_try(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) { return e == std::errc::bad_message; })); @@ -115,4 +127,19 @@ TEST(Expected, Try) { EXPECT_EQ(c, 'A'); } +TEST(Expected, Expect) { + int i = 0; + EXPECT_EQ(std::errc::operation_in_progress, increment_expect(IntExp(99), i)); + EXPECT_EQ(100, i); + EXPECT_EQ(std::errc::value_too_large, + increment_expect(ftl::Unexpected(std::errc::value_too_large), i)); + EXPECT_EQ(100, i); + + std::string str; + EXPECT_EQ(std::errc::operation_in_progress, repeat_expect(StringExp("ha"s), str)); + EXPECT_EQ("haha"s, str); + EXPECT_EQ(std::errc::bad_message, repeat_expect(ftl::Unexpected(std::errc::bad_message), str)); + EXPECT_EQ("haha"s, str); +} + } // namespace android::test diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp index ee121d53fe..b09814797f 100644 --- a/libs/input/Input.cpp +++ b/libs/input/Input.cpp @@ -96,6 +96,19 @@ int32_t resolveActionForSplitMotionEvent( return AMOTION_EVENT_ACTION_DOWN; } +float transformOrientation(const ui::Transform& transform, const PointerCoords& coords, + int32_t motionEventFlags) { + if ((motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION) == 0) { + return 0; + } + + const bool isDirectionalAngle = + (motionEventFlags & AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION) != 0; + + return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION), + isDirectionalAngle); +} + } // namespace const char* motionClassificationToString(MotionClassification classification) { @@ -187,7 +200,7 @@ vec2 transformWithoutTranslation(const ui::Transform& transform, const vec2& xy) return roundTransformedCoords(transformedXy - transformedOrigin); } -float transformAngle(const ui::Transform& transform, float angleRadians) { +float transformAngle(const ui::Transform& transform, float angleRadians, bool isDirectional) { // Construct and transform a vector oriented at the specified clockwise angle from vertical. // Coordinate system: down is increasing Y, right is increasing X. float x = sinf(angleRadians); @@ -201,6 +214,11 @@ float transformAngle(const ui::Transform& transform, float angleRadians) { transformedPoint.x -= origin.x; transformedPoint.y -= origin.y; + if (!isDirectional && transformedPoint.y > 0) { + // Limit the range of atan2f to [-pi/2, pi/2] by reversing the direction of the vector. + transformedPoint *= -1; + } + // Derive the transformed vector's clockwise angle from vertical. // The return value of atan2f is in range [-pi, pi] which conforms to the orientation API. return atan2f(transformedPoint.x, -transformedPoint.y); @@ -530,26 +548,6 @@ bool PointerCoords::operator==(const PointerCoords& other) const { return true; } -void PointerCoords::transform(const ui::Transform& transform) { - const vec2 xy = transform.transform(getXYValue()); - setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); - setAxisValue(AMOTION_EVENT_AXIS_Y, xy.y); - - if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_X) || - BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_RELATIVE_Y)) { - const ui::Transform rotation(transform.getOrientation()); - const vec2 relativeXy = rotation.transform(getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X), - getAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y)); - setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); - setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); - } - - if (BitSet64::hasBit(bits, AMOTION_EVENT_AXIS_ORIENTATION)) { - const float val = getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION); - setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, transformAngle(transform, val)); - } -} - // --- MotionEvent --- void MotionEvent::initialize(int32_t id, int32_t deviceId, uint32_t source, @@ -723,13 +721,13 @@ const PointerCoords* MotionEvent::getHistoricalRawPointerCoords( float MotionEvent::getHistoricalRawAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - return calculateTransformedAxisValue(axis, mSource, mRawTransform, coords); + return calculateTransformedAxisValue(axis, mSource, mFlags, mRawTransform, coords); } float MotionEvent::getHistoricalAxisValue(int32_t axis, size_t pointerIndex, size_t historicalIndex) const { const PointerCoords& coords = *getHistoricalRawPointerCoords(pointerIndex, historicalIndex); - return calculateTransformedAxisValue(axis, mSource, mTransform, coords); + return calculateTransformedAxisValue(axis, mSource, mFlags, mTransform, coords); } ssize_t MotionEvent::findPointerIndex(int32_t pointerId) const { @@ -786,8 +784,9 @@ void MotionEvent::applyTransform(const std::array<float, 9>& matrix) { transform.set(matrix); // Apply the transformation to all samples. - std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), - [&transform](PointerCoords& c) { c.transform(transform); }); + std::for_each(mSamplePointerCoords.begin(), mSamplePointerCoords.end(), [&](PointerCoords& c) { + calculateTransformedCoordsInPlace(c, mSource, mFlags, transform); + }); if (mRawXCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION && mRawYCursorPosition != AMOTION_EVENT_INVALID_CURSOR_POSITION) { @@ -1059,7 +1058,7 @@ vec2 MotionEvent::calculateTransformedXY(uint32_t source, const ui::Transform& t } // Keep in sync with calculateTransformedCoords. -float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, +float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, int32_t flags, const ui::Transform& transform, const PointerCoords& coords) { if (shouldDisregardTransformation(source)) { @@ -1081,7 +1080,7 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, } if (axis == AMOTION_EVENT_AXIS_ORIENTATION) { - return transformAngle(transform, coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION)); + return transformOrientation(transform, coords, flags); } return coords.getAxisValue(axis); @@ -1089,29 +1088,32 @@ float MotionEvent::calculateTransformedAxisValue(int32_t axis, uint32_t source, // 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) { +void MotionEvent::calculateTransformedCoordsInPlace(PointerCoords& coords, uint32_t source, + int32_t flags, const ui::Transform& transform) { if (shouldDisregardTransformation(source)) { - return coords; + return; } - 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); + coords.setAxisValue(AMOTION_EVENT_AXIS_X, xy.x); + coords.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); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, relativeXy.x); + coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, relativeXy.y); - out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, - transformAngle(transform, - coords.getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION))); + coords.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, + transformOrientation(transform, coords, flags)); +} +PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source, int32_t flags, + const ui::Transform& transform, + const PointerCoords& coords) { + PointerCoords out = coords; + calculateTransformedCoordsInPlace(out, source, flags, transform); return out; } diff --git a/libs/input/android/os/IInputConstants.aidl b/libs/input/android/os/IInputConstants.aidl index e8746d000e..a77dfa59fe 100644 --- a/libs/input/android/os/IInputConstants.aidl +++ b/libs/input/android/os/IInputConstants.aidl @@ -116,6 +116,31 @@ interface IInputConstants const int MOTION_EVENT_FLAG_NO_FOCUS_CHANGE = 0x40; /** + * This flag indicates that the event has a valid value for AXIS_ORIENTATION. + * + * This is a private flag that is not used in Java. + * @hide + */ + const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION = 0x80; + + /** + * This flag indicates that the pointers' AXIS_ORIENTATION can be used to precisely determine + * the direction in which the tool is pointing. The value of the orientation axis will be in + * the range [-pi, pi], which represents a full circle. This is usually supported by devices + * like styluses. + * + * Conversely, AXIS_ORIENTATION cannot be used to tell which direction the tool is pointing + * when this flag is not set. In this case, the axis value will have a range of [-pi/2, pi/2], + * which represents half a circle. This is usually the case for devices like touchscreens and + * touchpads, for which it is difficult to tell which direction along the major axis of the + * touch ellipse the finger is pointing. + * + * This is a private flag that is not used in Java. + * @hide + */ + const int MOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION = 0x100; + + /** * The input event was generated or modified by accessibility service. * Shared by both KeyEvent and MotionEvent flags, so this value should not overlap with either * set of flags, including in input/Input.h and in android/input.h. diff --git a/libs/input/tests/InputEvent_test.cpp b/libs/input/tests/InputEvent_test.cpp index 476b5cf818..3717f49fef 100644 --- a/libs/input/tests/InputEvent_test.cpp +++ b/libs/input/tests/InputEvent_test.cpp @@ -26,23 +26,39 @@ namespace android { +namespace { + // Default display id. -static constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; +constexpr ui::LogicalDisplayId DISPLAY_ID = ui::LogicalDisplayId::DEFAULT; -static constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; +constexpr float EPSILON = MotionEvent::ROUNDING_PRECISION; -static constexpr auto POINTER_0_DOWN = +constexpr auto POINTER_0_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_1_DOWN = +constexpr auto POINTER_1_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_0_UP = +constexpr auto POINTER_0_UP = AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); -static constexpr auto POINTER_1_UP = +constexpr auto POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +std::array<float, 9> asFloat9(const ui::Transform& t) { + std::array<float, 9> mat{}; + mat[0] = t[0][0]; + mat[1] = t[1][0]; + mat[2] = t[2][0]; + mat[3] = t[0][1]; + mat[4] = t[1][1]; + mat[5] = t[2][1]; + mat[6] = t[0][2]; + mat[7] = t[1][2]; + mat[8] = t[2][2]; + return mat; +} + class BaseTest : public testing::Test { protected: static constexpr std::array<uint8_t, 32> HMAC = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -50,6 +66,8 @@ protected: 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; }; +} // namespace + // --- PointerCoordsTest --- class PointerCoordsTest : public BaseTest { @@ -344,13 +362,15 @@ void MotionEventTest::SetUp() { } void MotionEventTest::initializeEventWithHistory(MotionEvent* event) { + const int32_t flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; event->initialize(mId, 2, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, HMAC, - AMOTION_EVENT_ACTION_MOVE, 0, AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, - 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, - mRawTransform, ARBITRARY_DOWN_TIME, ARBITRARY_EVENT_TIME, 2, - mPointerProperties, mSamples[0].pointerCoords); + AMOTION_EVENT_ACTION_MOVE, 0, flags, 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, mRawTransform, ARBITRARY_DOWN_TIME, + ARBITRARY_EVENT_TIME, 2, mPointerProperties, mSamples[0].pointerCoords); event->addSample(ARBITRARY_EVENT_TIME + 1, mSamples[1].pointerCoords); event->addSample(ARBITRARY_EVENT_TIME + 2, mSamples[2].pointerCoords); } @@ -364,7 +384,10 @@ void MotionEventTest::assertEqualsEventWithHistory(const MotionEvent* event) { ASSERT_EQ(DISPLAY_ID, event->getDisplayId()); EXPECT_EQ(HMAC, event->getHmac()); ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, event->getAction()); - ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED, event->getFlags()); + ASSERT_EQ(AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION, + event->getFlags()); ASSERT_EQ(AMOTION_EVENT_EDGE_FLAG_TOP, event->getEdgeFlags()); ASSERT_EQ(AMETA_ALT_ON, event->getMetaState()); ASSERT_EQ(AMOTION_EVENT_BUTTON_PRIMARY, event->getButtonState()); @@ -799,8 +822,10 @@ TEST_F(MotionEventTest, Transform) { } MotionEvent event; ui::Transform identityTransform; + const int32_t flags = AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; event.initialize(InputEvent::nextId(), /*deviceId=*/0, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID, - INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, /*flags=*/0, + INVALID_HMAC, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0, flags, AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, /*buttonState=*/0, MotionClassification::NONE, identityTransform, /*xPrecision=*/0, /*yPrecision=*/0, /*xCursorPosition=*/3 + RADIUS, /*yCursorPosition=*/2, @@ -1087,4 +1112,90 @@ TEST_F(MotionEventTest, CoordinatesAreRoundedAppropriately) { ASSERT_EQ(EXPECTED.y, event.getYCursorPosition()); } +TEST_F(MotionEventTest, InvalidOrientationNotRotated) { + // This touch event does not have a value for AXIS_ORIENTATION, and the flags are implicitly + // set to 0. The transform is set to a 90-degree rotation. + MotionEvent event = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .build(); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); + event.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_EQ(event.getOrientation(/*pointerIndex=*/0), 0.f); +} + +TEST_F(MotionEventTest, ValidZeroOrientationRotated) { + // This touch events will implicitly have a value of 0 for its AXIS_ORIENTATION. + auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER).x(4).y(4)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); + MotionEvent nonDirectionalEvent = builder.build(); + MotionEvent directionalEvent = + builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build(); + + // The angle is rotated by the initial transform, a 90-degree rotation. + ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), M_PI_2, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + ASSERT_NEAR(fabs(directionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), 0.f, EPSILON); + + nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_NEAR(fabs(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0)), M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), -M_PI_2, EPSILON); +} + +TEST_F(MotionEventTest, ValidNonZeroOrientationRotated) { + const float initial = 1.f; + auto builder = MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN) + .downTime(ARBITRARY_DOWN_TIME) + .pointer(PointerBuilder(/*id=*/4, ToolType::FINGER) + .x(4) + .y(4) + .axis(AMOTION_EVENT_AXIS_ORIENTATION, initial)) + .transform(ui::Transform(ui::Transform::ROT_90, 100, 100)) + .rawTransform(ui::Transform(ui::Transform::FLIP_H, 50, 50)) + .addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); + + MotionEvent nonDirectionalEvent = builder.build(); + MotionEvent directionalEvent = + builder.addFlag(AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION).build(); + + // The angle is rotated by the initial transform, a 90-degree rotation. + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial + M_PI_2, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_90, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI, EPSILON); + + nonDirectionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + directionalEvent.transform(asFloat9(ui::Transform(ui::Transform::ROT_180, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial, EPSILON); + + nonDirectionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + directionalEvent.applyTransform(asFloat9(ui::Transform(ui::Transform::ROT_270, 100, 100))); + ASSERT_NEAR(nonDirectionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); + ASSERT_NEAR(directionalEvent.getOrientation(/*pointerIndex=*/0), initial - M_PI_2, EPSILON); +} + } // namespace android diff --git a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp index 70529bbd39..f49469ccca 100644 --- a/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumerNoResampling_test.cpp @@ -96,7 +96,9 @@ PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; if (action == AMOTION_EVENT_ACTION_CANCEL) { flags |= AMOTION_EVENT_FLAG_CANCELED; } diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp index 48512f7c6e..e65a919bd6 100644 --- a/libs/input/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp @@ -89,7 +89,9 @@ PublishMotionArgs::PublishMotionArgs(int32_t inAction, nsecs_t inDownTime, hmac = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; - flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED; + flags = AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; if (action == AMOTION_EVENT_ACTION_CANCEL) { flags |= AMOTION_EVENT_FLAG_CANCELED; } diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp index 29aa3c3066..1a0ec48525 100644 --- a/services/inputflinger/dispatcher/Android.bp +++ b/services/inputflinger/dispatcher/Android.bp @@ -56,7 +56,9 @@ filegroup { cc_defaults { name: "libinputdispatcher_defaults", - srcs: [":libinputdispatcher_sources"], + srcs: [ + ":libinputdispatcher_sources", + ], shared_libs: [ "libbase", "libbinder", @@ -78,6 +80,7 @@ cc_defaults { "libattestation", "libgui_window_info_static", "libperfetto_client_experimental", + "perfetto_winscope_extensions_zero", ], target: { android: { diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index d22f3197bf..5ed5eb8e33 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -444,10 +444,12 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerato newCoords.copyFrom(motionEntry.pointerCoords[i]); // First, apply the current pointer's transform to update the coordinates into // window space. - newCoords.transform(currTransform); + MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source, + motionEntry.flags, currTransform); // Next, apply the inverse transform of the normalized coordinates so the // current coordinates are transformed into the normalized coordinate space. - newCoords.transform(inverseTransform); + MotionEvent::calculateTransformedCoordsInPlace(newCoords, motionEntry.source, + motionEntry.flags, inverseTransform); } } @@ -1894,8 +1896,6 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con doInterceptKeyBeforeDispatchingCommand(focusedWindowToken, *entry); }; postCommandLocked(std::move(command)); - // Poke user activity for keys not passed to user - pokeUserActivityLocked(*entry); return false; // wait for the command to run } else { entry->interceptKeyResult = KeyEntry::InterceptKeyResult::CONTINUE; @@ -1912,8 +1912,12 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED : InputEventInjectionResult::FAILED); mReporter->reportDroppedKey(entry->id); - // Poke user activity for undispatched keys - pokeUserActivityLocked(*entry); + // Poke user activity for consumed keys, as it may have not been reported due to + // the focused window requesting user activity to be disabled + if (*dropReason == DropReason::POLICY && + mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) { + pokeUserActivityLocked(*entry); + } return true; } @@ -3313,22 +3317,16 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) { if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) { return; } - // If the key code is unknown, we don't consider it user activity - if (keyEntry.keyCode == AKEYCODE_UNKNOWN) { - return; - } // Don't inhibit events that were intercepted or are not passed to // the apps, like system shortcuts if (windowDisablingUserActivityInfo != nullptr && - keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP && - keyEntry.policyFlags & POLICY_FLAG_PASS_TO_USER) { + keyEntry.interceptKeyResult != KeyEntry::InterceptKeyResult::SKIP) { if (DEBUG_DISPATCH_CYCLE) { ALOGD("Not poking user activity: disabled by window '%s'.", windowDisablingUserActivityInfo->name.c_str()); } return; } - break; } default: { @@ -5133,8 +5131,8 @@ void InputDispatcher::transformMotionEntryForInjectionLocked( } for (uint32_t i = 0; i < entry.getPointerCount(); i++) { entry.pointerCoords[i] = - MotionEvent::calculateTransformedCoords(entry.source, transformToDisplay, - entry.pointerCoords[i]); + MotionEvent::calculateTransformedCoords(entry.source, entry.flags, + transformToDisplay, entry.pointerCoords[i]); } } diff --git a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp index 2d7554c9c1..0b17507c2c 100644 --- a/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp +++ b/services/inputflinger/dispatcher/trace/AndroidInputEventProtoConverter.cpp @@ -123,7 +123,8 @@ void AndroidInputEventProtoConverter::toProtoWindowDispatchEvent( const auto& coords = motion->pointerCoords[i]; const auto coordsInWindow = - MotionEvent::calculateTransformedCoords(motion->source, args.transform, coords); + MotionEvent::calculateTransformedCoords(motion->source, motion->flags, + args.transform, coords); auto bits = BitSet64(coords.bits); for (int32_t axisIndex = 0; !bits.isEmpty(); axisIndex++) { const uint32_t axis = bits.clearFirstMarkedBit(); diff --git a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp index 9b9633a1bd..3d30ad6c7b 100644 --- a/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp +++ b/services/inputflinger/dispatcher/trace/InputTracingPerfettoBackend.cpp @@ -23,6 +23,8 @@ #include <android-base/logging.h> #include <binder/IServiceManager.h> #include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <private/android_filesystem_config.h> #include <utils/String16.h> @@ -229,7 +231,9 @@ void PerfettoBackend::traceMotionEvent(const TracedMotionEvent& event, } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchMotion = isRedacted ? inputEvent->set_dispatcher_motion_event_redacted() : inputEvent->set_dispatcher_motion_event(); AndroidInputEventProtoConverter::toProtoMotionEvent(event, *dispatchMotion, isRedacted); @@ -253,7 +257,9 @@ void PerfettoBackend::traceKeyEvent(const TracedKeyEvent& event, } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchKey = isRedacted ? inputEvent->set_dispatcher_key_event_redacted() : inputEvent->set_dispatcher_key_event(); AndroidInputEventProtoConverter::toProtoKeyEvent(event, *dispatchKey, isRedacted); @@ -277,7 +283,9 @@ void PerfettoBackend::traceWindowDispatch(const WindowDispatchArgs& dispatchArgs } const bool isRedacted = traceLevel == TraceLevel::TRACE_LEVEL_REDACTED; auto tracePacket = ctx.NewTracePacket(); - auto* inputEvent = tracePacket->set_android_input_event(); + auto* winscopeExtensions = static_cast<perfetto::protos::pbzero::WinscopeExtensionsImpl*>( + tracePacket->set_winscope_extensions()); + auto* inputEvent = winscopeExtensions->set_android_input_event(); auto* dispatchEvent = isRedacted ? inputEvent->set_dispatcher_window_dispatch_event_redacted() : inputEvent->set_dispatcher_window_dispatch_event(); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 9d049ae780..a3834908a7 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -2343,20 +2343,23 @@ void TouchInputMapper::cookPointerData() { if (mHaveTilt) { float tiltXAngle = (in.tiltX - mTiltXCenter) * mTiltXScale; float tiltYAngle = (in.tiltY - mTiltYCenter) * mTiltYScale; - orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle))); + orientation = transformAngle(mRawRotation, atan2f(-sinf(tiltXAngle), sinf(tiltYAngle)), + /*isDirectional=*/true); tilt = acosf(cosf(tiltXAngle) * cosf(tiltYAngle)); } else { tilt = 0; switch (mCalibration.orientationCalibration) { case Calibration::OrientationCalibration::INTERPOLATED: - orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale); + orientation = transformAngle(mRawRotation, in.orientation * mOrientationScale, + /*isDirectional=*/true); break; case Calibration::OrientationCalibration::VECTOR: { int32_t c1 = signExtendNybble((in.orientation & 0xf0) >> 4); int32_t c2 = signExtendNybble(in.orientation & 0x0f); if (c1 != 0 || c2 != 0) { - orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f); + orientation = transformAngle(mRawRotation, atan2f(c1, c2) * 0.5f, + /*isDirectional=*/true); float confidence = hypotf(c1, c2); float scale = 1.0f + confidence / 16.0f; touchMajor *= scale; @@ -3672,6 +3675,14 @@ NotifyMotionArgs TouchInputMapper::dispatchMotion( if (mCurrentStreamModifiedByExternalStylus) { source |= AINPUT_SOURCE_BLUETOOTH_STYLUS; } + if (mOrientedRanges.orientation.has_value()) { + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION; + if (mOrientedRanges.tilt.has_value()) { + // In the current implementation, only devices that report a value for tilt supports + // directional orientation. + flags |= AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION; + } + } const ui::LogicalDisplayId displayId = getAssociatedDisplayId().value_or(ui::LogicalDisplayId::INVALID); diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp index 530416c7aa..e17ee3a5d9 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.cpp @@ -215,6 +215,10 @@ void FakeInputDispatcherPolicy::setStaleEventTimeout(std::chrono::nanoseconds ti mStaleEventTimeout = timeout; } +void FakeInputDispatcherPolicy::setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching) { + mConsumeKeyBeforeDispatching = consumeKeyBeforeDispatching; +} + void FakeInputDispatcherPolicy::assertUserActivityNotPoked() { std::unique_lock lock(mLock); base::ScopedLockAssertion assumeLocked(mLock); @@ -401,6 +405,9 @@ void FakeInputDispatcherPolicy::interceptMotionBeforeQueueing(ui::LogicalDisplay nsecs_t FakeInputDispatcherPolicy::interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) { + if (mConsumeKeyBeforeDispatching) { + return -1; + } nsecs_t delay = std::chrono::nanoseconds(mInterceptKeyTimeout).count(); // Clear intercept state so we could dispatch the event in next wake. mInterceptKeyTimeout = 0ms; diff --git a/services/inputflinger/tests/FakeInputDispatcherPolicy.h b/services/inputflinger/tests/FakeInputDispatcherPolicy.h index 2c86146ba3..62ff10f8c8 100644 --- a/services/inputflinger/tests/FakeInputDispatcherPolicy.h +++ b/services/inputflinger/tests/FakeInputDispatcherPolicy.h @@ -115,6 +115,7 @@ public: void setUnhandledKeyHandler(std::function<std::optional<KeyEvent>(const KeyEvent&)> handler); void assertUnhandledKeyReported(int32_t keycode); void assertUnhandledKeyNotReported(); + void setConsumeKeyBeforeDispatching(bool consumeKeyBeforeDispatching); private: std::mutex mLock; @@ -144,6 +145,8 @@ private: std::chrono::nanoseconds mStaleEventTimeout = 1000ms; + bool mConsumeKeyBeforeDispatching = false; + BlockingQueue<std::pair<int32_t /*deviceId*/, std::set<gui::Uid>>> mNotifiedInteractions; std::condition_variable mNotifyUnhandledKey; diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 780bc13f90..56a05a3a3c 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -610,28 +610,6 @@ static NotifyKeyArgs generateKeyArgs( return args; } -static NotifyKeyArgs generateSystemShortcutArgs( - int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) { - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - // Define a valid key event. - NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, - AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_C, KEY_C, - AMETA_META_ON, currentTime); - - return args; -} - -static NotifyKeyArgs generateAssistantKeyArgs( - int32_t action, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::INVALID) { - nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC); - // Define a valid key event. - NotifyKeyArgs args(InputEvent::nextId(), currentTime, /*readTime=*/0, DEVICE_ID, - AINPUT_SOURCE_KEYBOARD, displayId, 0, action, /*flags=*/0, AKEYCODE_ASSIST, - KEY_ASSISTANT, AMETA_NONE, currentTime); - - return args; -} - [[nodiscard]] static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, ui::LogicalDisplayId displayId, const std::vector<PointF>& points) { @@ -6628,17 +6606,18 @@ TEST_F(InputDispatcherTest, FocusedWindow_DisableUserActivity) { window->consumeFocusEvent(true); - mDispatcher->notifyKey(generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + mDispatcher->notifyKey( + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); // Window should receive key down event. window->consumeKeyDown(ui::LogicalDisplayId::DEFAULT); - // Should have poked user activity + // Should have not poked user activity mDispatcher->waitForIdle(); mFakePolicy->assertUserActivityNotPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) { +TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceivePolicyConsumedKey) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", @@ -6650,31 +6629,36 @@ TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveSystemShortcut) { window->consumeFocusEvent(true); + mFakePolicy->setConsumeKeyBeforeDispatching(true); + mDispatcher->notifyKey( - generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); mDispatcher->waitForIdle(); - // System key is not passed down + // Key is not passed down window->assertNoEvents(); // Should have poked user activity mFakePolicy->assertUserActivityPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) { +TEST_F(InputDispatcherTest, FocusedWindow_PolicyConsumedKeyIgnoresDisableUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", ui::LogicalDisplayId::DEFAULT); + window->setDisableUserActivity(true); window->setFocusable(true); mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); setFocusedWindow(window); window->consumeFocusEvent(true); + mFakePolicy->setConsumeKeyBeforeDispatching(true); + mDispatcher->notifyKey( - generateAssistantKeyArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD).keyCode(AKEYCODE_A).build()); mDispatcher->waitForIdle(); // System key is not passed down @@ -6684,30 +6668,39 @@ TEST_F(InputDispatcherTest, FocusedWindow_DoesNotReceiveAssistantKey) { mFakePolicy->assertUserActivityPoked(); } -TEST_F(InputDispatcherTest, FocusedWindow_SystemKeyIgnoresDisableUserActivity) { +class DisableUserActivityInputDispatcherTest : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> {}; + +TEST_P(DisableUserActivityInputDispatcherTest, NotPassedToUserUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, mDispatcher, "Fake Window", ui::LogicalDisplayId::DEFAULT); - window->setDisableUserActivity(true); + window->setDisableUserActivity(GetParam()); + window->setFocusable(true); mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0}); setFocusedWindow(window); window->consumeFocusEvent(true); - mDispatcher->notifyKey( - generateSystemShortcutArgs(AKEY_EVENT_ACTION_DOWN, ui::LogicalDisplayId::DEFAULT)); + mDispatcher->notifyKey(KeyArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_KEYBOARD) + .keyCode(AKEYCODE_A) + .policyFlags(0) + .build()); mDispatcher->waitForIdle(); - // System key is not passed down + // Key is not passed down window->assertNoEvents(); - // Should have poked user activity - mFakePolicy->assertUserActivityPoked(); + // Should not have poked user activity + mFakePolicy->assertUserActivityNotPoked(); } +INSTANTIATE_TEST_CASE_P(DisableUserActivity, DisableUserActivityInputDispatcherTest, + ::testing::Bool()); + TEST_F(InputDispatcherTest, InjectedTouchesPokeUserActivity) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); sp<FakeWindowHandle> window = diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index 8989fceb24..fe238f3087 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -5472,6 +5472,9 @@ TEST_F(SingleTouchInputMapperTest, Process_AllAxes_DefaultCalibration) { ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, pressure, size, tool, tool, tool, tool, orientation, distance)); ASSERT_EQ(tilt, args.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_TILT)); + ASSERT_EQ(args.flags, + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION | + AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_DIRECTIONAL_ORIENTATION); } TEST_F(SingleTouchInputMapperTest, Process_XYAxes_AffineCalibration) { @@ -7914,6 +7917,7 @@ TEST_F(MultiTouchInputMapperTest, Process_AllAxes_WithDefaultCalibration) { ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], x, y, pressure, size, touchMajor, touchMinor, toolMajor, toolMinor, orientation, distance)); + ASSERT_EQ(args.flags, AMOTION_EVENT_PRIVATE_FLAG_SUPPORTS_ORIENTATION); } TEST_F(MultiTouchInputMapperTest, Process_TouchAndToolAxes_GeometricCalibration) { diff --git a/services/inputflinger/tests/InputTraceSession.cpp b/services/inputflinger/tests/InputTraceSession.cpp index 32acb5f288..a9d370aedd 100644 --- a/services/inputflinger/tests/InputTraceSession.cpp +++ b/services/inputflinger/tests/InputTraceSession.cpp @@ -20,6 +20,9 @@ #include <android-base/logging.h> #include <gtest/gtest.h> #include <input/PrintTools.h> +#include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <utility> @@ -30,6 +33,8 @@ using perfetto::protos::pbzero::AndroidInputEventConfig; using perfetto::protos::pbzero::AndroidKeyEvent; using perfetto::protos::pbzero::AndroidMotionEvent; using perfetto::protos::pbzero::AndroidWindowInputDispatchEvent; +using perfetto::protos::pbzero::WinscopeExtensions; +using perfetto::protos::pbzero::WinscopeExtensionsImpl; // These operator<< definitions must be in the global namespace for them to be accessible to the // GTEST library. They cannot be in the anonymous namespace. @@ -85,38 +90,45 @@ auto decodeTrace(const std::string& rawTrace) { Trace::Decoder trace{rawTrace}; if (trace.has_packet()) { - auto it = trace.packet(); - while (it) { + for (auto it = trace.packet(); it; it++) { TracePacket::Decoder packet{it->as_bytes()}; - if (packet.has_android_input_event()) { - AndroidInputEvent::Decoder event{packet.android_input_event()}; - if (event.has_dispatcher_motion_event()) { - tracedMotions.emplace_back(event.dispatcher_motion_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_motion_event_redacted()) { - tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(), - /*redacted=*/true); - } - if (event.has_dispatcher_key_event()) { - tracedKeys.emplace_back(event.dispatcher_key_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_key_event_redacted()) { - tracedKeys.emplace_back(event.dispatcher_key_event_redacted(), - /*redacted=*/true); - } - if (event.has_dispatcher_window_dispatch_event()) { - tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(), - /*redacted=*/false); - } - if (event.has_dispatcher_window_dispatch_event_redacted()) { - tracedWindowDispatches - .emplace_back(event.dispatcher_window_dispatch_event_redacted(), - /*redacted=*/true); - } + if (!packet.has_winscope_extensions()) { + continue; + } + + WinscopeExtensions::Decoder extensions{packet.winscope_extensions()}; + const auto& field = + extensions.Get(WinscopeExtensionsImpl::kAndroidInputEventFieldNumber); + if (!field.valid()) { + continue; + } + + AndroidInputEvent::Decoder event{field.as_bytes()}; + if (event.has_dispatcher_motion_event()) { + tracedMotions.emplace_back(event.dispatcher_motion_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_motion_event_redacted()) { + tracedMotions.emplace_back(event.dispatcher_motion_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_key_event()) { + tracedKeys.emplace_back(event.dispatcher_key_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_key_event_redacted()) { + tracedKeys.emplace_back(event.dispatcher_key_event_redacted(), + /*redacted=*/true); + } + if (event.has_dispatcher_window_dispatch_event()) { + tracedWindowDispatches.emplace_back(event.dispatcher_window_dispatch_event(), + /*redacted=*/false); + } + if (event.has_dispatcher_window_dispatch_event_redacted()) { + tracedWindowDispatches + .emplace_back(event.dispatcher_window_dispatch_event_redacted(), + /*redacted=*/true); } - it++; } } return std::tuple{std::move(tracedMotions), std::move(tracedKeys), diff --git a/services/inputflinger/tests/InputTraceSession.h b/services/inputflinger/tests/InputTraceSession.h index ed20bc8343..bda552165e 100644 --- a/services/inputflinger/tests/InputTraceSession.h +++ b/services/inputflinger/tests/InputTraceSession.h @@ -22,7 +22,6 @@ #include <gtest/gtest.h> #include <input/Input.h> #include <perfetto/config/android/android_input_event_config.pbzero.h> -#include <perfetto/trace/android/android_input_event.pbzero.h> #include <perfetto/trace/trace.pbzero.h> #include <perfetto/tracing.h> #include <variant> diff --git a/services/inputflinger/tests/InputTracingTest.cpp b/services/inputflinger/tests/InputTracingTest.cpp index 617d67f34d..2ccd93ec4c 100644 --- a/services/inputflinger/tests/InputTracingTest.cpp +++ b/services/inputflinger/tests/InputTracingTest.cpp @@ -30,6 +30,8 @@ #include <gtest/gtest.h> #include <input/Input.h> #include <perfetto/trace/android/android_input_event.pbzero.h> +#include <perfetto/trace/android/winscope_extensions.pbzero.h> +#include <perfetto/trace/android/winscope_extensions_impl.pbzero.h> #include <perfetto/trace/trace.pbzero.h> #include <private/android_filesystem_config.h> #include <map> |