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 notifyConfigurationChanged(const NotifyConfigurationChangedArgs& args) override; 111 void notifyKey(const NotifyKeyArgs& args) override; 112 void notifyMotion(const NotifyMotionArgs& args) override; 113 void notifySwitch(const NotifySwitchArgs& args) override; 114 void notifySensor(const NotifySensorArgs& args) override; 115 void notifyVibratorState(const NotifyVibratorStateArgs& args) override; 116 void notifyDeviceReset(const NotifyDeviceResetArgs& args) override; 117 void notifyPointerCaptureChanged(const NotifyPointerCaptureChangedArgs& args) override; 118 119 void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp, 120 const std::set<gui::Uid>& uids) override; 121 void dump(std::string& dump) override; 122 void monitor() override; 123 124 private: 125 std::mutex mLock; 126 InputListenerInterface& mNextListener; 127 InputDeviceMetricsLogger& mLogger GUARDED_BY(mLock); 128 const std::chrono::nanoseconds mUsageSessionTimeout; 129 130 // Type-safe wrapper for input device id. 131 struct DeviceId : ftl::Constructible<DeviceId, std::int32_t>, 132 ftl::Equatable<DeviceId>, 133 ftl::Orderable<DeviceId> { 134 using Constructible::Constructible; 135 }; toString(const DeviceId & id)136 static inline std::string toString(const DeviceId& id) { 137 return std::to_string(ftl::to_underlying(id)); 138 } 139 140 using Uid = gui::Uid; 141 using MetricsDeviceInfo = InputDeviceMetricsLogger::MetricsDeviceInfo; 142 143 std::map<DeviceId, MetricsDeviceInfo> mLoggedDeviceInfos GUARDED_BY(mLock); 144 145 using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>; 146 SyncQueue<Interaction> mInteractionsQueue GUARDED_BY(mLock); 147 148 class ActiveSession { 149 public: 150 explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout, 151 std::chrono::nanoseconds startTime); 152 void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source); 153 void recordInteraction(const Interaction&); 154 bool checkIfCompletedAt(std::chrono::nanoseconds timestamp); 155 InputDeviceMetricsLogger::DeviceUsageReport finishSession(); 156 157 private: 158 struct UsageSession { 159 std::chrono::nanoseconds start{}; 160 std::chrono::nanoseconds end{}; 161 }; 162 163 const std::chrono::nanoseconds mUsageSessionTimeout; 164 UsageSession mDeviceSession{}; 165 166 std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{}; 167 InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{}; 168 169 std::map<Uid, UsageSession> mActiveSessionsByUid{}; 170 InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{}; 171 }; 172 173 // The input devices that currently have active usage sessions. 174 std::map<DeviceId, ActiveSession> mActiveUsageSessions GUARDED_BY(mLock); 175 176 void onInputDevicesChanged(const std::vector<InputDeviceInfo>& infos) REQUIRES(mLock); 177 void onInputDeviceRemoved(DeviceId deviceId, const MetricsDeviceInfo& info) REQUIRES(mLock); 178 using SourceProvider = 179 std::function<std::set<InputDeviceUsageSource>(const MetricsDeviceInfo&)>; 180 void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime, 181 const SourceProvider& getSources) REQUIRES(mLock); 182 void onInputDeviceInteraction(const Interaction&) REQUIRES(mLock); 183 void reportCompletedSessions() REQUIRES(mLock); 184 }; 185 186 } // namespace android 187