• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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