blob: aef80a9663c3485b472ed285d80c1c751ba381f5 [file] [log] [blame]
// Copyright 2019 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 "extensions/browser/api/usb/usb_device_manager.h"
#include <memory>
#include <utility>
#include "base/lazy_instance.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/common/service_manager_connection.h"
#include "device/usb/public/mojom/device_enumeration_options.mojom.h"
#include "extensions/browser/api/device_permissions_manager.h"
#include "extensions/browser/event_router_factory.h"
#include "extensions/common/api/usb.h"
#include "extensions/common/permissions/permissions_data.h"
#include "extensions/common/permissions/usb_device_permission.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
namespace usb = extensions::api::usb;
using content::BrowserThread;
namespace extensions {
namespace {
// Returns true if the given extension has permission to receive events
// regarding this device.
bool WillDispatchDeviceEvent(const device::mojom::UsbDeviceInfo& device_info,
content::BrowserContext* browser_context,
const Extension* extension,
Event* event,
const base::DictionaryValue* listener_filter) {
// Check install-time and optional permissions.
std::unique_ptr<UsbDevicePermission::CheckParam> param =
UsbDevicePermission::CheckParam::ForUsbDevice(extension, device_info);
if (extension->permissions_data()->CheckAPIPermissionWithParam(
APIPermission::kUsbDevice, param.get())) {
return true;
}
// Check permissions granted through chrome.usb.getUserSelectedDevices.
DevicePermissions* device_permissions =
DevicePermissionsManager::Get(browser_context)
->GetForExtension(extension->id());
if (device_permissions->FindUsbDeviceEntry(device_info).get()) {
return true;
}
return false;
}
base::LazyInstance<BrowserContextKeyedAPIFactory<UsbDeviceManager>>::Leaky
g_event_router_factory = LAZY_INSTANCE_INITIALIZER;
} // namespace
// static
UsbDeviceManager* UsbDeviceManager::Get(
content::BrowserContext* browser_context) {
return BrowserContextKeyedAPIFactory<UsbDeviceManager>::Get(browser_context);
}
// static
BrowserContextKeyedAPIFactory<UsbDeviceManager>*
UsbDeviceManager::GetFactoryInstance() {
return g_event_router_factory.Pointer();
}
void UsbDeviceManager::Observer::OnDeviceAdded(
const device::mojom::UsbDeviceInfo& device_info) {}
void UsbDeviceManager::Observer::OnDeviceRemoved(
const device::mojom::UsbDeviceInfo& device_info) {}
void UsbDeviceManager::Observer::OnDeviceRemovedCleanup(
const device::mojom::UsbDeviceInfo& device_info) {}
void UsbDeviceManager::Observer::OnDeviceManagerConnectionError() {}
UsbDeviceManager::UsbDeviceManager(content::BrowserContext* browser_context)
: browser_context_(browser_context),
client_binding_(this),
weak_factory_(this) {
EventRouter* event_router = EventRouter::Get(browser_context_);
if (event_router) {
event_router->RegisterObserver(this, usb::OnDeviceAdded::kEventName);
event_router->RegisterObserver(this, usb::OnDeviceRemoved::kEventName);
}
}
UsbDeviceManager::~UsbDeviceManager() {}
void UsbDeviceManager::AddObserver(Observer* observer) {
EnsureConnectionWithDeviceManager();
observer_list_.AddObserver(observer);
}
void UsbDeviceManager::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
int UsbDeviceManager::GetIdFromGuid(const std::string& guid) {
auto iter = guid_to_id_map_.find(guid);
if (iter == guid_to_id_map_.end()) {
auto result = guid_to_id_map_.insert(std::make_pair(guid, next_id_++));
DCHECK(result.second);
iter = result.first;
id_to_guid_map_.insert(std::make_pair(iter->second, guid));
}
return iter->second;
}
bool UsbDeviceManager::GetGuidFromId(int id, std::string* guid) {
auto iter = id_to_guid_map_.find(id);
if (iter == id_to_guid_map_.end())
return false;
*guid = iter->second;
return true;
}
void UsbDeviceManager::GetApiDevice(
const device::mojom::UsbDeviceInfo& device_in,
api::usb::Device* device_out) {
device_out->device = GetIdFromGuid(device_in.guid);
device_out->vendor_id = device_in.vendor_id;
device_out->product_id = device_in.product_id;
device_out->version = device_in.device_version_major << 8 |
device_in.device_version_minor << 4 |
device_in.device_version_subminor;
device_out->product_name =
base::UTF16ToUTF8(device_in.product_name.value_or(base::string16()));
device_out->manufacturer_name =
base::UTF16ToUTF8(device_in.manufacturer_name.value_or(base::string16()));
device_out->serial_number =
base::UTF16ToUTF8(device_in.serial_number.value_or(base::string16()));
}
void UsbDeviceManager::GetDevices(
device::mojom::UsbDeviceManager::GetDevicesCallback callback) {
EnsureConnectionWithDeviceManager();
device_manager_->GetDevices(/*options=*/nullptr, std::move(callback));
}
void UsbDeviceManager::GetDevice(
const std::string& guid,
device::mojom::UsbDeviceRequest device_request,
device::mojom::UsbDeviceClientPtr device_client) {
EnsureConnectionWithDeviceManager();
device_manager_->GetDevice(guid, std::move(device_request),
std::move(device_client));
}
#if defined(OS_CHROMEOS)
void UsbDeviceManager::CheckAccess(
const std::string& guid,
device::mojom::UsbDeviceManager::CheckAccessCallback callback) {
EnsureConnectionWithDeviceManager();
device_manager_->CheckAccess(guid, std::move(callback));
}
#endif // defined(OS_CHROMEOS)
void UsbDeviceManager::Shutdown() {
EventRouter* event_router = EventRouter::Get(browser_context_);
if (event_router) {
event_router->UnregisterObserver(this);
}
}
void UsbDeviceManager::OnListenerAdded(const EventListenerInfo& details) {
EnsureConnectionWithDeviceManager();
}
void UsbDeviceManager::OnDeviceAdded(
device::mojom::UsbDeviceInfoPtr device_info) {
DCHECK(device_info);
DispatchEvent(usb::OnDeviceAdded::kEventName, *device_info);
// Notify all observers.
for (auto& observer : observer_list_)
observer.OnDeviceAdded(*device_info);
}
void UsbDeviceManager::OnDeviceRemoved(
device::mojom::UsbDeviceInfoPtr device_info) {
DCHECK(device_info);
DispatchEvent(usb::OnDeviceRemoved::kEventName, *device_info);
// Notify all observers for OnDeviceRemoved event.
for (auto& observer : observer_list_)
observer.OnDeviceRemoved(*device_info);
auto iter = guid_to_id_map_.find(device_info->guid);
if (iter != guid_to_id_map_.end()) {
int id = iter->second;
guid_to_id_map_.erase(iter);
id_to_guid_map_.erase(id);
}
// Notify all observers for OnDeviceRemovedCleanup event.
for (auto& observer : observer_list_)
observer.OnDeviceRemovedCleanup(*device_info);
}
void UsbDeviceManager::EnsureConnectionWithDeviceManager() {
if (device_manager_)
return;
// Request UsbDeviceManagerPtr from DeviceService.
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(device::mojom::kServiceName,
mojo::MakeRequest(&device_manager_));
SetUpDeviceManagerConnection();
}
void UsbDeviceManager::SetUpDeviceManagerConnection() {
DCHECK(device_manager_);
device_manager_.set_connection_error_handler(
base::BindOnce(&UsbDeviceManager::OnDeviceManagerConnectionError,
base::Unretained(this)));
// Listen for added/removed device events.
DCHECK(!client_binding_);
device::mojom::UsbDeviceManagerClientAssociatedPtrInfo client;
client_binding_.Bind(mojo::MakeRequest(&client));
device_manager_->SetClient(std::move(client));
}
void UsbDeviceManager::OnDeviceManagerConnectionError() {
device_manager_.reset();
client_binding_.Close();
guid_to_id_map_.clear();
id_to_guid_map_.clear();
// Notify all observers.
for (auto& observer : observer_list_)
observer.OnDeviceManagerConnectionError();
}
void UsbDeviceManager::DispatchEvent(
const std::string& event_name,
const device::mojom::UsbDeviceInfo& device_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
EventRouter* event_router = EventRouter::Get(browser_context_);
if (event_router) {
usb::Device device_obj;
GetApiDevice(device_info, &device_obj);
std::unique_ptr<Event> event;
if (event_name == usb::OnDeviceAdded::kEventName) {
event.reset(new Event(events::USB_ON_DEVICE_ADDED,
usb::OnDeviceAdded::kEventName,
usb::OnDeviceAdded::Create(device_obj)));
} else {
DCHECK(event_name == usb::OnDeviceRemoved::kEventName);
event.reset(new Event(events::USB_ON_DEVICE_REMOVED,
usb::OnDeviceRemoved::kEventName,
usb::OnDeviceRemoved::Create(device_obj)));
}
event->will_dispatch_callback = base::BindRepeating(
&WillDispatchDeviceEvent, base::ConstRef(device_info));
event_router->BroadcastEvent(std::move(event));
}
}
template <>
void BrowserContextKeyedAPIFactory<
UsbDeviceManager>::DeclareFactoryDependencies() {
DependsOn(DevicePermissionsManagerFactory::GetInstance());
DependsOn(EventRouterFactory::GetInstance());
}
} // namespace extensions