1 /*
2 * Copyright (C) 2025 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 #include "usb_libusb_inhouse_hotplug.h"
18
19 #include <chrono>
20 #include <thread>
21 #include <unordered_map>
22
23 #include "adb_trace.h"
24 #include "client/usb_libusb_hotplug.h"
25
26 #include "libusb/libusb.h"
27
28 namespace libusb_inhouse_hotplug {
29
30 class ScanRateLimiter {
31 public:
ScanRateLimiter(std::chrono::seconds rate)32 ScanRateLimiter(std::chrono::seconds rate) : rate_s_(rate) { Tick(); }
Exceeded()33 bool Exceeded() {
34 auto elapsed_since_last_scan = std::chrono::duration_cast<std::chrono::seconds>(
35 std::chrono::steady_clock::now() - last_tick_);
36 return elapsed_since_last_scan < rate_s_;
37 }
38
Tick()39 void Tick() { last_tick_ = std::chrono::steady_clock::now(); }
40
41 private:
42 std::chrono::seconds rate_s_;
43 std::chrono::time_point<std::chrono::steady_clock> last_tick_;
44 };
45
46 std::chrono::seconds kScan_rate_s = std::chrono::seconds(2);
47 static ScanRateLimiter rate_limiter{kScan_rate_s};
48
49 // We need to synchronize access to the list of known devices. It can be modified from both the
50 // monitoring thread but also LibUsbConnection threads (when they report being closed).
51 static std::mutex known_devices_mutex [[clang::no_destroy]];
52 static std::unordered_map<uint64_t, libusb_device*> GUARDED_BY(known_devices_mutex) known_devices
53 [[clang::no_destroy]];
54
scan()55 void scan() {
56 if (rate_limiter.Exceeded()) {
57 return;
58 }
59 rate_limiter.Tick();
60
61 VLOG(USB) << "inhouse USB scanning";
62 std::lock_guard<std::mutex> lock(known_devices_mutex);
63
64 // First retrieve all connected devices and detect new ones.
65 libusb_device** devs = nullptr;
66 libusb_get_device_list(nullptr, &devs);
67 std::unordered_map<uint64_t, libusb_device*> current_devices;
68 for (size_t i = 0; devs[i] != nullptr; i++) {
69 libusb_device* dev = devs[i];
70 auto session_id = LibUsbDevice::GenerateSessionId(dev).id;
71 if (!known_devices.contains(session_id) && !current_devices.contains(session_id)) {
72 hotplug_callback(nullptr, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, nullptr);
73 }
74 current_devices[session_id] = dev;
75 }
76
77 // Handle disconnected devices
78 for (const auto& [session_id, dev] : known_devices) {
79 if (!current_devices.contains(session_id)) {
80 hotplug_callback(nullptr, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, nullptr);
81 }
82 }
83 known_devices = std::move(current_devices);
84 libusb_free_device_list(devs, false);
85 }
86
report_error(const LibUsbConnection & connection)87 void report_error(const LibUsbConnection& connection) {
88 if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) {
89 return;
90 }
91 std::lock_guard<std::mutex> lock(known_devices_mutex);
92 known_devices.erase(connection.GetSessionId());
93 }
94 } // end namespace libusb_inhouse_hotplug