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