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 "InputDeviceMetricsSource.h" 20 #include "InputListener.h" 21 #include "NotifyArgs.h" 22 #include "SyncQueue.h" 23 24 #include <android-base/thread_annotations.h> 25 #include <ftl/mixins.h> 26 #include <gui/WindowInfo.h> 27 #include <input/InputDevice.h> 28 #include <chrono> 29 #include <functional> 30 #include <map> 31 #include <mutex> 32 #include <set> 33 #include <vector> 34 35 namespace android { 36 37 /** 38 * Logs metrics about registered input devices and their usages. 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 /** Called by the heartbeat to ensure that this component has not deadlocked. */ 55 virtual void monitor() = 0; 56 }; 57 58 /** The logging interface for the metrics collector, injected for testing. */ 59 class InputDeviceMetricsLogger { 60 public: 61 virtual std::chrono::nanoseconds getCurrentTime() = 0; 62 63 // Describes the breakdown of an input device usage session by its usage sources. 64 // An input device can have more than one usage source. For example, some game controllers have 65 // buttons, joysticks, and touchpads. We track usage by these sources to get a better picture of 66 // the device usage. The source breakdown of a 10 minute usage session could look like this: 67 // { {GAMEPAD, <9 mins>}, {TOUCHPAD, <2 mins>}, {TOUCHPAD, <3 mins>} } 68 // This would indicate that the GAMEPAD source was used first, and that source usage session 69 // lasted for 9 mins. During that time, the TOUCHPAD was used for 2 mins, until its source 70 // usage session expired. The TOUCHPAD was then used again later for another 3 mins. 71 using SourceUsageBreakdown = 72 std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>; 73 74 // Describes the breakdown of an input device usage session by the UIDs that it interacted with. 75 using UidUsageBreakdown = 76 std::vector<std::pair<gui::Uid, std::chrono::nanoseconds /*duration*/>>; 77 78 struct DeviceUsageReport { 79 std::chrono::nanoseconds usageDuration; 80 SourceUsageBreakdown sourceBreakdown; 81 UidUsageBreakdown uidBreakdown; 82 }; 83 84 // A subset of information from the InputDeviceInfo class that is used for metrics collection, 85 // used to avoid copying and storing all of the fields and strings in InputDeviceInfo. 86 struct MetricsDeviceInfo { 87 int32_t deviceId; 88 int32_t vendor; 89 int32_t product; 90 int32_t version; 91 int32_t bus; 92 bool isUsiStylus; 93 int32_t keyboardType; 94 }; 95 virtual void logInputDeviceUsageReported(const MetricsDeviceInfo&, 96 const DeviceUsageReport&) = 0; 97 virtual ~InputDeviceMetricsLogger() = default; 98 }; 99 100 class InputDeviceMetricsCollector : public InputDeviceMetricsCollectorInterface { 101 public: 102 explicit InputDeviceMetricsCollector(InputListenerInterface& listener); 103 ~InputDeviceMetricsCollector() override = default; 104 105 // Test constructor 106 InputDeviceMetricsCollector(InputListenerInterface& listener, InputDeviceMetricsLogger& logger, 107 std::chrono::nanoseconds usageSessionTimeout); 108 109 void notifyInputDevicesChanged(const NotifyInputDevicesChangedArgs& args) override; 110 void notifyKey(const NotifyKeyArgs& args) override; 111 void notifyMotion(const NotifyMotionArgs& args) override; 112 void notifySwitch(const NotifySwitchArgs& args) override; 113 void notifySensor(const NotifySensorArgs& args) override; 114 void notifyVibratorState(const NotifyVibratorStateArgs& args) override; 115 void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; 116 void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; 117 118 void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, 119 const std::set<gui::Uid>& uids) override; 120 void dump(std::string& dump) override; 121 void monitor() override; 122 123 private: 124 std::mutex mLock; 125 InputListenerInterface& mNextListener; 126 InputDeviceMetricsLogger& mLogger GUARDED_BY(mLock); 127 const std::chrono::nanoseconds mUsageSessionTimeout; 128 129 // Type-safe wrapper for input device id. 130 struct DeviceId : ftl::Constructible<DeviceId, std::int32_t>, 131 ftl::Equatable<DeviceId>, 132 ftl::Orderable<DeviceId> { 133 using Constructible::Constructible; 134 }; toString(const DeviceId & id)135 static inline std::string toString(const DeviceId& id) { 136 return std::to_string(ftl::to_underlying(id)); 137 } 138 139 using Uid = gui::Uid; 140 using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo; 141 142 std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos GUARDED_BY(mLock); 143 144 using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>; 145 SyncQueue<Interaction> mInteractionsQueue GUARDED_BY(mLock); 146 147 class ActiveSession { 148 public: 149 explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout, 150 std::chrono::nanoseconds startTime); 151 void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source); 152 void recordInteraction(const Interaction&); 153 bool checkIfCompletedAt(std::chrono::nanoseconds timestamp); 154 InputDeviceMetricsLogger::DeviceUsageReport finishSession(); 155 156 private: 157 struct UsageSession { 158 std::chrono::nanoseconds start{}; 159 std::chrono::nanoseconds end{}; 160 }; 161 162 const std::chrono::nanoseconds mUsageSessionTimeout; 163 UsageSession mDeviceSession{}; 164 165 std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{}; 166 InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{}; 167 168 std::map<Uid, UsageSession> mActiveSessionsByUid{}; 169 InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{}; 170 }; 171 172 // The input devices that currently have active usage sessions. 173 std::map<DeviceId, ActiveSession> mActiveUsageSessions GUARDED_BY(mLock); 174 175 void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) REQUIRES(mLock); 176 void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info) REQUIRES(mLock); 177 using SourceProvider = 178 std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>; 179 void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime, 180 const SourceProvider& getSources) REQUIRES(mLock); 181 void onInputDeviceInteraction(const Interaction&) REQUIRES(mLock); 182 void reportCompletedSessions() REQUIRES(mLock); 183 }; 184 185 } // namespace android 186