diff options
author | reillyg <reillyg@chromium.org> | 2015-01-07 11:48:19 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-01-07 19:49:05 +0000 |
commit | 0e8d9ed0901ba785f73fbf25ebe1dfcc4e0ef65e (patch) | |
tree | 68146352e7c26207ddd50c701025101539a9de98 /extensions | |
parent | 0a5cfb2615967de06e3addbe27fc491f68a440bd (diff) | |
download | chromium_src-0e8d9ed0901ba785f73fbf25ebe1dfcc4e0ef65e.zip chromium_src-0e8d9ed0901ba785f73fbf25ebe1dfcc4e0ef65e.tar.gz chromium_src-0e8d9ed0901ba785f73fbf25ebe1dfcc4e0ef65e.tar.bz2 |
Add onDeviceAdded and onDeviceRemoved events to chrome.usb.
This changes creates a UsbAPI browser context-keyed service object that
waits for an app to register for device connection events and then
configures an observer on the UsbService.
BUG=411715
Review URL: https://codereview.chromium.org/839443002
Cr-Commit-Position: refs/heads/master@{#310343}
Diffstat (limited to 'extensions')
-rw-r--r-- | extensions/browser/BUILD.gn | 2 | ||||
-rw-r--r-- | extensions/browser/api/device_permissions_manager.cc | 15 | ||||
-rw-r--r-- | extensions/browser/api/device_permissions_manager.h | 9 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_api.cc | 7 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_event_router.cc | 162 | ||||
-rw-r--r-- | extensions/browser/api/usb/usb_event_router.h | 60 | ||||
-rw-r--r-- | extensions/browser/browser_context_keyed_service_factories.cc | 2 | ||||
-rw-r--r-- | extensions/common/api/_api_features.json | 10 | ||||
-rw-r--r-- | extensions/common/api/hid.idl | 8 | ||||
-rw-r--r-- | extensions/common/api/usb.idl | 13 | ||||
-rw-r--r-- | extensions/extensions.gyp | 2 |
11 files changed, 274 insertions, 16 deletions
diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index 884059e..1b717da 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -241,6 +241,8 @@ source_set("browser") { "api/test/test_api.h", "api/usb/usb_api.cc", "api/usb/usb_api.h", + "api/usb/usb_event_router.cc", + "api/usb/usb_event_router.h", "api/usb/usb_device_resource.cc", "api/usb/usb_device_resource.h", "api/virtual_keyboard_private/virtual_keyboard_private_api.cc", diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc index 0050320..3b467eb 100644 --- a/extensions/browser/api/device_permissions_manager.cc +++ b/extensions/browser/api/device_permissions_manager.cc @@ -337,14 +337,17 @@ DevicePermissions::~DevicePermissions() { } scoped_refptr<DevicePermissionEntry> DevicePermissions::FindEntry( - scoped_refptr<device::UsbDevice> device) const { + scoped_refptr<device::UsbDevice> device, + const base::string16& serial_number) const { const auto& ephemeral_device_entry = ephemeral_devices_.find(device); if (ephemeral_device_entry != ephemeral_devices_.end()) { return ephemeral_device_entry->second; } - bool have_serial_number = false; - base::string16 serial_number; + if (serial_number.empty()) { + return nullptr; + } + for (const auto& entry : entries_) { if (!entry->IsPersistent()) { continue; @@ -355,12 +358,6 @@ scoped_refptr<DevicePermissionEntry> DevicePermissions::FindEntry( if (entry->product_id() != device->product_id()) { continue; } - if (!have_serial_number) { - if (!device->GetSerialNumber(&serial_number)) { - break; - } - have_serial_number = true; - } if (entry->serial_number() != serial_number) { continue; } diff --git a/extensions/browser/api/device_permissions_manager.h b/extensions/browser/api/device_permissions_manager.h index 52a1bd9..d76426c 100644 --- a/extensions/browser/api/device_permissions_manager.h +++ b/extensions/browser/api/device_permissions_manager.h @@ -102,10 +102,13 @@ class DevicePermissions { public: virtual ~DevicePermissions(); - // Attempts to find a permission entry matching the given device. This - // function must be called from the FILE thread. crbug.com/427985 + // Attempts to find a permission entry matching the given device. The device + // serial number is presented separately so that this function does not need + // to call device->GetSerialNumber() which may not be possible on the + // current thread. scoped_refptr<DevicePermissionEntry> FindEntry( - scoped_refptr<device::UsbDevice> device) const; + scoped_refptr<device::UsbDevice> device, + const base::string16& serial_number) const; const std::set<scoped_refptr<DevicePermissionEntry>>& entries() const { return entries_; diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc index 329327d..6286c05 100644 --- a/extensions/browser/api/usb/usb_api.cc +++ b/extensions/browser/api/usb/usb_api.cc @@ -466,8 +466,11 @@ bool UsbAsyncApiFunction::HasDevicePermission(scoped_refptr<UsbDevice> device) { DCHECK(device_permissions_); // Check the DevicePermissionsManager first so that if an entry is found - // it can be stored for later. - permission_entry_ = device_permissions_->FindEntry(device); + // it can be stored for later. This requires the serial number. + base::string16 serial_number; + device->GetSerialNumber(&serial_number); + + permission_entry_ = device_permissions_->FindEntry(device, serial_number); if (permission_entry_.get()) { return true; } diff --git a/extensions/browser/api/usb/usb_event_router.cc b/extensions/browser/api/usb/usb_event_router.cc new file mode 100644 index 0000000..93fff3f --- /dev/null +++ b/extensions/browser/api/usb/usb_event_router.cc @@ -0,0 +1,162 @@ +// Copyright 2015 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_event_router.h" + +#include "device/core/device_client.h" +#include "device/usb/usb_device.h" +#include "device/usb/usb_service.h" +#include "extensions/browser/api/device_permissions_manager.h" +#include "extensions/common/api/usb.h" +#include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/permissions/usb_device_permission.h" + +namespace usb = extensions::core_api::usb; + +using content::BrowserThread; +using device::UsbDevice; +using device::UsbService; + +namespace extensions { + +namespace { + +// Returns true iff the given extension has permission to receive events +// regarding this device. +bool WillDispatchDeviceEvent(scoped_refptr<UsbDevice> device, + const base::string16& serial_number, + content::BrowserContext* browser_context, + const Extension* extension, + base::ListValue* event_args) { + // Check install-time and optional permissions. + UsbDevicePermission::CheckParam param( + device->vendor_id(), device->product_id(), + UsbDevicePermissionData::UNSPECIFIED_INTERFACE); + if (extension->permissions_data()->CheckAPIPermissionWithParam( + APIPermission::kUsbDevice, ¶m)) { + return true; + } + + // Check permissions granted through chrome.usb.getUserSelectedDevices. + scoped_ptr<DevicePermissions> device_permissions = + DevicePermissionsManager::Get(browser_context) + ->GetForExtension(extension->id()); + if (device_permissions->FindEntry(device, serial_number).get()) { + return true; + } + + return false; +} + +base::LazyInstance<BrowserContextKeyedAPIFactory<UsbEventRouter>>::Leaky + g_event_router_factory = LAZY_INSTANCE_INITIALIZER; + +} // namespace + +class UsbEventRouter::FileThreadHelper : public UsbService::Observer { + public: + FileThreadHelper(base::WeakPtr<UsbEventRouter> usb_event_router) + : usb_event_router_(usb_event_router), observer_(this) {} + virtual ~FileThreadHelper() {} + + void Start() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + UsbService* service = device::DeviceClient::Get()->GetUsbService(); + if (service) { + observer_.Add(service); + } + } + + private: + // UsbService::Observer implementation. + void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + + base::string16 serial_number; + device->GetSerialNumber(&serial_number); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&UsbEventRouter::DispatchEvent, usb_event_router_, + usb::OnDeviceAdded::kEventName, device, serial_number)); + } + + void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + + base::string16 serial_number; + device->GetSerialNumber(&serial_number); + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + base::Bind(&UsbEventRouter::DispatchEvent, usb_event_router_, + usb::OnDeviceRemoved::kEventName, device, serial_number)); + } + + base::WeakPtr<UsbEventRouter> usb_event_router_; + ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; +}; + +// static +BrowserContextKeyedAPIFactory<UsbEventRouter>* +UsbEventRouter::GetFactoryInstance() { + return g_event_router_factory.Pointer(); +} + +UsbEventRouter::UsbEventRouter(content::BrowserContext* browser_context) + : browser_context_(browser_context), 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); + } +} + +UsbEventRouter::~UsbEventRouter() { +} + +void UsbEventRouter::Shutdown() { + EventRouter* event_router = EventRouter::Get(browser_context_); + if (event_router) { + event_router->UnregisterObserver(this); + } + helper_.reset(nullptr); +} + +void UsbEventRouter::OnListenerAdded(const EventListenerInfo& details) { + if (!helper_) { + helper_.reset(new FileThreadHelper(weak_factory_.GetWeakPtr())); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + base::Bind(&FileThreadHelper::Start, base::Unretained(helper_.get()))); + } +} + +void UsbEventRouter::DispatchEvent(const std::string& event_name, + scoped_refptr<UsbDevice> device, + const base::string16& serial_number) { + EventRouter* event_router = EventRouter::Get(browser_context_); + if (event_router) { + usb::Device device_obj; + device_obj.device = device->unique_id(); + device_obj.vendor_id = device->vendor_id(); + device_obj.product_id = device->product_id(); + + scoped_ptr<Event> event; + if (event_name == usb::OnDeviceAdded::kEventName) { + event.reset(new Event(usb::OnDeviceAdded::kEventName, + usb::OnDeviceAdded::Create(device_obj))); + } else { + DCHECK(event_name == usb::OnDeviceRemoved::kEventName); + event.reset(new Event(usb::OnDeviceRemoved::kEventName, + usb::OnDeviceRemoved::Create(device_obj))); + } + + event->will_dispatch_callback = + base::Bind(&WillDispatchDeviceEvent, device, serial_number); + event_router->BroadcastEvent(event.Pass()); + } +} + +} // extensions diff --git a/extensions/browser/api/usb/usb_event_router.h b/extensions/browser/api/usb/usb_event_router.h new file mode 100644 index 0000000..ba8c5c8 --- /dev/null +++ b/extensions/browser/api/usb/usb_event_router.h @@ -0,0 +1,60 @@ +// Copyright 2015 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. + +#ifndef EXTENSIONS_BROWSER_API_USB_USB_EVENT_ROUTER_H_ +#define EXTENSIONS_BROWSER_API_USB_USB_EVENT_ROUTER_H_ + +#include "content/public/browser/browser_thread.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" +#include "extensions/browser/event_router.h" + +namespace device { +class UsbDevice; +} + +namespace extensions { + +// A BrowserContext-scoped object which is registered as an observer of the +// EventRouter and UsbService in order to generate device add/remove events. +class UsbEventRouter : public BrowserContextKeyedAPI, + public EventRouter::Observer { + public: + // BrowserContextKeyedAPI implementation. + static BrowserContextKeyedAPIFactory<UsbEventRouter>* GetFactoryInstance(); + + private: + friend class BrowserContextKeyedAPIFactory<UsbEventRouter>; + + class FileThreadHelper; + + explicit UsbEventRouter(content::BrowserContext* context); + ~UsbEventRouter() override; + + // BrowserContextKeyedAPI implementation. + static const char* service_name() { return "UsbEventRouter"; } + static const bool kServiceIsNULLWhileTesting = true; + + // KeyedService implementation. + void Shutdown() override; + + // EventRouter::Observer implementation. + void OnListenerAdded(const EventListenerInfo& details) override; + + // Broadcasts a device add or remove event for the given device. + void DispatchEvent(const std::string& event_name, + scoped_refptr<device::UsbDevice> device, + const base::string16& serial_number); + + content::BrowserContext* const browser_context_; + base::WeakPtrFactory<UsbEventRouter> weak_factory_; + + scoped_ptr<FileThreadHelper, content::BrowserThread::DeleteOnFileThread> + helper_; + + DISALLOW_COPY_AND_ASSIGN(UsbEventRouter); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_USB_USB_EVENT_ROUTER_H_ diff --git a/extensions/browser/browser_context_keyed_service_factories.cc b/extensions/browser/browser_context_keyed_service_factories.cc index 11774be..1f916fb 100644 --- a/extensions/browser/browser_context_keyed_service_factories.cc +++ b/extensions/browser/browser_context_keyed_service_factories.cc @@ -18,6 +18,7 @@ #include "extensions/browser/api/sockets_udp/udp_socket_event_dispatcher.h" #include "extensions/browser/api/storage/storage_frontend.h" #include "extensions/browser/api/system_info/system_info_api.h" +#include "extensions/browser/api/usb/usb_event_router.h" #include "extensions/browser/api/vpn_provider/vpn_service_factory.h" #include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/process_manager_factory.h" @@ -46,6 +47,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { RuntimeAPI::GetFactoryInstance(); StorageFrontend::GetFactoryInstance(); SystemInfoAPI::GetFactoryInstance(); + UsbEventRouter::GetFactoryInstance(); } } // namespace extensions diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index db04729..c304bdb 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -330,6 +330,16 @@ "dependencies": ["permission:usb"], "contexts": ["blessed_extension"] }, + "usb.onDeviceAdded": { + "channel": "dev", + "dependencies": ["permission:usb"], + "contexts": ["blessed_extension"] + }, + "usb.onDeviceRemoved": { + "channel": "dev", + "dependencies": ["permission:usb"], + "contexts": ["blessed_extension"] + }, "vpnProvider": { "dependencies": ["permission:vpnProvider"], "contexts": ["blessed_extension"] diff --git a/extensions/common/api/hid.idl b/extensions/common/api/hid.idl index 2095c1a..3188d59 100644 --- a/extensions/common/api/hid.idl +++ b/extensions/common/api/hid.idl @@ -131,10 +131,14 @@ namespace hid { }; interface Events { - // Event generated when a device is added to the system. + // Event generated when a device is added to the system. Events are only + // broadcast to apps and extensions that have permission to access the + // device. Permission may have been granted at install time or when the user + // accepted an optional permission (see $(ref:permissions.request)). static void onDeviceAdded(HidDeviceInfo device); - // Event generated when a device is removed from the system. + // Event generated when a device is removed from the system. See + // $(ref:onDeviceAdded) for which events are delivered. // |deviceId|: The <code>deviceId</code> property of the device passed to // $(ref:onDeviceAdded). static void onDeviceRemoved(long deviceId); diff --git a/extensions/common/api/usb.idl b/extensions/common/api/usb.idl index bae27e2..9be109f 100644 --- a/extensions/common/api/usb.idl +++ b/extensions/common/api/usb.idl @@ -350,4 +350,17 @@ namespace usb { static void resetDevice(ConnectionHandle handle, ResetDeviceCallback callback); }; + + interface Events { + // Event generated when a device is added to the system. Events are only + // broadcast to apps and extensions that have permission to access the + // device. Permission may have been granted at install time, when the user + // accepted an optional permission (see $(ref:permissions.request)), or + // through $(ref:getUserSelectedDevices). + static void onDeviceAdded(Device device); + + // Event generated when a device is removed from the system. See + // $(ref:onDeviceAdded) for which events are delivered. + static void onDeviceRemoved(Device device); + }; }; diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 5e4c91d..e7e2734 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -531,6 +531,8 @@ 'browser/api/test/test_api.h', 'browser/api/usb/usb_api.cc', 'browser/api/usb/usb_api.h', + 'browser/api/usb/usb_event_router.cc', + 'browser/api/usb/usb_event_router.h', 'browser/api/usb/usb_device_resource.cc', 'browser/api/usb/usb_device_resource.h', 'browser/api/virtual_keyboard_private/virtual_keyboard_private_api.cc', |