1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include "InputListener.h" 20 #include "NotifyArgs.h" 21 #include "SyncQueue.h" 22 23 #include <ftl/mixins.h> 24 #include <gui/WindowInfo.h> 25 #include <input/InputDevice.h> 26 #include <statslog.h> 27 #include <chrono> 28 #include <functional> 29 #include <map> 30 #include <set> 31 #include <vector> 32 33 namespace android { 34 35 /** 36 * Logs metrics about registered input devices and their usages. 37 * 38 * All methods in the InputListenerInterface must be called from a single thread. 39 */ 40 class InputDeviceMetricsCollectorInterface : public InputListenerInterface { 41 public: 42 /** 43 * Notify the metrics collector that there was an input device interaction with apps. 44 * Called from the InputDispatcher thread. 45 */ 46 virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, 47 const std::set<gui::Uid>& uids) = 0; 48 /** 49 * Dump the state of the interaction blocker. 50 * This method may be called on any thread (usually by the input manager on a binder thread). 51 */ 52 virtual void dump(std::string& dump) = 0; 53 }; 54 55 /** 56 * Enum representation of the InputDeviceUsageSource. 57 */ 58 enum class InputDeviceUsageSource : int32_t { 59 UNKNOWN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__UNKNOWN, 60 BUTTONS = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__BUTTONS, 61 KEYBOARD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__KEYBOARD, 62 DPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__DPAD, 63 GAMEPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__GAMEPAD, 64 JOYSTICK = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__JOYSTICK, 65 MOUSE = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE, 66 MOUSE_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__MOUSE_CAPTURED, 67 TOUCHPAD = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD, 68 TOUCHPAD_CAPTURED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHPAD_CAPTURED, 69 ROTARY_ENCODER = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__ROTARY_ENCODER, 70 STYLUS_DIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_DIRECT, 71 STYLUS_INDIRECT = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_INDIRECT, 72 STYLUS_FUSED = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__STYLUS_FUSED, 73 TOUCH_NAVIGATION = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCH_NAVIGATION, 74 TOUCHSCREEN = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TOUCHSCREEN, 75 TRACKBALL = util::INPUT_DEVICE_USAGE_REPORTED__USAGE_SOURCES__TRACKBALL, 76 77 ftl_first = UNKNOWN, 78 ftl_last = TRACKBALL, 79 }; 80 81 /** Returns the InputDeviceUsageSource that corresponds to the key event. */ 82 InputDeviceUsageSource getUsageSourceForKeyArgs(const InputDeviceInfo&, const NotifyKeyArgs&); 83 84 /** Returns the InputDeviceUsageSources that correspond to the motion event. */ 85 std::set<InputDeviceUsageSource> getUsageSourcesForMotionArgs(const NotifyMotionArgs&); 86 87 /** The logging interface for the metrics collector, injected for testing. */ 88 class InputDeviceMetricsLogger { 89 public: 90 virtual std::chrono::nanoseconds getCurrentTime() = 0; 91 92 // Describes the breakdown of an input device usage session by its usage sources. 93 // An input device can have more than one usage source. For example, some game controllers have 94 // buttons, joysticks, and touchpads. We track usage by these sources to get a better picture of 95 // the device usage. The source breakdown of a 10 minute usage session could look like this: 96 // { {GAMEPAD, <9 mins>}, {TOUCHPAD, <2 mins>}, {TOUCHPAD, <3 mins>} } 97 // This would indicate that the GAMEPAD source was used first, and that source usage session 98 // lasted for 9 mins. During that time, the TOUCHPAD was used for 2 mins, until its source 99 // usage session expired. The TOUCHPAD was then used again later for another 3 mins. 100 using SourceUsageBreakdown = 101 std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>; 102 103 // Describes the breakdown of an input device usage session by the UIDs that it interacted with. 104 using UidUsageBreakdown = 105 std::vector<std::pair<gui::Uid, std::chrono::nanoseconds /*duration*/>>; 106 107 struct DeviceUsageReport { 108 std::chrono::nanoseconds usageDuration; 109 SourceUsageBreakdown sourceBreakdown; 110 UidUsageBreakdown uidBreakdown; 111 }; 112 113 virtual void logInputDeviceUsageReported(const InputDeviceInfo&, const DeviceUsageReport&) = 0; 114 virtual ~InputDeviceMetricsLogger() = default; 115 }; 116 117 class InputDeviceMetricsCollector : public InputDeviceMetricsCollectorInterface { 118 public: 119 explicit InputDeviceMetricsCollector(InputListenerInterface& listener); 120 ~InputDeviceMetricsCollector() override = default; 121 122 // Test constructor 123 InputDeviceMetricsCollector(InputListenerInterface& listener, InputDeviceMetricsLogger& logger, 124 std::chrono::nanoseconds usageSessionTimeout); 125 126 void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; 127 void notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; 128 void notifyKey(const NotifyKeyArgs& args) override; 129 void notifyMotion(const NotifyMotionArgs& args) override; 130 void notifySwitch(const NotifySwitchArgs& args) override; 131 void notifySensor(const NotifySensorArgs& args) override; 132 void notifyVibratorState(const NotifyVibratorStateArgs& args) override; 133 void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; 134 void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; 135 136 void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, 137 const std::set<gui::Uid>& uids) override; 138 void dump(std::string& dump) override; 139 140 private: 141 InputListenerInterface& mNextListener; 142 InputDeviceMetricsLogger& mLogger; 143 const std::chrono::nanoseconds mUsageSessionTimeout; 144 145 // Type-safe wrapper for input device id. 146 struct DeviceId : ftl::Constructible<DeviceId, std::int32_t>, 147 ftl::Equatable<DeviceId>, 148 ftl::Orderable<DeviceId> { 149 using Constructible::Constructible; 150 }; toString(const DeviceId & id)151 static inline std::string toString(const DeviceId& id) { 152 return std::to_string(ftl::to_underlying(id)); 153 } 154 155 using Uid = gui::Uid; 156 157 std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos; 158 159 using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>; 160 SyncQueue<Interaction> mInteractionsQueue; 161 162 class ActiveSession { 163 public: 164 explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout, 165 std::chrono::nanoseconds startTime); 166 void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source); 167 void recordInteraction(const Interaction&); 168 bool checkIfCompletedAt(std::chrono::nanoseconds timestamp); 169 InputDeviceMetricsLogger::DeviceUsageReport finishSession(); 170 171 private: 172 struct UsageSession { 173 std::chrono::nanoseconds start{}; 174 std::chrono::nanoseconds end{}; 175 }; 176 177 const std::chrono::nanoseconds mUsageSessionTimeout; 178 UsageSession mDeviceSession{}; 179 180 std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{}; 181 InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{}; 182 183 std::map<Uid, UsageSession> mActiveSessionsByUid{}; 184 InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{}; 185 }; 186 187 // The input devices that currently have active usage sessions. 188 std::map<DeviceId, ActiveSession> mActiveUsageSessions; 189 190 void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos); 191 void onInputDeviceRemoved(DeviceId deviceId, const InputDeviceInfo& info); 192 using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>; 193 void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime, 194 const SourceProvider& getSources); 195 void onInputDeviceInteraction(const Interaction&); 196 void reportCompletedSessions(); 197 }; 198 199 } // namespace android 200