Move common device metrics code to InputDeviceMetricsSource

Move common device metrics code from InputDeviceMetricsCollector to
InputDeviceMetricsSource.
The common code moved is:
* enum class InputDeviceUsageSource
* InputDeviceUsageSource getUsageSourceForKeyArgs(int32_t, const NotifyKeyArgs&)
* set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&)

This code will be used by both InputDeviceMetricsCollector,
which is a part of inputflinger as well as LatencyTracker,
which is a part of the dispatcher.
InputDeviceMetricsSource can be included in the places using the common code.

Bug: 303059859
Test: atest inputflinger_tests:InputDeviceMetricsSourceDeviceClassificationTest
Change-Id: Ia8528e641cd162d5025495d5bf6903b2dbab2ed3
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index d87a5a7..c0353af 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -50,6 +50,7 @@
         "HardwareProperties_test.cpp",
         "HardwareStateConverter_test.cpp",
         "InputDeviceMetricsCollector_test.cpp",
+        "InputDeviceMetricsSource_test.cpp",
         "InputMapperTest.cpp",
         "InputProcessor_test.cpp",
         "InputProcessorConverter_test.cpp",
diff --git a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
index fdf9ed1..85e055d 100644
--- a/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
+++ b/services/inputflinger/tests/InputDeviceMetricsCollector_test.cpp
@@ -36,7 +36,6 @@
 
 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;
@@ -48,10 +47,6 @@
 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 generateTestIdentifier(int32_t id = DEVICE_ID) {
     InputDeviceIdentifier identifier;
@@ -87,258 +82,6 @@
 
 } // 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(AINPUT_KEYBOARD_TYPE_NONE,
-                                               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(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::KEYBOARD: {
-            ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::DPAD: {
-            ASSERT_EQ(InputDeviceUsageSource::DPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_DPAD_CENTER)
-                                                       .build()));
-
-            ASSERT_EQ(InputDeviceUsageSource::DPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_DPAD_CENTER)
-                                                       .build()));
-            break;
-        }
-
-        case InputDeviceUsageSource::GAMEPAD: {
-            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
-                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
-                                                       .keyCode(AKEYCODE_BUTTON_A)
-                                                       .build()));
-
-            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
-                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
-                                               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, public InputDeviceMetricsLogger {
diff --git a/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
new file mode 100644
index 0000000..84ef52c
--- /dev/null
+++ b/services/inputflinger/tests/InputDeviceMetricsSource_test.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#include "../InputDeviceMetricsSource.h"
+
+#include <NotifyArgsBuilders.h>
+
+#include <android/input.h>
+#include <ftl/enum.h>
+#include <gtest/gtest.h>
+#include <input/Input.h>
+#include <input/InputEventBuilders.h>
+#include <linux/input.h>
+
+#include <set>
+
+namespace android {
+
+namespace {
+
+constexpr auto ALL_USAGE_SOURCES = ftl::enum_range<InputDeviceUsageSource>();
+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);
+
+} // namespace
+
+// --- InputDeviceMetricsSourceDeviceClassificationTest ---
+
+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(AINPUT_KEYBOARD_TYPE_NONE,
+                                               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(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_STYLUS_BUTTON_TAIL)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::KEYBOARD: {
+            ASSERT_EQ(InputDeviceUsageSource::KEYBOARD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::DPAD: {
+            ASSERT_EQ(InputDeviceUsageSource::DPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_DPAD_CENTER)
+                                                       .build()));
+
+            ASSERT_EQ(InputDeviceUsageSource::DPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_DPAD_CENTER)
+                                                       .build()));
+            break;
+        }
+
+        case InputDeviceUsageSource::GAMEPAD: {
+            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_NON_ALPHABETIC,
+                                               KeyArgsBuilder(AKEY_EVENT_ACTION_DOWN, KEY_SOURCES)
+                                                       .keyCode(AKEYCODE_BUTTON_A)
+                                                       .build()));
+
+            ASSERT_EQ(InputDeviceUsageSource::GAMEPAD,
+                      getUsageSourceForKeyArgs(AINPUT_KEYBOARD_TYPE_ALPHABETIC,
+                                               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(InputDeviceMetricsSourceDeviceClassificationTest,
+                         DeviceClassificationFixture,
+                         ::testing::ValuesIn(ALL_USAGE_SOURCES.begin(), ALL_USAGE_SOURCES.end()),
+                         [](const testing::TestParamInfo<InputDeviceUsageSource>& testParamInfo) {
+                             return ftl::enum_string(testParamInfo.param);
+                         });
+
+TEST(InputDeviceMetricsSourceDeviceClassificationTest, 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()));
+}
+
+} // namespace android