blob: a3707c294852c05abdcdf578c9ee3fcd11e9ead7 [file] [log] [blame]
// Copyright 2013 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.
// libudev is used for monitoring device changes.
#include "media/device_monitors/device_monitor_udev.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/sequence_checker.h"
#include "base/system/system_monitor.h"
#include "base/task/post_task.h"
#include "device/udev_linux/udev.h"
#include "device/udev_linux/udev_watcher.h"
namespace {
struct SubsystemMap {
base::SystemMonitor::DeviceType device_type;
const char* subsystem;
const char* devtype;
};
const char kAudioSubsystem[] = "sound";
const char kVideoSubsystem[] = "video4linux";
// Add more subsystems here for monitoring.
const SubsystemMap kSubsystemMap[] = {
{base::SystemMonitor::DEVTYPE_AUDIO, kAudioSubsystem, NULL},
{base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kVideoSubsystem, NULL},
};
} // namespace
namespace media {
// Wraps a device::UdevWatcher with an API that makes it easier to use from
// DeviceMonitorLinux. Since it is essentially a wrapper around blocking udev
// calls, Initialize() must be called from a task runner that can block.
class DeviceMonitorLinux::BlockingTaskRunnerHelper
: public device::UdevWatcher::Observer {
public:
BlockingTaskRunnerHelper();
~BlockingTaskRunnerHelper() override = default;
void Initialize();
private:
void OnDevicesChanged(device::ScopedUdevDevicePtr device);
// device::UdevWatcher::Observer overrides
void OnDeviceAdded(device::ScopedUdevDevicePtr device) override;
void OnDeviceRemoved(device::ScopedUdevDevicePtr device) override;
std::unique_ptr<device::UdevWatcher> udev_watcher_;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(BlockingTaskRunnerHelper);
};
DeviceMonitorLinux::BlockingTaskRunnerHelper::BlockingTaskRunnerHelper() {
// Detaches from the sequence on which this object was created. It will be
// bound to its owning sequence when Initialize() is called.
DETACH_FROM_SEQUENCE(sequence_checker_);
}
void DeviceMonitorLinux::BlockingTaskRunnerHelper::Initialize() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<device::UdevWatcher::Filter> filters;
for (const SubsystemMap& entry : kSubsystemMap) {
filters.emplace_back(entry.subsystem, entry.devtype);
}
udev_watcher_ = device::UdevWatcher::StartWatching(this, filters);
}
void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDeviceAdded(
device::ScopedUdevDevicePtr device) {
OnDevicesChanged(std::move(device));
}
void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDeviceRemoved(
device::ScopedUdevDevicePtr device) {
OnDevicesChanged(std::move(device));
}
void DeviceMonitorLinux::BlockingTaskRunnerHelper::OnDevicesChanged(
device::ScopedUdevDevicePtr device) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
base::SystemMonitor::DeviceType device_type =
base::SystemMonitor::DEVTYPE_UNKNOWN;
const std::string subsystem(device::udev_device_get_subsystem(device.get()));
for (const SubsystemMap& entry : kSubsystemMap) {
if (subsystem == entry.subsystem) {
device_type = entry.device_type;
break;
}
}
DCHECK_NE(device_type, base::SystemMonitor::DEVTYPE_UNKNOWN);
// base::SystemMonitor takes care of notifying each observer in their own task
// runner via base::ObserverListThreadSafe.
base::SystemMonitor::Get()->ProcessDevicesChanged(device_type);
}
DeviceMonitorLinux::DeviceMonitorLinux()
: blocking_task_runner_(base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::USER_VISIBLE,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN})),
blocking_task_helper_(new BlockingTaskRunnerHelper,
base::OnTaskRunnerDeleter(blocking_task_runner_)) {
// Unretained() is safe because the deletion of |blocking_task_helper_|
// is scheduled on |blocking_task_runner_| when DeviceMonitorLinux is
// deleted.
blocking_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&DeviceMonitorLinux::BlockingTaskRunnerHelper::Initialize,
base::Unretained(blocking_task_helper_.get())));
}
DeviceMonitorLinux::~DeviceMonitorLinux() = default;
} // namespace media