Add logic to classify input events into InputDeviceUsageSources
Bug: 275726706
Test: atest inputflinger_tests
Change-Id: I3edfe3b16d705498beb3453dff83cd2a84a9bc9d
diff --git a/services/inputflinger/InputDeviceMetricsCollector.cpp b/services/inputflinger/InputDeviceMetricsCollector.cpp
index 50f336b..71f3717 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.cpp
+++ b/services/inputflinger/InputDeviceMetricsCollector.cpp
@@ -17,10 +17,11 @@
#define LOG_TAG "InputDeviceMetricsCollector"
#include "InputDeviceMetricsCollector.h"
+#include "KeyCodeClassifications.h"
+
#include <android-base/stringprintf.h>
#include <input/PrintTools.h>
#include <linux/input.h>
-#include <statslog.h>
namespace android {
@@ -80,6 +81,96 @@
} // namespace
+InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo& info,
+ const NotifyKeyArgs& keyArgs) {
+ if (!isFromSource(keyArgs.source, AINPUT_SOURCE_KEYBOARD)) {
+ return InputDeviceUsageSource::UNKNOWN;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_DPAD) &&
+ DPAD_ALL_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::DPAD;
+ }
+
+ if (isFromSource(keyArgs.source, AINPUT_SOURCE_GAMEPAD) &&
+ GAMEPAD_KEYCODES.count(keyArgs.keyCode) != 0) {
+ return InputDeviceUsageSource::GAMEPAD;
+ }
+
+ if (info.getKeyboardType() == AINPUT_KEYBOARD_TYPE_ALPHABETIC) {
+ return InputDeviceUsageSource::KEYBOARD;
+ }
+
+ return InputDeviceUsageSource::BUTTONS;
+}
+
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs& motionArgs) {
+ LOG_ALWAYS_FATAL_IF(motionArgs.pointerCount < 1, "Received motion args without pointers");
+ std::set<InputDeviceUsageSource> sources;
+
+ for (uint32_t i = 0; i < motionArgs.pointerCount; i++) {
+ const auto toolType = motionArgs.pointerProperties[i].toolType;
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE)) {
+ if (toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE);
+ continue;
+ }
+ if (toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD);
+ continue;
+ }
+ if (isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_INDIRECT);
+ continue;
+ }
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_MOUSE_RELATIVE) &&
+ toolType == ToolType::MOUSE) {
+ sources.emplace(InputDeviceUsageSource::MOUSE_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHPAD) &&
+ toolType == ToolType::FINGER) {
+ sources.emplace(InputDeviceUsageSource::TOUCHPAD_CAPTURED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_BLUETOOTH_STYLUS) &&
+ isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_FUSED);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_STYLUS) && isStylusToolType(toolType)) {
+ sources.emplace(InputDeviceUsageSource::STYLUS_DIRECT);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCH_NAVIGATION)) {
+ sources.emplace(InputDeviceUsageSource::TOUCH_NAVIGATION);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_JOYSTICK)) {
+ sources.emplace(InputDeviceUsageSource::JOYSTICK);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_ROTARY_ENCODER)) {
+ sources.emplace(InputDeviceUsageSource::ROTARY_ENCODER);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TRACKBALL)) {
+ sources.emplace(InputDeviceUsageSource::TRACKBALL);
+ continue;
+ }
+ if (isFromSource(motionArgs.source, AINPUT_SOURCE_TOUCHSCREEN)) {
+ sources.emplace(InputDeviceUsageSource::TOUCHSCREEN);
+ continue;
+ }
+ sources.emplace(InputDeviceUsageSource::UNKNOWN);
+ }
+
+ return sources;
+}
+
+// --- InputDeviceMetricsCollector ---
+
InputDeviceMetricsCollector::InputDeviceMetricsCollector(InputListenerInterface& listener)
: InputDeviceMetricsCollector(listener, sStatsdLogger, DEFAULT_USAGE_SESSION_TIMEOUT) {}
diff --git a/services/inputflinger/InputDeviceMetricsCollector.h b/services/inputflinger/InputDeviceMetricsCollector.h
index c9daa92..4ef860f 100644
--- a/services/inputflinger/InputDeviceMetricsCollector.h
+++ b/services/inputflinger/InputDeviceMetricsCollector.h
@@ -17,11 +17,14 @@
#pragma once
#include "InputListener.h"
+#include "NotifyArgs.h"
#include <ftl/mixins.h>
#include <input/InputDevice.h>
+#include <statslog.h>
#include <chrono>
#include <map>
+#include <set>
#include <vector>
namespace android {
@@ -40,6 +43,38 @@
virtual void dump(std::string& dump) = 0;
};
+/**
+ * Enum representation of the InputDeviceUsageSource.
+ */
+enum class InputDeviceUsageSource : int32_t {
+ UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN,
+ BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS,
+ KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD,
+ DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD,
+ GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD,
+ JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK,
+ MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE,
+ MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED,
+ TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD,
+ TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED,
+ ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER,
+ STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT,
+ STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT,
+ STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED,
+ TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION,
+ TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN,
+ TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL,
+
+ ftl_first = UNKNOWN,
+ ftl_last = TRACKBALL,
+};
+
+/** Returns the InputDeviceUsageSource that corresponds to the key event. */
+InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo&, const NotifyKeyArgs&);
+
+/** Returns the InputDeviceUsageSources that correspond to the motion event. */
+std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&);
+
/** The logging interface for the metrics collector, injected for testing. */
class InputDeviceMetricsLogger {
public:
diff --git a/services/inputflinger/include/KeyCodeClassifications.h b/services/inputflinger/include/KeyCodeClassifications.h
new file mode 100644
index 0000000..a09b02e
--- /dev/null
+++ b/services/inputflinger/include/KeyCodeClassifications.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2023 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 <android/input.h>
+#include <set>
+
+namespace android {
+
+/** The set of all Android key codes that are required for a device to be classified as a D-pad. */
+static const std::set<int32_t> DPAD_REQUIRED_KEYCODES = {
+ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_CENTER,
+};
+
+/** The set of all Android key codes that correspond to D-pad keys. */
+static const std::set<int32_t> DPAD_ALL_KEYCODES = {
+ AKEYCODE_DPAD_UP, AKEYCODE_DPAD_DOWN, AKEYCODE_DPAD_LEFT,
+ AKEYCODE_DPAD_RIGHT, AKEYCODE_DPAD_CENTER, AKEYCODE_DPAD_UP_LEFT,
+ AKEYCODE_DPAD_UP_RIGHT, AKEYCODE_DPAD_DOWN_LEFT, AKEYCODE_DPAD_DOWN_RIGHT,
+};
+
+/** The set of all Android key codes that correspond to gamepad buttons. */
+static const std::set<int32_t> GAMEPAD_KEYCODES = {
+ AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
+ AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
+ AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, //
+ AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, //
+ AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, //
+ AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, //
+};
+
+/** The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
+static const std::set<int32_t> STYLUS_BUTTON_KEYCODES = {
+ AKEYCODE_STYLUS_BUTTON_PRIMARY,
+ AKEYCODE_STYLUS_BUTTON_SECONDARY,
+ AKEYCODE_STYLUS_BUTTON_TERTIARY,
+ AKEYCODE_STYLUS_BUTTON_TAIL,
+};
+
+} // namespace android
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index 0354164..04747cc 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -58,6 +58,8 @@
#include "EventHub.h"
+#include "KeyCodeClassifications.h"
+
#define INDENT " "
#define INDENT2 " "
#define INDENT3 " "
@@ -189,14 +191,6 @@
return out;
}
-/* The set of all Android key codes that correspond to buttons (bit-switches) on a stylus. */
-static constexpr std::array<int32_t, 4> STYLUS_BUTTON_KEYCODES = {
- AKEYCODE_STYLUS_BUTTON_PRIMARY,
- AKEYCODE_STYLUS_BUTTON_SECONDARY,
- AKEYCODE_STYLUS_BUTTON_TERTIARY,
- AKEYCODE_STYLUS_BUTTON_TAIL,
-};
-
/**
* Return true if name matches "v4l-touch*"
*/
@@ -2060,15 +2054,6 @@
// ----------------------------------------------------------------------------
-static const int32_t GAMEPAD_KEYCODES[] = {
- AKEYCODE_BUTTON_A, AKEYCODE_BUTTON_B, AKEYCODE_BUTTON_C, //
- AKEYCODE_BUTTON_X, AKEYCODE_BUTTON_Y, AKEYCODE_BUTTON_Z, //
- AKEYCODE_BUTTON_L1, AKEYCODE_BUTTON_R1, //
- AKEYCODE_BUTTON_L2, AKEYCODE_BUTTON_R2, //
- AKEYCODE_BUTTON_THUMBL, AKEYCODE_BUTTON_THUMBR, //
- AKEYCODE_BUTTON_START, AKEYCODE_BUTTON_SELECT, AKEYCODE_BUTTON_MODE, //
-};
-
status_t EventHub::registerFdForEpoll(int fd) {
// TODO(b/121395353) - consider adding EPOLLRDHUP
struct epoll_event eventItem = {};
@@ -2391,31 +2376,23 @@
device->classes |= InputDeviceClass::ALPHAKEY;
}
- // See if this device has a DPAD.
- if (device->hasKeycodeLocked(AKEYCODE_DPAD_UP) &&
- device->hasKeycodeLocked(AKEYCODE_DPAD_DOWN) &&
- device->hasKeycodeLocked(AKEYCODE_DPAD_LEFT) &&
- device->hasKeycodeLocked(AKEYCODE_DPAD_RIGHT) &&
- device->hasKeycodeLocked(AKEYCODE_DPAD_CENTER)) {
+ // See if this device has a D-pad.
+ if (std::all_of(DPAD_REQUIRED_KEYCODES.begin(), DPAD_REQUIRED_KEYCODES.end(),
+ [&](int32_t keycode) { return device->hasKeycodeLocked(keycode); })) {
device->classes |= InputDeviceClass::DPAD;
}
// See if this device has a gamepad.
- for (size_t i = 0; i < sizeof(GAMEPAD_KEYCODES) / sizeof(GAMEPAD_KEYCODES[0]); i++) {
- if (device->hasKeycodeLocked(GAMEPAD_KEYCODES[i])) {
- device->classes |= InputDeviceClass::GAMEPAD;
- break;
- }
+ if (std::any_of(GAMEPAD_KEYCODES.begin(), GAMEPAD_KEYCODES.end(),
+ [&](int32_t keycode) { return device->hasKeycodeLocked(keycode); })) {
+ device->classes |= InputDeviceClass::GAMEPAD;
}
// See if this device has any stylus buttons that we would want to fuse with touch data.
- if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT)) {
- for (int32_t keycode : STYLUS_BUTTON_KEYCODES) {
- if (device->hasKeycodeLocked(keycode)) {
- device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
- break;
- }
- }
+ if (!device->classes.any(InputDeviceClass::TOUCH | InputDeviceClass::TOUCH_MT) &&
+ std::any_of(STYLUS_BUTTON_KEYCODES.begin(), STYLUS_BUTTON_KEYCODES.end(),
+ [&](int32_t keycode) { return device->hasKeycodeLocked(keycode); })) {
+ device->classes |= InputDeviceClass::EXTERNAL_STYLUS;
}
}
diff --git a/services/inputflinger/tests/EventBuilders.h b/services/inputflinger/tests/EventBuilders.h
index a383220..606a57d 100644
--- a/services/inputflinger/tests/EventBuilders.h
+++ b/services/inputflinger/tests/EventBuilders.h
@@ -285,4 +285,78 @@
std::vector<PointerBuilder> mPointers;
};
+class KeyArgsBuilder {
+public:
+ KeyArgsBuilder(int32_t action, int32_t source) {
+ mAction = action;
+ mSource = source;
+ mEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ mDownTime = mEventTime;
+ }
+
+ KeyArgsBuilder& deviceId(int32_t deviceId) {
+ mDeviceId = deviceId;
+ return *this;
+ }
+
+ KeyArgsBuilder& downTime(nsecs_t downTime) {
+ mDownTime = downTime;
+ return *this;
+ }
+
+ KeyArgsBuilder& eventTime(nsecs_t eventTime) {
+ mEventTime = eventTime;
+ return *this;
+ }
+
+ KeyArgsBuilder& displayId(int32_t displayId) {
+ mDisplayId = displayId;
+ return *this;
+ }
+
+ KeyArgsBuilder& policyFlags(int32_t policyFlags) {
+ mPolicyFlags = policyFlags;
+ return *this;
+ }
+
+ KeyArgsBuilder& addFlag(uint32_t flags) {
+ mFlags |= flags;
+ return *this;
+ }
+
+ KeyArgsBuilder& keyCode(int32_t keyCode) {
+ mKeyCode = keyCode;
+ return *this;
+ }
+
+ NotifyKeyArgs build() const {
+ return {InputEvent::nextId(),
+ mEventTime,
+ /*readTime=*/mEventTime,
+ mDeviceId,
+ mSource,
+ mDisplayId,
+ mPolicyFlags,
+ mAction,
+ mFlags,
+ mKeyCode,
+ mScanCode,
+ mMetaState,
+ mDownTime};
+ }
+
+private:
+ int32_t mAction;
+ int32_t mDeviceId = DEFAULT_DEVICE_ID;
+ uint32_t mSource;
+ nsecs_t mDownTime;
+ nsecs_t mEventTime;
+ int32_t mDisplayId{ADISPLAY_ID_DEFAULT};
+ uint32_t mPolicyFlags = DEFAULT_POLICY_FLAGS;
+ int32_t mFlags{0};
+ int32_t mKeyCode{AKEYCODE_UNKNOWN};
+ int32_t mScanCode{0};
+ int32_t mMetaState{AMETA_NONE};
+};
+
} // namespace android
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index d82e426..85529bd 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -22,6 +22,7 @@
#include <array>
#include <tuple>
+#include "EventBuilders.h"
#include "TestInputListener.h"
namespace android {
@@ -33,6 +34,7 @@
constexpr auto USAGE_TIMEOUT = 8765309ns;
constexpr auto TIME = 999999ns;
+constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
constexpr int32_t DEVICE_ID = 3;
constexpr int32_t DEVICE_ID_2 = 4;
@@ -42,6 +44,12 @@
const std::string DEVICE_NAME = "Half Dome";
const std::string LOCATION = "California";
const std::string UNIQUE_ID = "Yosemite";
+constexpr uint32_t TOUCHSCREEN = AINPUT_SOURCE_TOUCHSCREEN;
+constexpr uint32_t STYLUS = AINPUT_SOURCE_STYLUS;
+constexpr uint32_t KEY_SOURCES =
+ AINPUT_SOURCE_KEYBOARD | AINPUT_SOURCE_DPAD | AINPUT_SOURCE_GAMEPAD;
+constexpr int32_t POINTER_1_DOWN =
+ AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
InputDeviceIdentifier getIdentifier(int32_t id = DEVICE_ID) {
InputDeviceIdentifier identifier;
@@ -55,16 +63,277 @@
return identifier;
}
-InputDeviceInfo generateTestDeviceInfo(int32_t id = DEVICE_ID) {
+InputDeviceInfo generateTestDeviceInfo(int32_t id = DEVICE_ID,
+ uint32_t sources = TOUCHSCREEN | STYLUS,
+ bool isAlphabetic = false) {
auto info = InputDeviceInfo();
info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, getIdentifier(id), "alias",
/*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE);
- info.addSource(AINPUT_SOURCE_TOUCHSCREEN);
+ info.addSource(sources);
+ info.setKeyboardType(isAlphabetic ? AINPUT_KEYBOARD_TYPE_ALPHABETIC
+ : AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC);
return info;
}
+const InputDeviceInfo ALPHABETIC_KEYBOARD_INFO =
+ generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/true);
+const InputDeviceInfo NON_ALPHABETIC_KEYBOARD_INFO =
+ generateTestDeviceInfo(DEVICE_ID, KEY_SOURCES, /*isAlphabetic=*/false);
+
} // namespace
+// --- InputDeviceMetricsCollectorDeviceClassificationTest ---
+
+class DeviceClassificationFixture : public ::testing::Test,
+ public ::testing::WithParamInterface<InputDeviceUsageSource> {};
+
+TEST_P(DeviceClassificationFixture, ValidClassifications) {
+ const InputDeviceUsageSource usageSource = GetParam();
+
+ // Use a switch to ensure a test is added for all source classifications.
+ switch (usageSource) {
+ case InputDeviceUsageSource::UNKNOWN: {
+ ASSERT_EQ(InputDeviceUsageSource::UNKNOWN,
+ getUsageSourceForKeyArgs(generateTestDeviceInfo(),
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, TOUCHSCREEN)
+ .build()));
+
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::UNKNOWN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_KEYBOARD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::PALM)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::BUTTONS: {
+ ASSERT_EQ(InputDeviceUsageSource::BUTTONS,
+ getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::KEYBOARD: {
+ ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
+ getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::DPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::DPAD,
+ getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_DPAD_CENTER)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::GAMEPAD: {
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(NON_ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+
+ ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+ getUsageSourceForKeyArgs(ALPHABETIC_KEYBOARD_INFO,
+ KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+ .keyCode(AKEYCODE_BUTTON_A)
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::JOYSTICK: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::JOYSTICK};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_JOYSTICK)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_GAS, 1.f))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_MOVE,
+ AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::MOUSE_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::MOUSE_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_MOUSE_RELATIVE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::MOUSE)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 100)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHPAD_CAPTURED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHPAD_CAPTURED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHPAD)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_X, 1)
+ .axis(AMOTION_EVENT_AXIS_RELATIVE_Y, 2))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::ROTARY_ENCODER: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::ROTARY_ENCODER};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_ROTARY_ENCODER)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_SCROLL, 10)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 10))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_DIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_INDIRECT: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_INDIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ STYLUS | TOUCHSCREEN | AINPUT_SOURCE_MOUSE)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::STYLUS_FUSED: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::STYLUS_FUSED};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_HOVER_ENTER,
+ AINPUT_SOURCE_BLUETOOTH_STYLUS | TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::STYLUS)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCH_NAVIGATION: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCH_NAVIGATION};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE,
+ AINPUT_SOURCE_TOUCH_NAVIGATION)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TOUCHSCREEN: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER)
+ .x(100)
+ .y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::FINGER)
+ .x(300)
+ .y(400))
+ .build()));
+ break;
+ }
+
+ case InputDeviceUsageSource::TRACKBALL: {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TRACKBALL};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(AMOTION_EVENT_ACTION_SCROLL,
+ AINPUT_SOURCE_TRACKBALL)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::UNKNOWN)
+ .axis(AMOTION_EVENT_AXIS_VSCROLL, 100)
+ .axis(AMOTION_EVENT_AXIS_HSCROLL, 200))
+ .build()));
+ break;
+ }
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(InputDeviceMetricsCollectorDeviceClassificationTest,
+ DeviceClassificationFixture,
+ ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
+ [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
+ return ftl::enum_string(testParamInfo.param);
+ });
+
+TEST(InputDeviceMetricsCollectorDeviceClassificationTest, MixedClassificationTouchscreenStylus) {
+ std::set<InputDeviceUsageSource> srcs{InputDeviceUsageSource::TOUCHSCREEN,
+ InputDeviceUsageSource::STYLUS_DIRECT};
+ ASSERT_EQ(srcs,
+ getUsageSourcesForMotionArgs(
+ MotionArgsBuilder(POINTER_1_DOWN, TOUCHSCREEN | STYLUS)
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(100).y(200))
+ .pointer(PointerBuilder(/*id=*/2, ToolType::STYLUS).x(300).y(400))
+ .build()));
+}
+
// --- InputDeviceMetricsCollectorTest ---
class InputDeviceMetricsCollectorTest : public testing::Test, InputDeviceMetricsLogger {
@@ -84,38 +353,17 @@
void setCurrentTime(nanoseconds time) { mCurrentTime = time; }
- NotifyMotionArgs generateMotionArgs(int32_t deviceId) {
- PointerProperties pointerProperties{};
- pointerProperties.id = 0;
- pointerProperties.toolType = ToolType::FINGER;
-
- PointerCoords pointerCoords{};
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, 100);
- pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
-
- return {/*id=*/0,
- mCurrentTime.count(),
- /*readTime=*/0,
- deviceId,
- AINPUT_SOURCE_TOUCHSCREEN,
- /*displayId=*/0,
- POLICY_FLAG_PASS_TO_USER,
- AMOTION_EVENT_ACTION_MOVE,
- /*actionButton=*/0,
- /*flags=*/0,
- AMETA_NONE,
- /*buttonState=*/0,
- MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE,
- /*pointerCount=*/1,
- &pointerProperties,
- &pointerCoords,
- /*xPrecision=*/0,
- /*yPrecision=*/0,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- AMOTION_EVENT_INVALID_CURSOR_POSITION,
- mCurrentTime.count(),
- /*videoFrames=*/{}};
+ NotifyMotionArgs generateMotionArgs(int32_t deviceId,
+ uint32_t source = AINPUT_SOURCE_TOUCHSCREEN,
+ std::vector<ToolType> toolTypes = {ToolType::FINGER}) {
+ MotionArgsBuilder builder(AMOTION_EVENT_ACTION_MOVE, source);
+ for (size_t i = 0; i < toolTypes.size(); i++) {
+ builder.pointer(PointerBuilder(i, toolTypes[i]));
+ }
+ return builder.deviceId(deviceId)
+ .eventTime(mCurrentTime.count())
+ .downTime(mCurrentTime.count())
+ .build();
}
private: