| /* |
| * 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 "../InputDeviceMetricsCollector.h" |
| |
| #include <NotifyArgsBuilders.h> |
| #include <gtest/gtest.h> |
| #include <gui/constants.h> |
| #include <input/InputEventBuilders.h> |
| #include <linux/input.h> |
| |
| #include <array> |
| #include <tuple> |
| |
| #include "TestInputListener.h" |
| |
| namespace android { |
| |
| using std::chrono_literals::operator""ns; |
| using std::chrono::nanoseconds; |
| |
| namespace { |
| |
| constexpr auto USAGE_TIMEOUT = 8765309ns; |
| constexpr auto TIME = 999999ns; |
| |
| constexpr int32_t DEVICE_ID = 3; |
| constexpr int32_t DEVICE_ID_2 = 4; |
| constexpr int32_t VID = 0xFEED; |
| constexpr int32_t PID = 0xDEAD; |
| constexpr int32_t VERSION = 0xBEEF; |
| 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; |
| |
| InputDeviceIdentifier generateTestIdentifier(int32_t id = DEVICE_ID) { |
| InputDeviceIdentifier identifier; |
| identifier.name = DEVICE_NAME + "_" + std::to_string(id); |
| identifier.location = LOCATION; |
| identifier.uniqueId = UNIQUE_ID; |
| identifier.vendor = VID; |
| identifier.product = PID; |
| identifier.version = VERSION; |
| identifier.bus = BUS_USB; |
| return identifier; |
| } |
| |
| InputDeviceInfo generateTestDeviceInfo(int32_t id = DEVICE_ID, |
| uint32_t sources = TOUCHSCREEN | STYLUS) { |
| auto info = InputDeviceInfo(); |
| info.initialize(id, /*generation=*/1, /*controllerNumber=*/1, generateTestIdentifier(id), |
| "alias", /*isExternal=*/false, /*hasMic=*/false, ADISPLAY_ID_NONE); |
| info.addSource(sources); |
| return info; |
| } |
| |
| const InputDeviceInfo TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID); |
| const InputDeviceInfo SECOND_TOUCHSCREEN_STYLUS_INFO = generateTestDeviceInfo(DEVICE_ID_2); |
| |
| std::set<gui::Uid> uids(std::initializer_list<int32_t> vals) { |
| std::set<gui::Uid> set; |
| for (const auto val : vals) { |
| set.emplace(val); |
| } |
| return set; |
| } |
| |
| } // namespace |
| |
| // --- InputDeviceMetricsCollectorTest --- |
| |
| class InputDeviceMetricsCollectorTest : public testing::Test, public InputDeviceMetricsLogger { |
| protected: |
| TestInputListener mTestListener; |
| InputDeviceMetricsCollector mMetricsCollector{mTestListener, *this, USAGE_TIMEOUT}; |
| |
| void assertUsageLogged(const InputDeviceInfo& info, nanoseconds duration, |
| std::optional<SourceUsageBreakdown> sourceBreakdown = {}, |
| std::optional<UidUsageBreakdown> uidBreakdown = {}) { |
| ASSERT_GE(mLoggedUsageSessions.size(), 1u); |
| const auto& [loggedInfo, report] = *mLoggedUsageSessions.begin(); |
| const auto& i = info.getIdentifier(); |
| ASSERT_EQ(info.getId(), loggedInfo.deviceId); |
| ASSERT_EQ(i.vendor, loggedInfo.vendor); |
| ASSERT_EQ(i.product, loggedInfo.product); |
| ASSERT_EQ(i.version, loggedInfo.version); |
| ASSERT_EQ(i.bus, loggedInfo.bus); |
| ASSERT_EQ(info.getUsiVersion().has_value(), loggedInfo.isUsiStylus); |
| ASSERT_EQ(duration, report.usageDuration); |
| if (sourceBreakdown) { |
| ASSERT_EQ(sourceBreakdown, report.sourceBreakdown); |
| } |
| if (uidBreakdown) { |
| ASSERT_EQ(uidBreakdown, report.uidBreakdown); |
| } |
| mLoggedUsageSessions.erase(mLoggedUsageSessions.begin()); |
| } |
| |
| void assertUsageNotLogged() { ASSERT_TRUE(mLoggedUsageSessions.empty()); } |
| |
| void setCurrentTime(nanoseconds time) { mCurrentTime = time; } |
| |
| nsecs_t currentTime() const { return mCurrentTime.count(); } |
| |
| 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: |
| std::vector<std::tuple<MetricsDeviceInfo, DeviceUsageReport>> mLoggedUsageSessions; |
| nanoseconds mCurrentTime{TIME}; |
| |
| nanoseconds getCurrentTime() override { return mCurrentTime; } |
| |
| void logInputDeviceUsageReported(const MetricsDeviceInfo& info, |
| const DeviceUsageReport& report) override { |
| mLoggedUsageSessions.emplace_back(info, report); |
| } |
| }; |
| |
| TEST_F(InputDeviceMetricsCollectorTest, DontLogUsageWhenDeviceNotRegistered) { |
| // Device was used. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mTestListener.assertNotifyMotionWasCalled(); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device was used again after the usage timeout expired, but we still don't log usage. |
| setCurrentTime(TIME + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mTestListener.assertNotifyMotionWasCalled(); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, DontLogUsageForIgnoredDevices) { |
| constexpr static std::array<int32_t, 2> ignoredDevices{ |
| {INVALID_INPUT_DEVICE_ID, VIRTUAL_KEYBOARD_ID}}; |
| |
| for (int32_t ignoredDeviceId : ignoredDevices) { |
| mMetricsCollector.notifyInputDevicesChanged( |
| {/*id=*/0, {generateTestDeviceInfo(ignoredDeviceId)}}); |
| |
| // Device was used. |
| mMetricsCollector.notifyMotion(generateMotionArgs(ignoredDeviceId)); |
| mTestListener.assertNotifyMotionWasCalled(); |
| mMetricsCollector.notifyDeviceInteraction(ignoredDeviceId, TIME.count(), uids({0, 1, 2})); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device was used again after the usage timeout expired, but we still don't log usage. |
| setCurrentTime(TIME + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(ignoredDeviceId)); |
| mTestListener.assertNotifyMotionWasCalled(); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Remove the ignored device, and ensure we still don't log usage. |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {}}); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, LogsSingleEventUsageSession) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| |
| // Device was used. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device was used again after the usage timeout. |
| setCurrentTime(TIME + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| // The usage session has zero duration because it consisted of only one event. |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 0ns)); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, LogsMultipleEventUsageSession) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| |
| // Device was used. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device was used again after some time. |
| setCurrentTime(TIME + 21ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| |
| setCurrentTime(TIME + 42ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| |
| // Device was used again after the usage timeout. |
| setCurrentTime(TIME + 42ns + 2 * USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 42ns)); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, RemovingDeviceEndsUsageSession) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| |
| // Device was used. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device was used again after some time. |
| setCurrentTime(TIME + 21ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| |
| // The device was removed before the usage timeout expired. |
| setCurrentTime(TIME + 42ns); |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {}}); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 21ns)); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, TracksUsageFromDifferentDevicesIndependently) { |
| mMetricsCollector.notifyInputDevicesChanged( |
| {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}}); |
| |
| // Device 1 was used. |
| setCurrentTime(TIME); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device 2 was used. |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2)); |
| setCurrentTime(TIME + 400ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device 1 was used after its usage timeout expired. Its usage session is reported. |
| setCurrentTime(TIME + 300ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns)); |
| |
| // Device 2 was used. |
| setCurrentTime(TIME + 350ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device 1 was used. |
| setCurrentTime(TIME + 500ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Device 2 is not used for a while, but Device 1 is used again. |
| setCurrentTime(TIME + 400ns + (2 * USAGE_TIMEOUT)); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| // Since Device 2's usage session ended, its usage should be reported. |
| ASSERT_NO_FATAL_FAILURE( |
| assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 150ns + USAGE_TIMEOUT)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown; |
| |
| // Use touchscreen. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Use a stylus with the same input device. |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, STYLUS, {ToolType::STYLUS})); |
| setCurrentTime(TIME + 400ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, STYLUS, {ToolType::STYLUS})); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Touchscreen was used again after its usage timeout expired. |
| // This should be tracked as a separate usage of the source in the breakdown. |
| setCurrentTime(TIME + 300ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, 100ns); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Continue stylus and touchscreen usages. |
| setCurrentTime(TIME + 350ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, STYLUS, {ToolType::STYLUS})); |
| setCurrentTime(TIME + 450ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Touchscreen was used after the stylus's usage timeout expired. |
| // The stylus usage should be tracked in the source breakdown. |
| setCurrentTime(TIME + 400ns + USAGE_TIMEOUT + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::STYLUS_DIRECT, |
| 150ns + USAGE_TIMEOUT); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Remove all devices to force the usage session to be logged. |
| setCurrentTime(TIME + 500ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyInputDevicesChanged({}); |
| expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, |
| 100ns + USAGE_TIMEOUT); |
| // Verify that only one usage session was logged for the device, and that session was broken |
| // down by source correctly. |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, |
| 400ns + USAGE_TIMEOUT + USAGE_TIMEOUT, |
| expectedSourceBreakdown)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource_TrackSourceByDevice) { |
| mMetricsCollector.notifyInputDevicesChanged( |
| {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}}); |
| InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown1; |
| InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown2; |
| |
| // Use both devices, with different sources. |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2, STYLUS, {ToolType::STYLUS})); |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN)); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2, STYLUS, {ToolType::STYLUS})); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Remove all devices to force the usage session to be logged. |
| mMetricsCollector.notifyInputDevicesChanged({}); |
| expectedSourceBreakdown1.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, 100ns); |
| expectedSourceBreakdown2.emplace_back(InputDeviceUsageSource::STYLUS_DIRECT, 100ns); |
| ASSERT_NO_FATAL_FAILURE( |
| assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns, expectedSourceBreakdown1)); |
| ASSERT_NO_FATAL_FAILURE( |
| assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 100ns, expectedSourceBreakdown2)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource_MultiSourceEvent) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| InputDeviceMetricsLogger::SourceUsageBreakdown expectedSourceBreakdown; |
| |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, // |
| {ToolType::STYLUS})); |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, // |
| {ToolType::STYLUS, ToolType::FINGER})); |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, // |
| {ToolType::STYLUS, ToolType::FINGER})); |
| setCurrentTime(TIME + 300ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, // |
| {ToolType::FINGER})); |
| setCurrentTime(TIME + 400ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID, TOUCHSCREEN | STYLUS, // |
| {ToolType::FINGER})); |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| |
| // Remove all devices to force the usage session to be logged. |
| mMetricsCollector.notifyInputDevicesChanged({}); |
| expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::STYLUS_DIRECT, 200ns); |
| expectedSourceBreakdown.emplace_back(InputDeviceUsageSource::TOUCHSCREEN, 300ns); |
| ASSERT_NO_FATAL_FAILURE( |
| assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 400ns, expectedSourceBreakdown)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, UidsNotTrackedWhenThereIsNoActiveSession) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| |
| // Notify interaction with UIDs before the device is used. |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1})); |
| |
| // Use the device. |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| |
| // Notify interaction for the wrong device. |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({42})); |
| |
| // Notify interaction after usage session would have expired. |
| // This interaction should not be tracked. |
| setCurrentTime(TIME + 200ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3})); |
| |
| // Use the device again, by starting a new usage session. |
| setCurrentTime(TIME + 300ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| |
| // The first usage session is logged. |
| static const UidUsageBreakdown emptyBreakdown; |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 100ns, |
| /*sourceBreakdown=*/{}, |
| /*uidBreakdown=*/emptyBreakdown)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| UidUsageBreakdown expectedUidBreakdown; |
| |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1})); |
| |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2})); |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2, 3})); |
| |
| expectedUidBreakdown.emplace_back(1, 200ns); |
| expectedUidBreakdown.emplace_back(2, 100ns); |
| expectedUidBreakdown.emplace_back(3, 0ns); |
| |
| // Remove the device to force the usage session to be logged. |
| mMetricsCollector.notifyInputDevicesChanged({}); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 200ns, |
| /*sourceBreakdown=*/{}, expectedUidBreakdown)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksMultipleSessionsForUid) { |
| mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {TOUCHSCREEN_STYLUS_INFO}}); |
| UidUsageBreakdown expectedUidBreakdown; |
| |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2})); |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2})); |
| |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1})); |
| |
| setCurrentTime(TIME + 300ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3})); |
| setCurrentTime(TIME + 400ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 3})); |
| |
| setCurrentTime(TIME + 200ns + USAGE_TIMEOUT); |
| expectedUidBreakdown.emplace_back(2, 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({4})); |
| |
| setCurrentTime(TIME + 300ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 4})); |
| |
| setCurrentTime(TIME + 400ns + USAGE_TIMEOUT); |
| expectedUidBreakdown.emplace_back(3, 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({2, 3})); |
| |
| setCurrentTime(TIME + 500ns + USAGE_TIMEOUT); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({3})); |
| |
| // Remove the device to force the usage session to be logged. |
| mMetricsCollector.notifyInputDevicesChanged({}); |
| expectedUidBreakdown.emplace_back(1, 300ns + USAGE_TIMEOUT); |
| expectedUidBreakdown.emplace_back(2, 0ns); |
| expectedUidBreakdown.emplace_back(3, 100ns); |
| expectedUidBreakdown.emplace_back(4, 100ns); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 500ns + USAGE_TIMEOUT, |
| /*sourceBreakdown=*/{}, expectedUidBreakdown)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksUidsByDevice) { |
| mMetricsCollector.notifyInputDevicesChanged( |
| {/*id=*/0, {TOUCHSCREEN_STYLUS_INFO, SECOND_TOUCHSCREEN_STYLUS_INFO}}); |
| UidUsageBreakdown expectedUidBreakdown1; |
| UidUsageBreakdown expectedUidBreakdown2; |
| |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2})); |
| |
| setCurrentTime(TIME + 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3})); |
| |
| setCurrentTime(TIME + 200ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), uids({1, 2})); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2)); |
| mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), uids({1, 3})); |
| |
| setCurrentTime(TIME + 200ns + USAGE_TIMEOUT); |
| expectedUidBreakdown1.emplace_back(1, 200ns); |
| expectedUidBreakdown1.emplace_back(2, 200ns); |
| expectedUidBreakdown2.emplace_back(1, 100ns); |
| expectedUidBreakdown2.emplace_back(3, 100ns); |
| mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(TOUCHSCREEN_STYLUS_INFO, 200ns, |
| /*sourceBreakdown=*/{}, expectedUidBreakdown1)); |
| ASSERT_NO_FATAL_FAILURE(assertUsageLogged(SECOND_TOUCHSCREEN_STYLUS_INFO, 100ns, |
| /*sourceBreakdown=*/{}, expectedUidBreakdown2)); |
| |
| ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged()); |
| } |
| |
| } // namespace android |