1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 // libudev is used for monitoring device changes.
6
7 #include "media/device_monitors/device_monitor_udev.h"
8
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/sequence_checker.h"
14 #include "base/system/system_monitor.h"
15 #include "base/task/post_task.h"
16 #include "base/task/thread_pool.h"
17 #include "device/udev_linux/udev.h"
18 #include "device/udev_linux/udev_watcher.h"
19
20 namespace {
21
22 struct SubsystemMap {
23 base::SystemMonitor::DeviceType device_type;
24 const char* subsystem;
25 const char* devtype;
26 };
27
28 const char kAudioSubsystem[] = "sound";
29 const char kVideoSubsystem[] = "video4linux";
30
31 // Add more subsystems here for monitoring.
32 const SubsystemMap kSubsystemMap[] = {
33 {base::SystemMonitor::DEVTYPE_AUDIO, kAudioSubsystem, NULL},
34 {base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kVideoSubsystem, NULL},
35 };
36
37 } // namespace
38
39 namespace media {
40
41 // Wraps a device::UdevWatcher with an API that makes it easier to use from
42 // DeviceMonitorLinux. Since it is essentially a wrapper around blocking udev
43 // calls, Initialize() must be called from a task runner that can block.
44 class DeviceMonitorLinux::BlockingTaskRunnerHelper
45 : public device::UdevWatcher::Observer {
46 public:
47 BlockingTaskRunnerHelper();
48 ~BlockingTaskRunnerHelper() override = default;
49
50 void Initialize();
51
52 private:
53 void OnDevicesChanged(device::ScopedUdevDevicePtr device);
54
55 // device::UdevWatcher::Observer overrides
56 void OnDeviceAdded(device::ScopedUdevDevicePtr device) override;
57 void OnDeviceRemoved(device::ScopedUdevDevicePtr device) override;
58 void OnDeviceChanged(device::ScopedUdevDevicePtr device) override;
59
60 std::unique_ptr<device::UdevWatcher> udev_watcher_;
61
62 SEQUENCE_CHECKER(sequence_checker_);
63
64 DISALLOW_COPY_AND_ASSIGN(BlockingTaskRunnerHelper);
65 };
66
BlockingTaskRunnerHelper()67 DeviceMonitorLinux::BlockingTaskRunnerHelper::BlockingTaskRunnerHelper() {
68 // Detaches from the sequence on which this object was created. It will be
69 // bound to its owning sequence when Initialize() is called.
70 DETACH_FROM_SEQUENCE(sequence_checker_);
71 }
72
Initialize()73 void DeviceMonitorLinux::BlockingTaskRunnerHelper::Initialize() {
74 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
75 std::vector<device::UdevWatcher::Filter> filters;
76 for (const SubsystemMap& entry : kSubsystemMap) {
77 filters.emplace_back(entry.subsystem, entry.devtype);
78 }
79 udev_watcher_ = device::UdevWatcher::StartWatching(this, filters);
80 }
81
OnDeviceAdded(device::ScopedUdevDevicePtr device)82 void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDeviceAdded(
83 device::ScopedUdevDevicePtr device) {
84 OnDevicesChanged(std::move(device));
85 }
86
OnDeviceRemoved(device::ScopedUdevDevicePtr device)87 void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDeviceRemoved(
88 device::ScopedUdevDevicePtr device) {
89 OnDevicesChanged(std::move(device));
90 }
91
OnDeviceChanged(device::ScopedUdevDevicePtr device)92 void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDeviceChanged(
93 device::ScopedUdevDevicePtr device) {
94 OnDevicesChanged(std::move(device));
95 }
96
OnDevicesChanged(device::ScopedUdevDevicePtr device)97 void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDevicesChanged(
98 device::ScopedUdevDevicePtr device) {
99 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
100
101 base::SystemMonitor::DeviceType device_type =
102 base::SystemMonitor::DEVTYPE_UNKNOWN;
103 const std::string subsystem(device::udev_device_get_subsystem(device.get()));
104 for (const SubsystemMap& entry : kSubsystemMap) {
105 if (subsystem == entry.subsystem) {
106 device_type = entry.device_type;
107 break;
108 }
109 }
110 DCHECK_NE(device_type, base::SystemMonitor::DEVTYPE_UNKNOWN);
111
112 // base::SystemMonitor takes care of notifying each observer in their own task
113 // runner via base::ObserverListThreadSafe.
114 base::SystemMonitor::Get()->ProcessDevicesChanged(device_type);
115 }
116
DeviceMonitorLinux()117 DeviceMonitorLinux::DeviceMonitorLinux()
118 : blocking_task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
119 {base::MayBlock(), base::TaskPriority::USER_VISIBLE,
120 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
121 blocking_task_helper_(new BlockingTaskRunnerHelper,
122 base::OnTaskRunnerDeleter(blocking_task_runner_)) {
123 // Unretained() is safe because the deletion of |blocking_task_helper_|
124 // is scheduled on |blocking_task_runner_| when DeviceMonitorLinux is
125 // deleted.
126 blocking_task_runner_->PostTask(
127 FROM_HERE,
128 base::BindOnce(&DeviceMonitorLinux::BlockingTaskRunnerHelper::Initialize,
129 base::Unretained(blocking_task_helper_.get())));
130 }
131
132 DeviceMonitorLinux::~DeviceMonitorLinux() = default;
133
134 } // namespace media
135