/* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "usb_libusb_inhouse_hotplug.h" #include #include #include #include "adb_trace.h" #include "client/usb_libusb_hotplug.h" #include "libusb/libusb.h" namespace libusb_inhouse_hotplug { class ScanRateLimiter { public: ScanRateLimiter(std::chrono::seconds rate) : rate_s_(rate) { Tick(); } bool Exceeded() { auto elapsed_since_last_scan = std::chrono::duration_cast( std::chrono::steady_clock::now() - last_tick_); return elapsed_since_last_scan < rate_s_; } void Tick() { last_tick_ = std::chrono::steady_clock::now(); } private: std::chrono::seconds rate_s_; std::chrono::time_point last_tick_; }; std::chrono::seconds kScan_rate_s = std::chrono::seconds(2); static ScanRateLimiter rate_limiter{kScan_rate_s}; // We need to synchronize access to the list of known devices. It can be modified from both the // monitoring thread but also LibUsbConnection threads (when they report being closed). static std::mutex known_devices_mutex [[clang::no_destroy]]; static std::unordered_map GUARDED_BY(known_devices_mutex) known_devices [[clang::no_destroy]]; void scan() { if (rate_limiter.Exceeded()) { return; } rate_limiter.Tick(); VLOG(USB) << "inhouse USB scanning"; std::lock_guard lock(known_devices_mutex); // First retrieve all connected devices and detect new ones. libusb_device** devs = nullptr; libusb_get_device_list(nullptr, &devs); std::unordered_map current_devices; for (size_t i = 0; devs[i] != nullptr; i++) { libusb_device* dev = devs[i]; auto session_id = LibUsbDevice::GenerateSessionId(dev).id; if (!known_devices.contains(session_id) && !current_devices.contains(session_id)) { hotplug_callback(nullptr, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED, nullptr); } current_devices[session_id] = dev; } // Handle disconnected devices for (const auto& [session_id, dev] : known_devices) { if (!current_devices.contains(session_id)) { hotplug_callback(nullptr, dev, LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT, nullptr); } } known_devices = std::move(current_devices); libusb_free_device_list(devs, false); } void report_error(const LibUsbConnection& connection) { if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { return; } std::lock_guard lock(known_devices_mutex); known_devices.erase(connection.GetSessionId()); } } // end namespace libusb_inhouse_hotplug