summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 18:40:11 +0000
committerrockot@chromium.org <rockot@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-05 18:40:11 +0000
commit6e84e2c187647c30f3f4563aac671ba9416b4182 (patch)
tree7443a12f1dbaca77ca6ab5a51b433fb91d3854ff /extensions
parent304219bc9ecaba2bc4055bae084dfbd85319cca5 (diff)
downloadchromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.zip
chromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.tar.gz
chromium_src-6e84e2c187647c30f3f4563aac671ba9416b4182.tar.bz2
Move chrome.usb to //extensions
This moves the chrome.usb API implementation into the core set of extensions APIs. BUG=369391 R=rpaquay TBR=yoz for extensions TBR=sky for +chrome/browser/ui in test DEPS Review URL: https://codereview.chromium.org/268713013 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@268231 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions')
-rw-r--r--extensions/DEPS3
-rw-r--r--extensions/browser/api/usb/DEPS3
-rw-r--r--extensions/browser/api/usb/OWNERS4
-rw-r--r--extensions/browser/api/usb/usb_api.cc1208
-rw-r--r--extensions/browser/api/usb/usb_api.h317
-rw-r--r--extensions/browser/api/usb/usb_apitest.cc275
-rw-r--r--extensions/browser/api/usb/usb_device_resource.cc45
-rw-r--r--extensions/browser/api/usb/usb_device_resource.h50
-rw-r--r--extensions/browser/api/usb/usb_manual_apitest.cc18
-rw-r--r--extensions/common/api/_api_features.json4
-rw-r--r--extensions/common/api/_permission_features.json30
-rw-r--r--extensions/common/api/api.gyp1
-rw-r--r--extensions/common/api/usb.idl307
-rw-r--r--extensions/extensions.gyp8
14 files changed, 2272 insertions, 1 deletions
diff --git a/extensions/DEPS b/extensions/DEPS
index bb58d4f..11611da 100644
--- a/extensions/DEPS
+++ b/extensions/DEPS
@@ -26,9 +26,12 @@ specific_include_rules = {
# Temporarily allowed testing includes. See above.
# TODO(jamescook): Remove these. http://crbug.com/162530
+ "+chrome/browser/extensions/api/permissions/permissions_api.h",
"+chrome/browser/extensions/extension_api_unittest.h",
+ "+chrome/browser/extensions/extension_apitest.h",
"+chrome/browser/extensions/extension_service_unittest.h",
"+chrome/browser/extensions/test_extension_system.h",
+ "+chrome/browser/ui/browser.h",
"+chrome/common/chrome_paths.h",
"+chrome/common/extensions/features/feature_channel.h",
"+chrome/common/extensions/manifest_tests/extension_manifest_test.h",
diff --git a/extensions/browser/api/usb/DEPS b/extensions/browser/api/usb/DEPS
new file mode 100644
index 0000000..70ac41d
--- /dev/null
+++ b/extensions/browser/api/usb/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+components/usb_service",
+]
diff --git a/extensions/browser/api/usb/OWNERS b/extensions/browser/api/usb/OWNERS
new file mode 100644
index 0000000..38fa250
--- /dev/null
+++ b/extensions/browser/api/usb/OWNERS
@@ -0,0 +1,4 @@
+# USB API members. For core API change, check with owners in
+# chrome/browser/extensions/OWNERS
+rockot@chromium.org
+rpaquay@chromium.org
diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc
new file mode 100644
index 0000000..93ac6c1
--- /dev/null
+++ b/extensions/browser/api/usb/usb_api.cc
@@ -0,0 +1,1208 @@
+// Copyright 2014 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_api.h"
+
+#include <string>
+#include <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop_proxy.h"
+#include "components/usb_service/usb_device_handle.h"
+#include "components/usb_service/usb_service.h"
+#include "extensions/browser/api/usb/usb_device_resource.h"
+#include "extensions/browser/extension_system.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;
+namespace BulkTransfer = usb::BulkTransfer;
+namespace ClaimInterface = usb::ClaimInterface;
+namespace CloseDevice = usb::CloseDevice;
+namespace ControlTransfer = usb::ControlTransfer;
+namespace FindDevices = usb::FindDevices;
+namespace GetDevices = usb::GetDevices;
+namespace InterruptTransfer = usb::InterruptTransfer;
+namespace IsochronousTransfer = usb::IsochronousTransfer;
+namespace ListInterfaces = usb::ListInterfaces;
+namespace OpenDevice = usb::OpenDevice;
+namespace ReleaseInterface = usb::ReleaseInterface;
+namespace RequestAccess = usb::RequestAccess;
+namespace ResetDevice = usb::ResetDevice;
+namespace SetInterfaceAlternateSetting = usb::SetInterfaceAlternateSetting;
+
+using content::BrowserThread;
+using std::string;
+using std::vector;
+using usb::ControlTransferInfo;
+using usb::ConnectionHandle;
+using usb::Device;
+using usb::Direction;
+using usb::EndpointDescriptor;
+using usb::GenericTransferInfo;
+using usb::InterfaceDescriptor;
+using usb::IsochronousTransferInfo;
+using usb::Recipient;
+using usb::RequestType;
+using usb::SynchronizationType;
+using usb::TransferType;
+using usb::UsageType;
+using usb_service::UsbConfigDescriptor;
+using usb_service::UsbDevice;
+using usb_service::UsbDeviceHandle;
+using usb_service::UsbEndpointDescriptor;
+using usb_service::UsbEndpointDirection;
+using usb_service::UsbInterfaceAltSettingDescriptor;
+using usb_service::UsbInterfaceDescriptor;
+using usb_service::UsbService;
+using usb_service::UsbSynchronizationType;
+using usb_service::UsbTransferStatus;
+using usb_service::UsbTransferType;
+using usb_service::UsbUsageType;
+
+typedef std::vector<scoped_refptr<UsbDevice> > DeviceVector;
+typedef scoped_ptr<DeviceVector> ScopedDeviceVector;
+
+namespace {
+
+const char kDataKey[] = "data";
+const char kResultCodeKey[] = "resultCode";
+
+const char kErrorInitService[] = "Failed to initialize USB service.";
+
+const char kErrorOpen[] = "Failed to open device.";
+const char kErrorCancelled[] = "Transfer was cancelled.";
+const char kErrorDisconnect[] = "Device disconnected.";
+const char kErrorGeneric[] = "Transfer failed.";
+#if !defined(OS_CHROMEOS)
+const char kErrorNotSupported[] = "Not supported on this platform.";
+#endif
+const char kErrorOverflow[] = "Inbound transfer overflow.";
+const char kErrorStalled[] = "Transfer stalled.";
+const char kErrorTimeout[] = "Transfer timed out.";
+const char kErrorTransferLength[] = "Transfer length is insufficient.";
+
+const char kErrorCannotListInterfaces[] = "Error listing interfaces.";
+const char kErrorCannotClaimInterface[] = "Error claiming interface.";
+const char kErrorCannotReleaseInterface[] = "Error releasing interface.";
+const char kErrorCannotSetInterfaceAlternateSetting[] =
+ "Error setting alternate interface setting.";
+const char kErrorConvertDirection[] = "Invalid transfer direction.";
+const char kErrorConvertRecipient[] = "Invalid transfer recipient.";
+const char kErrorConvertRequestType[] = "Invalid request type.";
+const char kErrorConvertSynchronizationType[] = "Invalid synchronization type";
+const char kErrorConvertTransferType[] = "Invalid endpoint type.";
+const char kErrorConvertUsageType[] = "Invalid usage type.";
+const char kErrorMalformedParameters[] = "Error parsing parameters.";
+const char kErrorNoDevice[] = "No such device.";
+const char kErrorPermissionDenied[] = "Permission to access device was denied";
+const char kErrorInvalidTransferLength[] =
+ "Transfer length must be a positive number less than 104,857,600.";
+const char kErrorInvalidNumberOfPackets[] =
+ "Number of packets must be a positive number less than 4,194,304.";
+const char kErrorInvalidPacketLength[] =
+ "Packet length must be a positive number less than 65,536.";
+const char kErrorResetDevice[] =
+ "Error resetting the device. The device has been closed.";
+
+const size_t kMaxTransferLength = 100 * 1024 * 1024;
+const int kMaxPackets = 4 * 1024 * 1024;
+const int kMaxPacketLength = 64 * 1024;
+
+bool ConvertDirectionToApi(const UsbEndpointDirection& input,
+ Direction* output) {
+ switch (input) {
+ case usb_service::USB_DIRECTION_INBOUND:
+ *output = usb::DIRECTION_IN;
+ return true;
+ case usb_service::USB_DIRECTION_OUTBOUND:
+ *output = usb::DIRECTION_OUT;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertSynchronizationTypeToApi(const UsbSynchronizationType& input,
+ usb::SynchronizationType* output) {
+ switch (input) {
+ case usb_service::USB_SYNCHRONIZATION_NONE:
+ *output = usb::SYNCHRONIZATION_TYPE_NONE;
+ return true;
+ case usb_service::USB_SYNCHRONIZATION_ASYNCHRONOUS:
+ *output = usb::SYNCHRONIZATION_TYPE_ASYNCHRONOUS;
+ return true;
+ case usb_service::USB_SYNCHRONIZATION_ADAPTIVE:
+ *output = usb::SYNCHRONIZATION_TYPE_ADAPTIVE;
+ return true;
+ case usb_service::USB_SYNCHRONIZATION_SYNCHRONOUS:
+ *output = usb::SYNCHRONIZATION_TYPE_SYNCHRONOUS;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertTransferTypeToApi(const UsbTransferType& input,
+ usb::TransferType* output) {
+ switch (input) {
+ case usb_service::USB_TRANSFER_CONTROL:
+ *output = usb::TRANSFER_TYPE_CONTROL;
+ return true;
+ case usb_service::USB_TRANSFER_INTERRUPT:
+ *output = usb::TRANSFER_TYPE_INTERRUPT;
+ return true;
+ case usb_service::USB_TRANSFER_ISOCHRONOUS:
+ *output = usb::TRANSFER_TYPE_ISOCHRONOUS;
+ return true;
+ case usb_service::USB_TRANSFER_BULK:
+ *output = usb::TRANSFER_TYPE_BULK;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertUsageTypeToApi(const UsbUsageType& input, usb::UsageType* output) {
+ switch (input) {
+ case usb_service::USB_USAGE_DATA:
+ *output = usb::USAGE_TYPE_DATA;
+ return true;
+ case usb_service::USB_USAGE_FEEDBACK:
+ *output = usb::USAGE_TYPE_FEEDBACK;
+ return true;
+ case usb_service::USB_USAGE_EXPLICIT_FEEDBACK:
+ *output = usb::USAGE_TYPE_EXPLICITFEEDBACK;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertDirection(const Direction& input, UsbEndpointDirection* output) {
+ switch (input) {
+ case usb::DIRECTION_IN:
+ *output = usb_service::USB_DIRECTION_INBOUND;
+ return true;
+ case usb::DIRECTION_OUT:
+ *output = usb_service::USB_DIRECTION_OUTBOUND;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertRequestType(const RequestType& input,
+ UsbDeviceHandle::TransferRequestType* output) {
+ switch (input) {
+ case usb::REQUEST_TYPE_STANDARD:
+ *output = UsbDeviceHandle::STANDARD;
+ return true;
+ case usb::REQUEST_TYPE_CLASS:
+ *output = UsbDeviceHandle::CLASS;
+ return true;
+ case usb::REQUEST_TYPE_VENDOR:
+ *output = UsbDeviceHandle::VENDOR;
+ return true;
+ case usb::REQUEST_TYPE_RESERVED:
+ *output = UsbDeviceHandle::RESERVED;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+bool ConvertRecipient(const Recipient& input,
+ UsbDeviceHandle::TransferRecipient* output) {
+ switch (input) {
+ case usb::RECIPIENT_DEVICE:
+ *output = UsbDeviceHandle::DEVICE;
+ return true;
+ case usb::RECIPIENT_INTERFACE:
+ *output = UsbDeviceHandle::INTERFACE;
+ return true;
+ case usb::RECIPIENT_ENDPOINT:
+ *output = UsbDeviceHandle::ENDPOINT;
+ return true;
+ case usb::RECIPIENT_OTHER:
+ *output = UsbDeviceHandle::OTHER;
+ return true;
+ default:
+ NOTREACHED();
+ return false;
+ }
+}
+
+template <class T>
+bool GetTransferSize(const T& input, size_t* output) {
+ if (input.direction == usb::DIRECTION_IN) {
+ const int* length = input.length.get();
+ if (length && *length >= 0 &&
+ static_cast<size_t>(*length) < kMaxTransferLength) {
+ *output = *length;
+ return true;
+ }
+ } else if (input.direction == usb::DIRECTION_OUT) {
+ if (input.data.get()) {
+ *output = input.data->size();
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class T>
+scoped_refptr<net::IOBuffer> CreateBufferForTransfer(
+ const T& input,
+ UsbEndpointDirection direction,
+ size_t size) {
+ if (size >= kMaxTransferLength)
+ return NULL;
+
+ // Allocate a |size|-bytes buffer, or a one-byte buffer if |size| is 0. This
+ // is due to an impedance mismatch between IOBuffer and URBs. An IOBuffer
+ // cannot represent a zero-length buffer, while an URB can.
+ scoped_refptr<net::IOBuffer> buffer =
+ new net::IOBuffer(std::max(static_cast<size_t>(1), size));
+
+ if (direction == usb_service::USB_DIRECTION_INBOUND) {
+ return buffer;
+ } else if (direction == usb_service::USB_DIRECTION_OUTBOUND) {
+ if (input.data.get() && size <= input.data->size()) {
+ memcpy(buffer->data(), input.data->data(), size);
+ return buffer;
+ }
+ }
+ NOTREACHED();
+ return NULL;
+}
+
+const char* ConvertTransferStatusToErrorString(const UsbTransferStatus status) {
+ switch (status) {
+ case usb_service::USB_TRANSFER_COMPLETED:
+ return "";
+ case usb_service::USB_TRANSFER_ERROR:
+ return kErrorGeneric;
+ case usb_service::USB_TRANSFER_TIMEOUT:
+ return kErrorTimeout;
+ case usb_service::USB_TRANSFER_CANCELLED:
+ return kErrorCancelled;
+ case usb_service::USB_TRANSFER_STALLED:
+ return kErrorStalled;
+ case usb_service::USB_TRANSFER_DISCONNECT:
+ return kErrorDisconnect;
+ case usb_service::USB_TRANSFER_OVERFLOW:
+ return kErrorOverflow;
+ case usb_service::USB_TRANSFER_LENGTH_SHORT:
+ return kErrorTransferLength;
+ default:
+ NOTREACHED();
+ return "";
+ }
+}
+
+#if defined(OS_CHROMEOS)
+void RequestUsbDevicesAccessHelper(
+ ScopedDeviceVector devices,
+ std::vector<scoped_refptr<UsbDevice> >::iterator i,
+ int interface_id,
+ const base::Callback<void(ScopedDeviceVector result)>& callback,
+ bool success) {
+ if (success) {
+ ++i;
+ } else {
+ i = devices->erase(i);
+ }
+ if (i == devices->end()) {
+ callback.Run(devices.Pass());
+ return;
+ }
+ (*i)->RequestUsbAcess(interface_id,
+ base::Bind(RequestUsbDevicesAccessHelper,
+ base::Passed(devices.Pass()),
+ i,
+ interface_id,
+ callback));
+}
+
+void RequestUsbDevicesAccess(
+ ScopedDeviceVector devices,
+ int interface_id,
+ const base::Callback<void(ScopedDeviceVector result)>& callback) {
+ if (devices->empty()) {
+ callback.Run(devices.Pass());
+ return;
+ }
+ std::vector<scoped_refptr<UsbDevice> >::iterator i = devices->begin();
+ (*i)->RequestUsbAcess(interface_id,
+ base::Bind(RequestUsbDevicesAccessHelper,
+ base::Passed(devices.Pass()),
+ i,
+ interface_id,
+ callback));
+}
+#endif // OS_CHROMEOS
+
+base::DictionaryValue* CreateTransferInfo(UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> data,
+ size_t length) {
+ base::DictionaryValue* result = new base::DictionaryValue();
+ result->SetInteger(kResultCodeKey, status);
+ result->Set(kDataKey,
+ base::BinaryValue::CreateWithCopiedBuffer(data->data(), length));
+ return result;
+}
+
+base::Value* PopulateConnectionHandle(int handle,
+ int vendor_id,
+ int product_id) {
+ ConnectionHandle result;
+ result.handle = handle;
+ result.vendor_id = vendor_id;
+ result.product_id = product_id;
+ return result.ToValue().release();
+}
+
+base::Value* PopulateDevice(UsbDevice* device) {
+ Device result;
+ result.device = device->unique_id();
+ result.vendor_id = device->vendor_id();
+ result.product_id = device->product_id();
+ return result.ToValue().release();
+}
+
+base::Value* PopulateInterfaceDescriptor(
+ int interface_number,
+ int alternate_setting,
+ int interface_class,
+ int interface_subclass,
+ int interface_protocol,
+ std::vector<linked_ptr<EndpointDescriptor> >* endpoints) {
+ InterfaceDescriptor descriptor;
+ descriptor.interface_number = interface_number;
+ descriptor.alternate_setting = alternate_setting;
+ descriptor.interface_class = interface_class;
+ descriptor.interface_subclass = interface_subclass;
+ descriptor.interface_protocol = interface_protocol;
+ descriptor.endpoints = *endpoints;
+ return descriptor.ToValue().release();
+}
+
+} // namespace
+
+namespace extensions {
+
+UsbAsyncApiFunction::UsbAsyncApiFunction() : manager_(NULL) {
+}
+
+UsbAsyncApiFunction::~UsbAsyncApiFunction() {
+}
+
+bool UsbAsyncApiFunction::PrePrepare() {
+ manager_ = ApiResourceManager<UsbDeviceResource>::Get(browser_context());
+ set_work_thread_id(BrowserThread::FILE);
+ return manager_ != NULL;
+}
+
+bool UsbAsyncApiFunction::Respond() {
+ return error_.empty();
+}
+
+scoped_refptr<UsbDevice> UsbAsyncApiFunction::GetDeviceOrOrCompleteWithError(
+ const Device& input_device) {
+ const uint16_t vendor_id = input_device.vendor_id;
+ const uint16_t product_id = input_device.product_id;
+ UsbDevicePermission::CheckParam param(
+ vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kUsbDevice, &param)) {
+ LOG(WARNING) << "Insufficient permissions to access device.";
+ CompleteWithError(kErrorPermissionDenied);
+ return NULL;
+ }
+
+ UsbService* service = UsbService::GetInstance();
+ if (!service) {
+ CompleteWithError(kErrorInitService);
+ return NULL;
+ }
+ scoped_refptr<UsbDevice> device;
+
+ device = service->GetDeviceById(input_device.device);
+
+ if (!device) {
+ CompleteWithError(kErrorNoDevice);
+ return NULL;
+ }
+
+ if (device->vendor_id() != input_device.vendor_id ||
+ device->product_id() != input_device.product_id) {
+ // Must act as if there is no such a device.
+ // Otherwise can be used to finger print unauthorized devices.
+ CompleteWithError(kErrorNoDevice);
+ return NULL;
+ }
+
+ return device;
+}
+
+scoped_refptr<UsbDeviceHandle>
+UsbAsyncApiFunction::GetDeviceHandleOrCompleteWithError(
+ const ConnectionHandle& input_device_handle) {
+ UsbDeviceResource* resource =
+ manager_->Get(extension_->id(), input_device_handle.handle);
+ if (!resource) {
+ CompleteWithError(kErrorNoDevice);
+ return NULL;
+ }
+
+ if (!resource->device() || !resource->device()->device()) {
+ CompleteWithError(kErrorDisconnect);
+ manager_->Remove(extension_->id(), input_device_handle.handle);
+ return NULL;
+ }
+
+ if (resource->device()->device()->vendor_id() !=
+ input_device_handle.vendor_id ||
+ resource->device()->device()->product_id() !=
+ input_device_handle.product_id) {
+ CompleteWithError(kErrorNoDevice);
+ return NULL;
+ }
+
+ return resource->device();
+}
+
+void UsbAsyncApiFunction::RemoveUsbDeviceResource(int api_resource_id) {
+ manager_->Remove(extension_->id(), api_resource_id);
+}
+
+void UsbAsyncApiFunction::CompleteWithError(const std::string& error) {
+ SetError(error);
+ AsyncWorkCompleted();
+}
+
+UsbAsyncApiTransferFunction::UsbAsyncApiTransferFunction() {
+}
+
+UsbAsyncApiTransferFunction::~UsbAsyncApiTransferFunction() {
+}
+
+void UsbAsyncApiTransferFunction::OnCompleted(UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> data,
+ size_t length) {
+ if (status != usb_service::USB_TRANSFER_COMPLETED)
+ SetError(ConvertTransferStatusToErrorString(status));
+
+ SetResult(CreateTransferInfo(status, data, length));
+ AsyncWorkCompleted();
+}
+
+bool UsbAsyncApiTransferFunction::ConvertDirectionSafely(
+ const Direction& input,
+ UsbEndpointDirection* output) {
+ const bool converted = ConvertDirection(input, output);
+ if (!converted)
+ SetError(kErrorConvertDirection);
+ return converted;
+}
+
+bool UsbAsyncApiTransferFunction::ConvertRequestTypeSafely(
+ const RequestType& input,
+ UsbDeviceHandle::TransferRequestType* output) {
+ const bool converted = ConvertRequestType(input, output);
+ if (!converted)
+ SetError(kErrorConvertRequestType);
+ return converted;
+}
+
+bool UsbAsyncApiTransferFunction::ConvertRecipientSafely(
+ const Recipient& input,
+ UsbDeviceHandle::TransferRecipient* output) {
+ const bool converted = ConvertRecipient(input, output);
+ if (!converted)
+ SetError(kErrorConvertRecipient);
+ return converted;
+}
+
+UsbFindDevicesFunction::UsbFindDevicesFunction() {
+}
+
+UsbFindDevicesFunction::~UsbFindDevicesFunction() {
+}
+
+bool UsbFindDevicesFunction::Prepare() {
+ parameters_ = FindDevices::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbFindDevicesFunction::AsyncWorkStart() {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+ const uint16_t vendor_id = parameters_->options.vendor_id;
+ const uint16_t product_id = parameters_->options.product_id;
+ int interface_id = parameters_->options.interface_id.get()
+ ? *parameters_->options.interface_id.get()
+ : UsbDevicePermissionData::ANY_INTERFACE;
+ UsbDevicePermission::CheckParam param(vendor_id, product_id, interface_id);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kUsbDevice, &param)) {
+ LOG(WARNING) << "Insufficient permissions to access device.";
+ CompleteWithError(kErrorPermissionDenied);
+ return;
+ }
+
+ UsbService* service = UsbService::GetInstance();
+ if (!service) {
+ CompleteWithError(kErrorInitService);
+ return;
+ }
+
+ ScopedDeviceVector devices(new DeviceVector());
+ service->GetDevices(devices.get());
+
+ for (DeviceVector::iterator it = devices->begin(); it != devices->end();) {
+ if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) {
+ it = devices->erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+#if defined(OS_CHROMEOS)
+ RequestUsbDevicesAccess(
+ devices.Pass(),
+ interface_id,
+ base::Bind(&UsbFindDevicesFunction::OpenDevices, this));
+#else
+ OpenDevices(devices.Pass());
+#endif // OS_CHROMEOS
+}
+
+void UsbFindDevicesFunction::OpenDevices(ScopedDeviceVector devices) {
+ base::ListValue* result = new base::ListValue();
+
+ for (size_t i = 0; i < devices->size(); ++i) {
+ scoped_refptr<UsbDeviceHandle> device_handle = devices->at(i)->Open();
+ if (device_handle)
+ device_handles_.push_back(device_handle);
+ }
+
+ for (size_t i = 0; i < device_handles_.size(); ++i) {
+ UsbDeviceHandle* const device_handle = device_handles_[i].get();
+ UsbDeviceResource* const resource =
+ new UsbDeviceResource(extension_->id(), device_handle);
+
+ result->Append(PopulateConnectionHandle(manager_->Add(resource),
+ parameters_->options.vendor_id,
+ parameters_->options.product_id));
+ }
+
+ SetResult(result);
+ AsyncWorkCompleted();
+}
+
+UsbGetDevicesFunction::UsbGetDevicesFunction() {
+}
+
+UsbGetDevicesFunction::~UsbGetDevicesFunction() {
+}
+
+bool UsbGetDevicesFunction::Prepare() {
+ parameters_ = GetDevices::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbGetDevicesFunction::AsyncWorkStart() {
+ scoped_ptr<base::ListValue> result(new base::ListValue());
+
+ const uint16_t vendor_id = parameters_->options.vendor_id;
+ const uint16_t product_id = parameters_->options.product_id;
+ UsbDevicePermission::CheckParam param(
+ vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
+ if (!PermissionsData::CheckAPIPermissionWithParam(
+ GetExtension(), APIPermission::kUsbDevice, &param)) {
+ LOG(WARNING) << "Insufficient permissions to access device.";
+ CompleteWithError(kErrorPermissionDenied);
+ return;
+ }
+
+ UsbService* service = UsbService::GetInstance();
+ if (!service) {
+ CompleteWithError(kErrorInitService);
+ return;
+ }
+
+ DeviceVector devices;
+ service->GetDevices(&devices);
+
+ for (DeviceVector::iterator it = devices.begin(); it != devices.end();) {
+ if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) {
+ it = devices.erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ for (size_t i = 0; i < devices.size(); ++i) {
+ result->Append(PopulateDevice(devices[i].get()));
+ }
+
+ SetResult(result.release());
+ AsyncWorkCompleted();
+}
+
+UsbRequestAccessFunction::UsbRequestAccessFunction() {
+}
+
+UsbRequestAccessFunction::~UsbRequestAccessFunction() {
+}
+
+bool UsbRequestAccessFunction::Prepare() {
+ parameters_ = RequestAccess::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbRequestAccessFunction::AsyncWorkStart() {
+#if defined(OS_CHROMEOS)
+ scoped_refptr<UsbDevice> device =
+ GetDeviceOrOrCompleteWithError(parameters_->device);
+ if (!device)
+ return;
+
+ device->RequestUsbAcess(
+ parameters_->interface_id,
+ base::Bind(&UsbRequestAccessFunction::OnCompleted, this));
+#else
+ SetResult(new base::FundamentalValue(false));
+ CompleteWithError(kErrorNotSupported);
+#endif // OS_CHROMEOS
+}
+
+void UsbRequestAccessFunction::OnCompleted(bool success) {
+ SetResult(new base::FundamentalValue(success));
+ AsyncWorkCompleted();
+}
+
+UsbOpenDeviceFunction::UsbOpenDeviceFunction() {
+}
+
+UsbOpenDeviceFunction::~UsbOpenDeviceFunction() {
+}
+
+bool UsbOpenDeviceFunction::Prepare() {
+ parameters_ = OpenDevice::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbOpenDeviceFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDevice> device =
+ GetDeviceOrOrCompleteWithError(parameters_->device);
+ if (!device)
+ return;
+
+ handle_ = device->Open();
+ if (!handle_) {
+ SetError(kErrorOpen);
+ AsyncWorkCompleted();
+ return;
+ }
+
+ SetResult(PopulateConnectionHandle(
+ manager_->Add(new UsbDeviceResource(extension_->id(), handle_)),
+ handle_->device()->vendor_id(),
+ handle_->device()->product_id()));
+ AsyncWorkCompleted();
+}
+
+UsbListInterfacesFunction::UsbListInterfacesFunction() {
+}
+
+UsbListInterfacesFunction::~UsbListInterfacesFunction() {
+}
+
+bool UsbListInterfacesFunction::Prepare() {
+ parameters_ = ListInterfaces::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbListInterfacesFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ scoped_refptr<UsbConfigDescriptor> config =
+ device_handle->device()->ListInterfaces();
+
+ if (!config) {
+ SetError(kErrorCannotListInterfaces);
+ AsyncWorkCompleted();
+ return;
+ }
+
+ result_.reset(new base::ListValue());
+
+ for (size_t i = 0, num_interfaces = config->GetNumInterfaces();
+ i < num_interfaces;
+ ++i) {
+ scoped_refptr<const UsbInterfaceDescriptor> usb_interface(
+ config->GetInterface(i));
+ for (size_t j = 0, num_descriptors = usb_interface->GetNumAltSettings();
+ j < num_descriptors;
+ ++j) {
+ scoped_refptr<const UsbInterfaceAltSettingDescriptor> descriptor =
+ usb_interface->GetAltSetting(j);
+ std::vector<linked_ptr<EndpointDescriptor> > endpoints;
+ for (size_t k = 0, num_endpoints = descriptor->GetNumEndpoints();
+ k < num_endpoints;
+ k++) {
+ scoped_refptr<const UsbEndpointDescriptor> endpoint =
+ descriptor->GetEndpoint(k);
+ linked_ptr<EndpointDescriptor> endpoint_desc(new EndpointDescriptor());
+
+ TransferType type;
+ Direction direction;
+ SynchronizationType synchronization;
+ UsageType usage;
+
+ if (!ConvertTransferTypeSafely(endpoint->GetTransferType(), &type) ||
+ !ConvertDirectionSafely(endpoint->GetDirection(), &direction) ||
+ !ConvertSynchronizationTypeSafely(
+ endpoint->GetSynchronizationType(), &synchronization) ||
+ !ConvertUsageTypeSafely(endpoint->GetUsageType(), &usage)) {
+ SetError(kErrorCannotListInterfaces);
+ AsyncWorkCompleted();
+ return;
+ }
+
+ endpoint_desc->address = endpoint->GetAddress();
+ endpoint_desc->type = type;
+ endpoint_desc->direction = direction;
+ endpoint_desc->maximum_packet_size = endpoint->GetMaximumPacketSize();
+ endpoint_desc->synchronization = synchronization;
+ endpoint_desc->usage = usage;
+
+ int* polling_interval = new int;
+ endpoint_desc->polling_interval.reset(polling_interval);
+ *polling_interval = endpoint->GetPollingInterval();
+
+ endpoints.push_back(endpoint_desc);
+ }
+
+ result_->Append(
+ PopulateInterfaceDescriptor(descriptor->GetInterfaceNumber(),
+ descriptor->GetAlternateSetting(),
+ descriptor->GetInterfaceClass(),
+ descriptor->GetInterfaceSubclass(),
+ descriptor->GetInterfaceProtocol(),
+ &endpoints));
+ }
+ }
+
+ SetResult(result_.release());
+ AsyncWorkCompleted();
+}
+
+bool UsbListInterfacesFunction::ConvertDirectionSafely(
+ const UsbEndpointDirection& input,
+ usb::Direction* output) {
+ const bool converted = ConvertDirectionToApi(input, output);
+ if (!converted)
+ SetError(kErrorConvertDirection);
+ return converted;
+}
+
+bool UsbListInterfacesFunction::ConvertSynchronizationTypeSafely(
+ const UsbSynchronizationType& input,
+ usb::SynchronizationType* output) {
+ const bool converted = ConvertSynchronizationTypeToApi(input, output);
+ if (!converted)
+ SetError(kErrorConvertSynchronizationType);
+ return converted;
+}
+
+bool UsbListInterfacesFunction::ConvertTransferTypeSafely(
+ const UsbTransferType& input,
+ usb::TransferType* output) {
+ const bool converted = ConvertTransferTypeToApi(input, output);
+ if (!converted)
+ SetError(kErrorConvertTransferType);
+ return converted;
+}
+
+bool UsbListInterfacesFunction::ConvertUsageTypeSafely(
+ const UsbUsageType& input,
+ usb::UsageType* output) {
+ const bool converted = ConvertUsageTypeToApi(input, output);
+ if (!converted)
+ SetError(kErrorConvertUsageType);
+ return converted;
+}
+
+UsbCloseDeviceFunction::UsbCloseDeviceFunction() {
+}
+
+UsbCloseDeviceFunction::~UsbCloseDeviceFunction() {
+}
+
+bool UsbCloseDeviceFunction::Prepare() {
+ parameters_ = CloseDevice::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbCloseDeviceFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ device_handle->Close();
+ RemoveUsbDeviceResource(parameters_->handle.handle);
+ AsyncWorkCompleted();
+}
+
+UsbClaimInterfaceFunction::UsbClaimInterfaceFunction() {
+}
+
+UsbClaimInterfaceFunction::~UsbClaimInterfaceFunction() {
+}
+
+bool UsbClaimInterfaceFunction::Prepare() {
+ parameters_ = ClaimInterface::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbClaimInterfaceFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ bool success = device_handle->ClaimInterface(parameters_->interface_number);
+
+ if (!success)
+ SetError(kErrorCannotClaimInterface);
+ AsyncWorkCompleted();
+}
+
+UsbReleaseInterfaceFunction::UsbReleaseInterfaceFunction() {
+}
+
+UsbReleaseInterfaceFunction::~UsbReleaseInterfaceFunction() {
+}
+
+bool UsbReleaseInterfaceFunction::Prepare() {
+ parameters_ = ReleaseInterface::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbReleaseInterfaceFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ bool success = device_handle->ReleaseInterface(parameters_->interface_number);
+ if (!success)
+ SetError(kErrorCannotReleaseInterface);
+ AsyncWorkCompleted();
+}
+
+UsbSetInterfaceAlternateSettingFunction::
+ UsbSetInterfaceAlternateSettingFunction() {
+}
+
+UsbSetInterfaceAlternateSettingFunction::
+ ~UsbSetInterfaceAlternateSettingFunction() {
+}
+
+bool UsbSetInterfaceAlternateSettingFunction::Prepare() {
+ parameters_ = SetInterfaceAlternateSetting::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbSetInterfaceAlternateSettingFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ bool success = device_handle->SetInterfaceAlternateSetting(
+ parameters_->interface_number, parameters_->alternate_setting);
+ if (!success)
+ SetError(kErrorCannotSetInterfaceAlternateSetting);
+
+ AsyncWorkCompleted();
+}
+
+UsbControlTransferFunction::UsbControlTransferFunction() {
+}
+
+UsbControlTransferFunction::~UsbControlTransferFunction() {
+}
+
+bool UsbControlTransferFunction::Prepare() {
+ parameters_ = ControlTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbControlTransferFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ const ControlTransferInfo& transfer = parameters_->transfer_info;
+
+ UsbEndpointDirection direction;
+ UsbDeviceHandle::TransferRequestType request_type;
+ UsbDeviceHandle::TransferRecipient recipient;
+ size_t size = 0;
+
+ if (!ConvertDirectionSafely(transfer.direction, &direction) ||
+ !ConvertRequestTypeSafely(transfer.request_type, &request_type) ||
+ !ConvertRecipientSafely(transfer.recipient, &recipient)) {
+ AsyncWorkCompleted();
+ return;
+ }
+
+ if (!GetTransferSize(transfer, &size)) {
+ CompleteWithError(kErrorInvalidTransferLength);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer =
+ CreateBufferForTransfer(transfer, direction, size);
+ if (!buffer.get()) {
+ CompleteWithError(kErrorMalformedParameters);
+ return;
+ }
+
+ device_handle->ControlTransfer(
+ direction,
+ request_type,
+ recipient,
+ transfer.request,
+ transfer.value,
+ transfer.index,
+ buffer.get(),
+ size,
+ 0,
+ base::Bind(&UsbControlTransferFunction::OnCompleted, this));
+}
+
+UsbBulkTransferFunction::UsbBulkTransferFunction() {
+}
+
+UsbBulkTransferFunction::~UsbBulkTransferFunction() {
+}
+
+bool UsbBulkTransferFunction::Prepare() {
+ parameters_ = BulkTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbBulkTransferFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ const GenericTransferInfo& transfer = parameters_->transfer_info;
+
+ UsbEndpointDirection direction;
+ size_t size = 0;
+
+ if (!ConvertDirectionSafely(transfer.direction, &direction)) {
+ AsyncWorkCompleted();
+ return;
+ }
+
+ if (!GetTransferSize(transfer, &size)) {
+ CompleteWithError(kErrorInvalidTransferLength);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer =
+ CreateBufferForTransfer(transfer, direction, size);
+ if (!buffer.get()) {
+ CompleteWithError(kErrorMalformedParameters);
+ return;
+ }
+
+ device_handle->BulkTransfer(
+ direction,
+ transfer.endpoint,
+ buffer.get(),
+ size,
+ 0,
+ base::Bind(&UsbBulkTransferFunction::OnCompleted, this));
+}
+
+UsbInterruptTransferFunction::UsbInterruptTransferFunction() {
+}
+
+UsbInterruptTransferFunction::~UsbInterruptTransferFunction() {
+}
+
+bool UsbInterruptTransferFunction::Prepare() {
+ parameters_ = InterruptTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbInterruptTransferFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ const GenericTransferInfo& transfer = parameters_->transfer_info;
+
+ UsbEndpointDirection direction;
+ size_t size = 0;
+
+ if (!ConvertDirectionSafely(transfer.direction, &direction)) {
+ AsyncWorkCompleted();
+ return;
+ }
+
+ if (!GetTransferSize(transfer, &size)) {
+ CompleteWithError(kErrorInvalidTransferLength);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer =
+ CreateBufferForTransfer(transfer, direction, size);
+ if (!buffer.get()) {
+ CompleteWithError(kErrorMalformedParameters);
+ return;
+ }
+
+ device_handle->InterruptTransfer(
+ direction,
+ transfer.endpoint,
+ buffer.get(),
+ size,
+ 0,
+ base::Bind(&UsbInterruptTransferFunction::OnCompleted, this));
+}
+
+UsbIsochronousTransferFunction::UsbIsochronousTransferFunction() {
+}
+
+UsbIsochronousTransferFunction::~UsbIsochronousTransferFunction() {
+}
+
+bool UsbIsochronousTransferFunction::Prepare() {
+ parameters_ = IsochronousTransfer::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbIsochronousTransferFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ const IsochronousTransferInfo& transfer = parameters_->transfer_info;
+ const GenericTransferInfo& generic_transfer = transfer.transfer_info;
+
+ size_t size = 0;
+ UsbEndpointDirection direction;
+
+ if (!ConvertDirectionSafely(generic_transfer.direction, &direction)) {
+ AsyncWorkCompleted();
+ return;
+ }
+ if (!GetTransferSize(generic_transfer, &size)) {
+ CompleteWithError(kErrorInvalidTransferLength);
+ return;
+ }
+ if (transfer.packets < 0 || transfer.packets >= kMaxPackets) {
+ CompleteWithError(kErrorInvalidNumberOfPackets);
+ return;
+ }
+ unsigned int packets = transfer.packets;
+ if (transfer.packet_length < 0 ||
+ transfer.packet_length >= kMaxPacketLength) {
+ CompleteWithError(kErrorInvalidPacketLength);
+ return;
+ }
+ unsigned int packet_length = transfer.packet_length;
+ const uint64 total_length = packets * packet_length;
+ if (packets > size || total_length > size) {
+ CompleteWithError(kErrorTransferLength);
+ return;
+ }
+
+ scoped_refptr<net::IOBuffer> buffer =
+ CreateBufferForTransfer(generic_transfer, direction, size);
+ if (!buffer.get()) {
+ CompleteWithError(kErrorMalformedParameters);
+ return;
+ }
+
+ device_handle->IsochronousTransfer(
+ direction,
+ generic_transfer.endpoint,
+ buffer.get(),
+ size,
+ packets,
+ packet_length,
+ 0,
+ base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this));
+}
+
+UsbResetDeviceFunction::UsbResetDeviceFunction() {
+}
+
+UsbResetDeviceFunction::~UsbResetDeviceFunction() {
+}
+
+bool UsbResetDeviceFunction::Prepare() {
+ parameters_ = ResetDevice::Params::Create(*args_);
+ EXTENSION_FUNCTION_VALIDATE(parameters_.get());
+ return true;
+}
+
+void UsbResetDeviceFunction::AsyncWorkStart() {
+ scoped_refptr<UsbDeviceHandle> device_handle =
+ GetDeviceHandleOrCompleteWithError(parameters_->handle);
+ if (!device_handle)
+ return;
+
+ bool success = device_handle->ResetDevice();
+ if (!success) {
+ device_handle->Close();
+ RemoveUsbDeviceResource(parameters_->handle.handle);
+ SetResult(new base::FundamentalValue(false));
+ CompleteWithError(kErrorResetDevice);
+ return;
+ }
+
+ SetResult(new base::FundamentalValue(true));
+ AsyncWorkCompleted();
+}
+
+} // namespace extensions
diff --git a/extensions/browser/api/usb/usb_api.h b/extensions/browser/api/usb/usb_api.h
new file mode 100644
index 0000000..3dd0d2b
--- /dev/null
+++ b/extensions/browser/api/usb/usb_api.h
@@ -0,0 +1,317 @@
+// Copyright 2014 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_API_H_
+#define EXTENSIONS_BROWSER_API_USB_USB_API_H_
+
+#include <string>
+#include <vector>
+
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "components/usb_service/usb_device.h"
+#include "components/usb_service/usb_device_handle.h"
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/browser/api/async_api_function.h"
+#include "extensions/common/api/usb.h"
+#include "net/base/io_buffer.h"
+
+namespace extensions {
+
+class UsbDeviceResource;
+
+class UsbAsyncApiFunction : public AsyncApiFunction {
+ public:
+ UsbAsyncApiFunction();
+
+ protected:
+ virtual ~UsbAsyncApiFunction();
+
+ virtual bool PrePrepare() OVERRIDE;
+ virtual bool Respond() OVERRIDE;
+
+ scoped_refptr<usb_service::UsbDevice> GetDeviceOrOrCompleteWithError(
+ const extensions::core_api::usb::Device& input_device);
+
+ scoped_refptr<usb_service::UsbDeviceHandle>
+ GetDeviceHandleOrCompleteWithError(
+ const extensions::core_api::usb::ConnectionHandle&
+ input_device_handle);
+
+ void RemoveUsbDeviceResource(int api_resource_id);
+
+ void CompleteWithError(const std::string& error);
+
+ ApiResourceManager<UsbDeviceResource>* manager_;
+};
+
+class UsbAsyncApiTransferFunction : public UsbAsyncApiFunction {
+ protected:
+ UsbAsyncApiTransferFunction();
+ virtual ~UsbAsyncApiTransferFunction();
+
+ bool ConvertDirectionSafely(const extensions::core_api::usb::Direction& input,
+ usb_service::UsbEndpointDirection* output);
+ bool ConvertRequestTypeSafely(
+ const extensions::core_api::usb::RequestType& input,
+ usb_service::UsbDeviceHandle::TransferRequestType* output);
+ bool ConvertRecipientSafely(
+ const extensions::core_api::usb::Recipient& input,
+ usb_service::UsbDeviceHandle::TransferRecipient* output);
+
+ void OnCompleted(usb_service::UsbTransferStatus status,
+ scoped_refptr<net::IOBuffer> data,
+ size_t length);
+};
+
+class UsbFindDevicesFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.findDevices", USB_FINDDEVICES)
+
+ UsbFindDevicesFunction();
+
+ protected:
+ virtual ~UsbFindDevicesFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ void OpenDevices(
+ scoped_ptr<std::vector<scoped_refptr<usb_service::UsbDevice> > > devices);
+
+ std::vector<scoped_refptr<usb_service::UsbDeviceHandle> > device_handles_;
+ scoped_ptr<extensions::core_api::usb::FindDevices::Params> parameters_;
+};
+
+class UsbGetDevicesFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.getDevices", USB_GETDEVICES)
+
+ UsbGetDevicesFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ protected:
+ virtual ~UsbGetDevicesFunction();
+
+ private:
+ void EnumerationCompletedFileThread(
+ scoped_ptr<std::vector<scoped_refptr<usb_service::UsbDevice> > > devices);
+
+ scoped_ptr<extensions::core_api::usb::GetDevices::Params> parameters_;
+};
+
+class UsbRequestAccessFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.requestAccess", USB_REQUESTACCESS)
+
+ UsbRequestAccessFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ protected:
+ virtual ~UsbRequestAccessFunction();
+
+ void OnCompleted(bool success);
+
+ private:
+ scoped_ptr<extensions::core_api::usb::RequestAccess::Params> parameters_;
+};
+
+class UsbOpenDeviceFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.openDevice", USB_OPENDEVICE)
+
+ UsbOpenDeviceFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ protected:
+ virtual ~UsbOpenDeviceFunction();
+
+ private:
+ scoped_refptr<usb_service::UsbDeviceHandle> handle_;
+ scoped_ptr<extensions::core_api::usb::OpenDevice::Params> parameters_;
+};
+
+class UsbListInterfacesFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.listInterfaces", USB_LISTINTERFACES)
+
+ UsbListInterfacesFunction();
+
+ protected:
+ virtual ~UsbListInterfacesFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ bool ConvertDirectionSafely(const usb_service::UsbEndpointDirection& input,
+ extensions::core_api::usb::Direction* output);
+ bool ConvertSynchronizationTypeSafely(
+ const usb_service::UsbSynchronizationType& input,
+ extensions::core_api::usb::SynchronizationType* output);
+ bool ConvertTransferTypeSafely(
+ const usb_service::UsbTransferType& input,
+ extensions::core_api::usb::TransferType* output);
+ bool ConvertUsageTypeSafely(const usb_service::UsbUsageType& input,
+ extensions::core_api::usb::UsageType* output);
+
+ scoped_ptr<base::ListValue> result_;
+ scoped_ptr<extensions::core_api::usb::ListInterfaces::Params> parameters_;
+};
+
+class UsbCloseDeviceFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.closeDevice", USB_CLOSEDEVICE)
+
+ UsbCloseDeviceFunction();
+
+ protected:
+ virtual ~UsbCloseDeviceFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::CloseDevice::Params> parameters_;
+};
+
+class UsbClaimInterfaceFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.claimInterface", USB_CLAIMINTERFACE)
+
+ UsbClaimInterfaceFunction();
+
+ protected:
+ virtual ~UsbClaimInterfaceFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::ClaimInterface::Params> parameters_;
+};
+
+class UsbReleaseInterfaceFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.releaseInterface", USB_RELEASEINTERFACE)
+
+ UsbReleaseInterfaceFunction();
+
+ protected:
+ virtual ~UsbReleaseInterfaceFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::ReleaseInterface::Params> parameters_;
+};
+
+class UsbSetInterfaceAlternateSettingFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.setInterfaceAlternateSetting",
+ USB_SETINTERFACEALTERNATESETTING)
+
+ UsbSetInterfaceAlternateSettingFunction();
+
+ private:
+ virtual ~UsbSetInterfaceAlternateSettingFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ scoped_ptr<extensions::core_api::usb::SetInterfaceAlternateSetting::Params>
+ parameters_;
+};
+
+class UsbControlTransferFunction : public UsbAsyncApiTransferFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.controlTransfer", USB_CONTROLTRANSFER)
+
+ UsbControlTransferFunction();
+
+ protected:
+ virtual ~UsbControlTransferFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::ControlTransfer::Params> parameters_;
+};
+
+class UsbBulkTransferFunction : public UsbAsyncApiTransferFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.bulkTransfer", USB_BULKTRANSFER)
+
+ UsbBulkTransferFunction();
+
+ protected:
+ virtual ~UsbBulkTransferFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::BulkTransfer::Params> parameters_;
+};
+
+class UsbInterruptTransferFunction : public UsbAsyncApiTransferFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.interruptTransfer", USB_INTERRUPTTRANSFER)
+
+ UsbInterruptTransferFunction();
+
+ protected:
+ virtual ~UsbInterruptTransferFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::InterruptTransfer::Params> parameters_;
+};
+
+class UsbIsochronousTransferFunction : public UsbAsyncApiTransferFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.isochronousTransfer", USB_ISOCHRONOUSTRANSFER)
+
+ UsbIsochronousTransferFunction();
+
+ protected:
+ virtual ~UsbIsochronousTransferFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::IsochronousTransfer::Params>
+ parameters_;
+};
+
+class UsbResetDeviceFunction : public UsbAsyncApiFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("usb.resetDevice", USB_RESETDEVICE)
+
+ UsbResetDeviceFunction();
+
+ protected:
+ virtual ~UsbResetDeviceFunction();
+
+ virtual bool Prepare() OVERRIDE;
+ virtual void AsyncWorkStart() OVERRIDE;
+
+ private:
+ scoped_ptr<extensions::core_api::usb::ResetDevice::Params> parameters_;
+};
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_USB_USB_API_H_
diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc
new file mode 100644
index 0000000..455c663
--- /dev/null
+++ b/extensions/browser/api/usb/usb_apitest.cc
@@ -0,0 +1,275 @@
+// Copyright 2014 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 "chrome/browser/extensions/extension_apitest.h"
+#include "chrome/browser/ui/browser.h"
+#include "components/usb_service/usb_service.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/test/test_utils.h"
+#include "extensions/browser/api/usb/usb_api.h"
+#include "net/base/io_buffer.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::AnyNumber;
+using testing::_;
+using testing::Return;
+using content::BrowserThread;
+using usb_service::UsbConfigDescriptor;
+using usb_service::UsbDevice;
+using usb_service::UsbDeviceHandle;
+using usb_service::UsbEndpointDirection;
+using usb_service::UsbService;
+using usb_service::UsbTransferCallback;
+
+namespace {
+
+ACTION_TEMPLATE(InvokeUsbTransferCallback,
+ HAS_1_TEMPLATE_PARAMS(int, k),
+ AND_1_VALUE_PARAMS(p1)) {
+ ::std::tr1::get<k>(args).Run(p1, new net::IOBuffer(1), 1);
+}
+
+// MSVC erroneously thinks that at least one of the arguments for the transfer
+// methods differ by const or volatility and emits a warning about the old
+// standards-noncompliant behaviour of their compiler.
+#if defined(OS_WIN)
+#pragma warning(push)
+#pragma warning(disable : 4373)
+#endif
+
+class MockUsbDeviceHandle : public UsbDeviceHandle {
+ public:
+ MockUsbDeviceHandle() : UsbDeviceHandle() {}
+
+ MOCK_METHOD0(Close, void());
+
+ MOCK_METHOD10(ControlTransfer,
+ void(const UsbEndpointDirection direction,
+ const TransferRequestType request_type,
+ const TransferRecipient recipient,
+ const uint8 request,
+ const uint16 value,
+ const uint16 index,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback));
+
+ MOCK_METHOD6(BulkTransfer,
+ void(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback));
+
+ MOCK_METHOD6(InterruptTransfer,
+ void(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback));
+
+ MOCK_METHOD8(IsochronousTransfer,
+ void(const UsbEndpointDirection direction,
+ const uint8 endpoint,
+ net::IOBuffer* buffer,
+ const size_t length,
+ const unsigned int packets,
+ const unsigned int packet_length,
+ const unsigned int timeout,
+ const UsbTransferCallback& callback));
+
+ MOCK_METHOD0(ResetDevice, bool());
+
+ void set_device(UsbDevice* device) { device_ = device; }
+
+ protected:
+ virtual ~MockUsbDeviceHandle() {}
+};
+
+class MockUsbConfigDescriptor : public UsbConfigDescriptor {
+ public:
+ MOCK_CONST_METHOD0(GetNumInterfaces, size_t());
+
+ protected:
+ virtual ~MockUsbConfigDescriptor() {}
+};
+
+class MockUsbDevice : public UsbDevice {
+ public:
+ explicit MockUsbDevice(MockUsbDeviceHandle* mock_handle)
+ : UsbDevice(), mock_handle_(mock_handle) {
+ mock_handle->set_device(this);
+ }
+
+ virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE {
+ return mock_handle_;
+ }
+
+ virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE {
+ EXPECT_TRUE(false) << "Should not be reached";
+ return false;
+ }
+
+#if defined(OS_CHROMEOS)
+ virtual void RequestUsbAcess(
+ int interface_id,
+ const base::Callback<void(bool success)>& callback) OVERRIDE {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE, base::Bind(callback, true));
+ }
+#endif // OS_CHROMEOS
+
+ MOCK_METHOD0(ListInterfaces, scoped_refptr<UsbConfigDescriptor>());
+
+ private:
+ MockUsbDeviceHandle* mock_handle_;
+ virtual ~MockUsbDevice() {}
+};
+
+class MockUsbService : public UsbService {
+ public:
+ explicit MockUsbService(scoped_refptr<UsbDevice> device) : device_(device) {}
+
+ protected:
+ virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) OVERRIDE {
+ EXPECT_EQ(unique_id, 0U);
+ return device_;
+ }
+
+ virtual void GetDevices(
+ std::vector<scoped_refptr<UsbDevice> >* devices) OVERRIDE {
+ STLClearObject(devices);
+ devices->push_back(device_);
+ }
+
+ scoped_refptr<UsbDevice> device_;
+};
+
+#if defined(OS_WIN)
+#pragma warning(pop)
+#endif
+
+class UsbApiTest : public ExtensionApiTest {
+ public:
+ virtual void SetUpOnMainThread() OVERRIDE {
+ mock_device_handle_ = new MockUsbDeviceHandle();
+ mock_device_ = new MockUsbDevice(mock_device_handle_.get());
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ BrowserThread::PostTaskAndReply(BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&UsbApiTest::SetUpService, this),
+ runner->QuitClosure());
+ runner->Run();
+ }
+
+ void SetUpService() {
+ UsbService::SetInstanceForTest(new MockUsbService(mock_device_));
+ }
+
+ virtual void CleanUpOnMainThread() OVERRIDE {
+ scoped_refptr<content::MessageLoopRunner> runner =
+ new content::MessageLoopRunner;
+ UsbService* service = NULL;
+ BrowserThread::PostTaskAndReply(
+ BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&UsbService::SetInstanceForTest, service),
+ runner->QuitClosure());
+ runner->Run();
+ }
+
+ protected:
+ scoped_refptr<MockUsbDeviceHandle> mock_device_handle_;
+ scoped_refptr<MockUsbDevice> mock_device_;
+};
+
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) {
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(4);
+ ASSERT_TRUE(RunExtensionTest("usb/device_handling"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) {
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(2);
+ EXPECT_CALL(*mock_device_handle_.get(), ResetDevice())
+ .WillOnce(Return(true))
+ .WillOnce(Return(false));
+ EXPECT_CALL(
+ *mock_device_handle_.get(),
+ InterruptTransfer(usb_service::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED));
+ ASSERT_TRUE(RunExtensionTest("usb/reset_device"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, ListInterfaces) {
+ scoped_refptr<MockUsbConfigDescriptor> mock_descriptor =
+ new MockUsbConfigDescriptor();
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
+ EXPECT_CALL(*mock_descriptor.get(), GetNumInterfaces()).WillOnce(Return(0));
+ EXPECT_CALL(*mock_device_.get(), ListInterfaces())
+ .WillOnce(Return(mock_descriptor));
+ ASSERT_TRUE(RunExtensionTest("usb/list_interfaces"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferEvent) {
+ EXPECT_CALL(*mock_device_handle_.get(),
+ ControlTransfer(usb_service::USB_DIRECTION_OUTBOUND,
+ UsbDeviceHandle::STANDARD,
+ UsbDeviceHandle::DEVICE,
+ 1,
+ 2,
+ 3,
+ _,
+ 1,
+ _,
+ _))
+ .WillOnce(
+ InvokeUsbTransferCallback<9>(usb_service::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(*mock_device_handle_.get(),
+ BulkTransfer(usb_service::USB_DIRECTION_OUTBOUND, 1, _, 1, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(
+ *mock_device_handle_.get(),
+ InterruptTransfer(usb_service::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(*mock_device_handle_.get(),
+ IsochronousTransfer(
+ usb_service::USB_DIRECTION_OUTBOUND, 3, _, 1, 1, 1, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<7>(usb_service::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
+ ASSERT_TRUE(RunExtensionTest("usb/transfer_event"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, ZeroLengthTransfer) {
+ EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, 0, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED));
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
+ ASSERT_TRUE(RunExtensionTest("usb/zero_length_transfer"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, TransferFailure) {
+ EXPECT_CALL(*mock_device_handle_.get(), BulkTransfer(_, _, _, _, _, _))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_COMPLETED))
+ .WillOnce(InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_ERROR))
+ .WillOnce(
+ InvokeUsbTransferCallback<5>(usb_service::USB_TRANSFER_TIMEOUT));
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
+ ASSERT_TRUE(RunExtensionTest("usb/transfer_failure"));
+}
+
+IN_PROC_BROWSER_TEST_F(UsbApiTest, InvalidLengthTransfer) {
+ EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(AnyNumber());
+ ASSERT_TRUE(RunExtensionTest("usb/invalid_length_transfer"));
+}
diff --git a/extensions/browser/api/usb/usb_device_resource.cc b/extensions/browser/api/usb/usb_device_resource.cc
new file mode 100644
index 0000000..030de7b
--- /dev/null
+++ b/extensions/browser/api/usb/usb_device_resource.cc
@@ -0,0 +1,45 @@
+// Copyright 2014 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_resource.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/synchronization/lock.h"
+#include "components/usb_service/usb_device_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/api/api_resource.h"
+#include "extensions/common/api/usb.h"
+
+using content::BrowserThread;
+using usb_service::UsbDeviceHandle;
+
+namespace extensions {
+
+static base::LazyInstance<
+ BrowserContextKeyedAPIFactory<ApiResourceManager<UsbDeviceResource> > >
+ g_factory = LAZY_INSTANCE_INITIALIZER;
+
+// static
+template <>
+BrowserContextKeyedAPIFactory<ApiResourceManager<UsbDeviceResource> >*
+ApiResourceManager<UsbDeviceResource>::GetFactoryInstance() {
+ return g_factory.Pointer();
+}
+
+UsbDeviceResource::UsbDeviceResource(const std::string& owner_extension_id,
+ scoped_refptr<UsbDeviceHandle> device)
+ : ApiResource(owner_extension_id), device_(device) {
+}
+
+UsbDeviceResource::~UsbDeviceResource() {
+ BrowserThread::PostTask(BrowserThread::FILE,
+ FROM_HERE,
+ base::Bind(&UsbDeviceHandle::Close, device_));
+}
+
+} // namespace extensions
diff --git a/extensions/browser/api/usb/usb_device_resource.h b/extensions/browser/api/usb/usb_device_resource.h
new file mode 100644
index 0000000..cb6cbbe
--- /dev/null
+++ b/extensions/browser/api/usb/usb_device_resource.h
@@ -0,0 +1,50 @@
+// Copyright 2014 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_DEVICE_RESOURCE_H_
+#define EXTENSIONS_BROWSER_API_USB_USB_DEVICE_RESOURCE_H_
+
+#include <set>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/ref_counted.h"
+#include "base/synchronization/lock.h"
+#include "components/usb_service/usb_device_handle.h"
+#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/api/api_resource.h"
+#include "extensions/browser/api/api_resource_manager.h"
+#include "extensions/common/api/usb.h"
+
+namespace net {
+class IOBuffer;
+} // namespace net
+
+namespace extensions {
+
+// A UsbDeviceResource is an ApiResource wrapper for a UsbDevice.
+class UsbDeviceResource : public ApiResource {
+ public:
+ UsbDeviceResource(const std::string& owner_extension_id,
+ scoped_refptr<usb_service::UsbDeviceHandle> device);
+ virtual ~UsbDeviceResource();
+
+ scoped_refptr<usb_service::UsbDeviceHandle> device() { return device_; }
+
+ static const content::BrowserThread::ID kThreadId =
+ content::BrowserThread::FILE;
+
+ private:
+ friend class ApiResourceManager<UsbDeviceResource>;
+ static const char* service_name() { return "UsbDeviceResourceManager"; }
+
+ scoped_refptr<usb_service::UsbDeviceHandle> device_;
+
+ DISALLOW_COPY_AND_ASSIGN(UsbDeviceResource);
+};
+
+} // namespace extensions
+
+#endif // EXTENSIONS_BROWSER_API_USB_USB_DEVICE_RESOURCE_H_
diff --git a/extensions/browser/api/usb/usb_manual_apitest.cc b/extensions/browser/api/usb/usb_manual_apitest.cc
new file mode 100644
index 0000000..d28be9d
--- /dev/null
+++ b/extensions/browser/api/usb/usb_manual_apitest.cc
@@ -0,0 +1,18 @@
+// Copyright 2014 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 "chrome/browser/extensions/api/permissions/permissions_api.h"
+#include "chrome/browser/extensions/extension_apitest.h"
+
+namespace {
+
+class UsbManualApiTest : public ExtensionApiTest {};
+
+} // namespace
+
+IN_PROC_BROWSER_TEST_F(UsbManualApiTest, MANUAL_ListInterfaces) {
+ extensions::PermissionsRequestFunction::SetIgnoreUserGestureForTests(true);
+ extensions::PermissionsRequestFunction::SetAutoConfirmForTests(true);
+ ASSERT_TRUE(RunExtensionTest("usb_manual/list_interfaces"));
+}
diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json
index cae93b1..4ea9b9e 100644
--- a/extensions/common/api/_api_features.json
+++ b/extensions/common/api/_api_features.json
@@ -39,5 +39,9 @@
"channel": "stable",
"extension_types": "all",
"contexts": ["blessed_extension", "unblessed_extension", "content_script"]
+ },
+ "usb": {
+ "dependencies": ["permission:usb"],
+ "contexts": ["blessed_extension"]
}
}
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json
index a1e975b..3447aac 100644
--- a/extensions/common/api/_permission_features.json
+++ b/extensions/common/api/_permission_features.json
@@ -53,5 +53,33 @@
"channel": "stable",
"extension_types": ["extension", "legacy_packaged_app", "platform_app"],
"min_manifest_version": 2
- }
+ },
+ "usb": [
+ {
+ "channel": "stable",
+ "extension_types": ["platform_app"]
+ },
+ {
+ "channel": "stable",
+ "extension_types": ["extension"],
+ "whitelist": [
+ "496B6890097EB6E19809ADEADD095A8721FBB2E0", // FIDO U2F APIs
+ "E24F1786D842E91E74C27929B0B3715A4689A473" // CryptoToken
+ ]
+ }
+ ],
+ "usbDevices": [
+ {
+ "channel": "stable",
+ "extension_types": ["platform_app"]
+ },
+ {
+ "channel": "stable",
+ "extension_types": ["extension"],
+ "whitelist": [
+ "496B6890097EB6E19809ADEADD095A8721FBB2E0", // FIDO U2F APIs
+ "E24F1786D842E91E74C27929B0B3715A4689A473" // CryptoToken
+ ]
+ }
+ ]
}
diff --git a/extensions/common/api/api.gyp b/extensions/common/api/api.gyp
index 4e3c2b0..b124637 100644
--- a/extensions/common/api/api.gyp
+++ b/extensions/common/api/api.gyp
@@ -30,6 +30,7 @@
'sockets_udp.idl',
'storage.json',
'test.json',
+ 'usb.idl',
],
'cc_dir': 'extensions/common/api',
'root_namespace': 'extensions::core_api',
diff --git a/extensions/common/api/usb.idl b/extensions/common/api/usb.idl
new file mode 100644
index 0000000..278171e
--- /dev/null
+++ b/extensions/common/api/usb.idl
@@ -0,0 +1,307 @@
+// Copyright 2014 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.
+
+// Use the <code>chrome.usb</code> API to interact with connected USB
+// devices. This API provides access to USB operations from within the context
+// of an app. Using this API, apps can function as drivers for hardware devices.
+namespace usb {
+
+ // Direction, Recipient, RequestType, and TransferType all map to their
+ // namesakes within the USB specification.
+ enum Direction {in, out};
+ enum Recipient {device, _interface, endpoint, other};
+ enum RequestType {standard, class, vendor, reserved};
+ enum TransferType {control, interrupt, isochronous, bulk};
+
+ // For isochronous mode, SynchronizationType and UsageType map to their
+ // namesakes within the USB specification.
+ enum SynchronizationType {asynchronous, adaptive, synchronous};
+ enum UsageType {data, feedback, explicitFeedback};
+
+ // Returned by |getDevices| to identify a connected USB device.
+ dictionary Device {
+ // The id of the USB device. It remains unchanged until the device is
+ // unplugged.
+ long device;
+ long vendorId;
+ long productId;
+ };
+
+ // Returned by |openDevice| to be used for USB communication.
+ // Every time a device is opened, a new connection handle is created.
+ //
+ // A connection handle represents the underlying data structure that contains
+ // all the data we need to communicate with a USB device, including the status
+ // of interfaces, the pending transfers, the descriptors, and etc. A connectin
+ // handle id is different from a USB device id.
+ //
+ // All connection handles can work together if the device allows it.
+ // The connection handle will be automatically closed when the app is reloaded
+ // or suspended.
+ //
+ // When a connection handle is closed, all the interfaces it claimed will be
+ // released and all the transfers in progress will be canceled immediately.
+ dictionary ConnectionHandle {
+ // The id of the USB connection handle.
+ long handle;
+ long vendorId;
+ long productId;
+ };
+
+ dictionary EndpointDescriptor {
+ long address;
+ TransferType type;
+ Direction direction;
+ long maximumPacketSize;
+
+ // Used for isochronous mode.
+ SynchronizationType? synchronization;
+ UsageType? usage;
+
+ // If this is an interrupt endpoint, this will be 1-255.
+ long? pollingInterval;
+ };
+
+ dictionary InterfaceDescriptor {
+ long interfaceNumber;
+ long alternateSetting;
+ long interfaceClass;
+ long interfaceSubclass;
+ long interfaceProtocol;
+ DOMString? description;
+ EndpointDescriptor[] endpoints;
+ };
+
+ // ControlTransferInfo represents that parameters to a single USB control
+ // transfer.
+ dictionary ControlTransferInfo {
+ // The direction of this transfer.
+ Direction direction;
+
+ // The intended recipient for this transfer.
+ Recipient recipient;
+
+ // The type of this request.
+ RequestType requestType;
+
+ long request;
+ long value;
+ long index;
+
+ // If this transfer is an input transfer, then this field must be set to
+ // indicate the expected data length. If this is an output transfer, then
+ // this field is ignored.
+ long? length;
+
+ // The data payload carried by this transfer. If this is an output transfer
+ // then this field must be set.
+ ArrayBuffer? data;
+ };
+
+ // GenericTransferInfo is used by both bulk and interrupt transfers to
+ // specify the parameters of the transfer.
+ dictionary GenericTransferInfo {
+ // The direction of this transfer.
+ Direction direction;
+
+ long endpoint;
+
+ // If this is an input transfer then this field indicates the size of the
+ // input buffer. If this is an output transfer then this field is ignored.
+ long? length;
+
+ // If this is an output transfer then this field must be populated.
+ // Otherwise, it will be ignored.
+ ArrayBuffer? data;
+ };
+
+ // IsochronousTransferInfo describes a single multi-packet isochronous
+ // transfer.
+ dictionary IsochronousTransferInfo {
+ // All of the normal transfer parameters are encapsulated in the
+ // transferInfo parameters. Note that the data specified in this parameter
+ // block is split along packetLength boundaries to form the individual
+ // packets of the transfer.
+ GenericTransferInfo transferInfo;
+
+ // The total number of packets in this transfer.
+ long packets;
+
+ // The length of each of the packets in this transfer.
+ long packetLength;
+ };
+
+ dictionary TransferResultInfo {
+ // A value of 0 indicates that the transfer was a success. Other values
+ // indicate failure.
+ long? resultCode;
+
+ // If the transfer was an input transfer then this field will contain all
+ // of the input data requested.
+ ArrayBuffer? data;
+ };
+
+ // Describes the properties of devices which are found via |getDevices|.
+ dictionary EnumerateDevicesOptions {
+ long vendorId;
+ long productId;
+ };
+
+ // Describes the properties of devices which are found via |findDevices|.
+ dictionary EnumerateDevicesAndRequestAccessOptions {
+ long vendorId;
+ long productId;
+ // The interface id to request access against.
+ // Only available on ChromeOS. It has no effect on other platforms.
+ long? interfaceId;
+ };
+
+ callback VoidCallback = void ();
+ callback GetDevicesCallback = void (Device[] devices);
+ callback RequestAccessCallback = void (boolean sucess);
+ callback OpenDeviceCallback = void (ConnectionHandle handle);
+ callback FindDevicesCallback = void (ConnectionHandle[] handles);
+ callback ListInterfacesCallback = void (InterfaceDescriptor[] descriptors);
+ callback CloseDeviceCallback = void ();
+ callback TransferCallback = void (TransferResultInfo info);
+ callback ResetDeviceCallback = void(boolean result);
+
+ interface Functions {
+ // Lists USB devices specified by vendorId/productId/interfaceId tuple.
+ // |options|: The properties to search for on target devices.
+ // |callback|: Invoked with a list of |Device|s on complete.
+ static void getDevices(EnumerateDevicesOptions options,
+ GetDevicesCallback callback);
+
+ // This method is ChromeOS specific. Calling this method on other platforms
+ // will fail.
+ // Requests access from the permission broker to an OS claimed device if the
+ // given interface on the device is not claimed.
+ //
+ // |device|: The device to request access to.
+ // |interfaceId|:
+ static void requestAccess(Device device,
+ long interfaceId,
+ RequestAccessCallback callback);
+
+ // Opens a USB device returned by |getDevices|.
+ // |device|: The device to open.
+ // |callback|: Invoked with the created ConnectionHandle on complete.
+ static void openDevice(Device device, OpenDeviceCallback callback);
+
+ // Finds USB devices specified by the vendorId/productId/interfaceId tuple
+ // and, if permissions allow, opens them for use.
+ //
+ // On Chrome OS, you can specify the interfaceId. In that case the method
+ // will request access from permission broker in the same way as in
+ // |requestUsbAcess|.
+ //
+ // If the access request is rejected, or the device is failed to be opened,
+ // its connection handle will not be created or returned.
+ //
+ // Calling this method is equivalent to calling |getDevices| followed by
+ // a series of |requestAccess| (if it is on ChromeOs) and |openDevice|
+ // calls, and returning all the successfully opened connection handles.
+ //
+ // |options|: The properties to search for on target devices.
+ // |callback|: Invoked with the opened ConnectionHandle on complete.
+ static void findDevices(EnumerateDevicesAndRequestAccessOptions options,
+ FindDevicesCallback callback);
+
+ // Closes a connection handle. Invoking operations on a device after it
+ // has been closed is a safe operation, but causes no action to be taken.
+ // |handle|: The connection handle to close.
+ // |callback|: The callback to invoke once the device is closed.
+ static void closeDevice(ConnectionHandle handle,
+ optional CloseDeviceCallback callback);
+
+ // Lists all the interfaces on the USB device.
+ // |handle|: The device from which the interfaces should be listed.
+ // |callback|: The callback to invoke when the interfaces are enumerated.
+ static void listInterfaces(ConnectionHandle handle,
+ ListInterfacesCallback callback);
+
+ // Claims an interface on the specified USB device.
+ // Before you can transfer data with endpoints, you must claim their parent
+ // interfaces. Only one connection handle on the same host can claim each
+ // interface. If the interface is already claimed, this call will fail.
+ //
+ // You shall call releaseInterface when the interface is not needed anymore.
+ //
+ // |handle|: The device on which the interface is to be claimed.
+ // |interface|: The interface number to be claimed.
+ // |callback|: The callback to invoke once the interface is claimed.
+ static void claimInterface(ConnectionHandle handle, long interfaceNumber,
+ VoidCallback callback);
+
+ // Releases a claim to an interface on the provided device.
+ // |handle|: The device on which the interface is to be released.
+ // |interface|: The interface number to be released.
+ // |callback|: The callback to invoke once the interface is released.
+ static void releaseInterface(ConnectionHandle handle, long interfaceNumber,
+ VoidCallback callback);
+
+ // Selects an alternate setting on a previously claimed interface on a
+ // device.
+ // |handle|: The device on which the interface settings are to be set.
+ // |interface|: The interface number to be set.
+ // |alternateSetting|: The alternate setting to set.
+ // |callback|: The callback to invoke once the interface setting is set.
+ static void setInterfaceAlternateSetting(ConnectionHandle handle,
+ long interfaceNumber,
+ long alternateSetting,
+ VoidCallback callback);
+
+ // Performs a control transfer on the specified device. See the
+ // ControlTransferInfo structure for the parameters required to make a
+ // transfer.
+ //
+ // Conceptually control transfer talks to the device itself. You do not need
+ // to claim interface 0 to perform a control transfer.
+ //
+ // |handle|: A connection handle to make the transfer on.
+ // |transferInfo|: The parameters to the transfer. See ControlTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void controlTransfer(ConnectionHandle handle,
+ ControlTransferInfo transferInfo,
+ TransferCallback callback);
+
+ // Performs a bulk transfer on the specified device.
+ // |handle|: A connection handle to make the transfer on.
+ // |transferInfo|: The parameters to the transfer. See GenericTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void bulkTransfer(ConnectionHandle handle,
+ GenericTransferInfo transferInfo,
+ TransferCallback callback);
+
+ // Performs an interrupt transfer on the specified device.
+ // |handle|: A connection handle to make the transfer on.
+ // |transferInfo|: The parameters to the transfer. See GenericTransferInfo.
+ // |callback|: Invoked once the transfer has completed.
+ static void interruptTransfer(ConnectionHandle handle,
+ GenericTransferInfo transferInfo,
+ TransferCallback callback);
+
+ // Performs an isochronous transfer on the specific device.
+ // |handle|: A connection handle to make the transfer on.
+ // |transferInfo|: The parameters to the transfer. See
+ // IsochronousTransferInfo.
+ // |callback|: Invoked once the transfer has been completed.
+ static void isochronousTransfer(ConnectionHandle handle,
+ IsochronousTransferInfo transferInfo,
+ TransferCallback callback);
+
+ // Tries to reset the USB device and restores it to the previous status.
+ // If the reset fails, the given connection handle will be closed and the
+ // USB device will appear to be disconnected then reconnected.
+ // In that case you must call |getDevices| or |findDevices| again to acquire
+ // the device.
+ //
+ // |handle|: A connection handle to reset.
+ // |callback|: Invoked once the device is reset with a boolean indicating
+ // whether the reset is completed successfully.
+ static void resetDevice(ConnectionHandle handle,
+ ResetDeviceCallback callback);
+ };
+};
diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp
index 4411dae6..faaa42bc 100644
--- a/extensions/extensions.gyp
+++ b/extensions/extensions.gyp
@@ -221,6 +221,7 @@
'type': 'static_library',
'dependencies': [
'../components/components.gyp:keyed_service_content',
+ '../components/components.gyp:usb_service',
'../content/content.gyp:content_browser',
'../skia/skia.gyp:skia',
'../third_party/leveldatabase/leveldatabase.gyp:leveldatabase',
@@ -292,6 +293,10 @@
'browser/api/storage/weak_unlimited_settings_storage.h',
'browser/api/test/test_api.cc',
'browser/api/test/test_api.h',
+ 'browser/api/usb/usb_api.cc',
+ 'browser/api/usb/usb_api.h',
+ 'browser/api/usb/usb_device_resource.cc',
+ 'browser/api/usb/usb_device_resource.h',
'browser/api_activity_monitor.h',
'browser/app_sorting.h',
'browser/blacklist_state.h',
@@ -405,6 +410,9 @@
'browser/browser_context_keyed_service_factories.cc',
'browser/browser_context_keyed_service_factories.h',
],
+ 'dependencies!': [
+ '../components/components.gyp:usb_service',
+ ],
}],
],
# Disable c4267 warnings until we fix size_t to int truncations.