summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2015-01-07 11:48:19 -0800
committerCommit bot <commit-bot@chromium.org>2015-01-07 19:49:05 +0000
commit0e8d9ed0901ba785f73fbf25ebe1dfcc4e0ef65e (patch)
tree68146352e7c26207ddd50c701025101539a9de98 /extensions
parent0a5cfb2615967de06e3addbe27fc491f68a440bd (diff)
downloadchromium_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.gn2
-rw-r--r--extensions/browser/api/device_permissions_manager.cc15
-rw-r--r--extensions/browser/api/device_permissions_manager.h9
-rw-r--r--extensions/browser/api/usb/usb_api.cc7
-rw-r--r--extensions/browser/api/usb/usb_event_router.cc162
-rw-r--r--extensions/browser/api/usb/usb_event_router.h60
-rw-r--r--extensions/browser/browser_context_keyed_service_factories.cc2
-rw-r--r--extensions/common/api/_api_features.json10
-rw-r--r--extensions/common/api/hid.idl8
-rw-r--r--extensions/common/api/usb.idl13
-rw-r--r--extensions/extensions.gyp2
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, &param)) {
+ 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',