| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/base/device_monitor_linux.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include "base/bind.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "device/udev_linux/udev.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| const char kUdevName[] = "udev"; |
| const char kUdevActionAdd[] = "add"; |
| const char kUdevActionRemove[] = "remove"; |
| |
| // The instance will be reset when message loop destroys. |
| base::LazyInstance<DeviceMonitorLinux>::Leaky g_device_monitor_linux = |
| LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| DeviceMonitorLinux::DeviceMonitorLinux() : monitor_fd_(-1) { |
| base::ThreadRestrictions::AssertIOAllowed(); |
| |
| udev_.reset(udev_new()); |
| if (!udev_) { |
| LOG(ERROR) << "Failed to create udev."; |
| return; |
| } |
| monitor_.reset(udev_monitor_new_from_netlink(udev_.get(), kUdevName)); |
| if (!monitor_) { |
| LOG(ERROR) << "Failed to create udev monitor."; |
| return; |
| } |
| |
| int ret = udev_monitor_enable_receiving(monitor_.get()); |
| if (ret != 0) { |
| LOG(ERROR) << "Failed to start udev monitoring."; |
| return; |
| } |
| |
| monitor_fd_ = udev_monitor_get_fd(monitor_.get()); |
| if (monitor_fd_ <= 0) { |
| LOG(ERROR) << "Failed to get udev monitor FD."; |
| return; |
| } |
| } |
| |
| // static |
| DeviceMonitorLinux* DeviceMonitorLinux::GetInstance() { |
| return g_device_monitor_linux.Pointer(); |
| } |
| |
| void DeviceMonitorLinux::AddObserver(Observer* observer) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| observers_.AddObserver(observer); |
| |
| if (monitor_watch_controller_) |
| return; |
| |
| monitor_watch_controller_ = base::FileDescriptorWatcher::WatchReadable( |
| monitor_fd_, |
| base::Bind(&DeviceMonitorLinux::OnMonitorCanReadWithoutBlocking, |
| base::Unretained(this))); |
| } |
| |
| void DeviceMonitorLinux::RemoveObserver(Observer* observer) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| observers_.RemoveObserver(observer); |
| |
| if (observers_.might_have_observers()) |
| return; |
| |
| monitor_watch_controller_.reset(); |
| } |
| |
| void DeviceMonitorLinux::Enumerate(const EnumerateCallback& callback) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev_.get())); |
| |
| if (!enumerate) { |
| LOG(ERROR) << "Failed to enumerate devices."; |
| return; |
| } |
| |
| if (udev_enumerate_scan_devices(enumerate.get()) != 0) { |
| LOG(ERROR) << "Failed to enumerate devices."; |
| return; |
| } |
| |
| // This list is managed by |enumerate|. |
| udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); |
| for (udev_list_entry* i = devices; i != nullptr; |
| i = udev_list_entry_get_next(i)) { |
| ScopedUdevDevicePtr device( |
| udev_device_new_from_syspath(udev_.get(), udev_list_entry_get_name(i))); |
| if (device) |
| callback.Run(device.get()); |
| } |
| } |
| |
| DeviceMonitorLinux::~DeviceMonitorLinux() { |
| // A leaky LazyInstance is never destroyed. |
| NOTREACHED(); |
| } |
| |
| void DeviceMonitorLinux::OnMonitorCanReadWithoutBlocking() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| ScopedUdevDevicePtr device(udev_monitor_receive_device(monitor_.get())); |
| if (!device) |
| return; |
| |
| std::string action(udev_device_get_action(device.get())); |
| if (action == kUdevActionAdd) { |
| for (auto& observer : observers_) |
| observer.OnDeviceAdded(device.get()); |
| } else if (action == kUdevActionRemove) { |
| for (auto& observer : observers_) |
| observer.OnDeviceRemoved(device.get()); |
| } |
| } |
| |
| } // namespace device |