diff options
author | gdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-28 02:13:09 +0000 |
---|---|---|
committer | gdk@chromium.org <gdk@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-04-28 02:13:09 +0000 |
commit | 0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0 (patch) | |
tree | 89a2ad56663e27bccbe09ccc171530b981bdb4c8 | |
parent | 064fd6c0eaa0e5cccf2161ffadcbd80701f17cd9 (diff) | |
download | chromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.zip chromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.tar.gz chromium_src-0b81c3fd9f6614fe27acdbd48c1947a86d22e8c0.tar.bz2 |
Experimental USB API
This change adds a new API for low-level access to USB devices. The change
depends on https://chromiumcodereview.appspot.com/10161035/
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/10224009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@134423 0039d316-1c4b-4281-b951-d872f2087c98
23 files changed, 853 insertions, 18 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index a8b7452..f9674d7 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -4305,6 +4305,9 @@ Update checks have repeatedly failed for the extension "<ph name="EXTENSION_NAME <message name="IDS_EXTENSION_PROMPT_WARNING_MANAGED_MODE" desc="Permission string for access to managed mode."> Managed mode </message> + <message name="IDS_EXTENSION_PROMPT_WARNING_USB" desc="Permission string for access to USB devices."> + Your USB peripherals + </message> <!-- Extension/App error messages --> <message name="IDS_EXTENSION_CANT_GET_ABSOLUTE_PATH" desc="Warning displayed in pack dialog when the absolute path to the extension directory can not be found."> diff --git a/chrome/browser/extensions/api/api_resource.h b/chrome/browser/extensions/api/api_resource.h index 44a6d8e..24f1afc 100644 --- a/chrome/browser/extensions/api/api_resource.h +++ b/chrome/browser/extensions/api/api_resource.h @@ -23,7 +23,8 @@ class APIResource { protected: enum APIResourceType { SocketResource, - SerialConnectionResource + SerialConnectionResource, + UsbDeviceResource, }; APIResource(APIResourceType api_resource_type, APIResourceEventNotifier* event_notifier); diff --git a/chrome/browser/extensions/api/api_resource_controller.cc b/chrome/browser/extensions/api/api_resource_controller.cc index b101429..28d7e74 100644 --- a/chrome/browser/extensions/api/api_resource_controller.cc +++ b/chrome/browser/extensions/api/api_resource_controller.cc @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/api_resource_controller.h" #include "chrome/browser/extensions/api/serial/serial_connection.h" #include "chrome/browser/extensions/api/socket/socket.h" +#include "chrome/browser/extensions/api/usb/usb_device_resource.h" namespace extensions { @@ -79,4 +80,10 @@ SerialConnection* APIResourceController::GetSerialConnection( GetAPIResource(APIResource::SerialConnectionResource, api_resource_id)); } +UsbDeviceResource* APIResourceController::GetUsbDeviceResource( + int api_resource_id) const { + return static_cast<UsbDeviceResource*>(GetAPIResource( + APIResource::UsbDeviceResource, api_resource_id)); +} + } // namespace extensions diff --git a/chrome/browser/extensions/api/api_resource_controller.h b/chrome/browser/extensions/api/api_resource_controller.h index 3e79182..1df2944 100644 --- a/chrome/browser/extensions/api/api_resource_controller.h +++ b/chrome/browser/extensions/api/api_resource_controller.h @@ -16,6 +16,7 @@ namespace extensions { class SerialConnection; class Socket; +class UsbDeviceResource; // kSrcIdKey, or "srcId," binds an APIResource to the onEvent closure that was // optionally passed to the APIResource's create() method. We generated it in @@ -42,6 +43,7 @@ class APIResourceController { // codebase. Socket* GetSocket(int api_resource_id) const; SerialConnection* GetSerialConnection(int api_resource_id) const; + UsbDeviceResource* GetUsbDeviceResource(int api_resource_id) const; private: int next_api_resource_id_; diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.cc b/chrome/browser/extensions/api/api_resource_event_notifier.cc index ef33c58..6a15809 100644 --- a/chrome/browser/extensions/api/api_resource_event_notifier.cc +++ b/chrome/browser/extensions/api/api_resource_event_notifier.cc @@ -14,18 +14,20 @@ using content::BrowserThread; namespace events { -// TODO(miket): This should be generic, but at the moment only socket sends -// onEvent events. We'll fix this when serial becomes nonblocking. -const char kOnAPIResourceEvent[] = "experimental.socket.onEvent"; +const char kExperimentalSocketOnEvent[] = "experimental.socket.onEvent"; +const char kExperimentalUsbOnEvent[] = "experimental.usb.onEvent"; }; namespace extensions { const char kEventTypeKey[] = "type"; + const char kEventTypeConnectComplete[] = "connectComplete"; const char kEventTypeDataRead[] = "dataRead"; const char kEventTypeWriteComplete[] = "writeComplete"; +const char kEventTypeTransferComplete[] = "transferComplete"; + const char kSrcIdKey[] = "srcId"; const char kIsFinalEventKey[] = "isFinalEvent"; @@ -48,7 +50,8 @@ APIResourceEventNotifier::APIResourceEventNotifier( } void APIResourceEventNotifier::OnConnectComplete(int result_code) { - SendEventWithResultCode(API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code); + SendEventWithResultCode(events::kExperimentalSocketOnEvent, + API_RESOURCE_EVENT_CONNECT_COMPLETE, result_code); } void APIResourceEventNotifier::OnDataRead(int result_code, @@ -70,11 +73,26 @@ void APIResourceEventNotifier::OnDataRead(int result_code, event->Set(kDataKey, data); event->SetString(kAddressKey, address); event->SetInteger(kPortKey, port); - DispatchEvent(event); + DispatchEvent(events::kExperimentalSocketOnEvent, event); } void APIResourceEventNotifier::OnWriteComplete(int result_code) { - SendEventWithResultCode(API_RESOURCE_EVENT_WRITE_COMPLETE, result_code); + SendEventWithResultCode(events::kExperimentalSocketOnEvent, + API_RESOURCE_EVENT_WRITE_COMPLETE, result_code); +} + +void APIResourceEventNotifier::OnTransferComplete(int result_code, + base::ListValue* data) { + if (src_id_ < 0) { + delete data; + return; + } + + DictionaryValue* event = CreateAPIResourceEvent( + API_RESOURCE_EVENT_TRANSFER_COMPLETE); + event->SetInteger(kResultCodeKey, result_code); + event->Set(kDataKey, data); + DispatchEvent(events::kExperimentalUsbOnEvent, event); } // static @@ -87,6 +105,8 @@ std::string APIResourceEventNotifier::APIResourceEventTypeToString( return kEventTypeDataRead; case API_RESOURCE_EVENT_WRITE_COMPLETE: return kEventTypeWriteComplete; + case API_RESOURCE_EVENT_TRANSFER_COMPLETE: + return kEventTypeTransferComplete; } NOTREACHED(); @@ -95,15 +115,17 @@ std::string APIResourceEventNotifier::APIResourceEventTypeToString( APIResourceEventNotifier::~APIResourceEventNotifier() {} -void APIResourceEventNotifier::DispatchEvent(DictionaryValue* event) { +void APIResourceEventNotifier::DispatchEvent(const std::string &extension, + DictionaryValue* event) { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind( - &APIResourceEventNotifier::DispatchEventOnUIThread, this, event)); + &APIResourceEventNotifier::DispatchEventOnUIThread, this, extension, + event)); } void APIResourceEventNotifier::DispatchEventOnUIThread( - DictionaryValue* event) { + const std::string &extension, DictionaryValue* event) { ListValue args; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -111,9 +133,8 @@ void APIResourceEventNotifier::DispatchEventOnUIThread( args.Set(0, event); std::string json_args; base::JSONWriter::Write(&args, &json_args); - router_->DispatchEventToExtension(src_extension_id_, - events::kOnAPIResourceEvent, - json_args, profile_, src_url_); + router_->DispatchEventToExtension(src_extension_id_, extension, json_args, + profile_, src_url_); } DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent( @@ -132,6 +153,7 @@ DictionaryValue* APIResourceEventNotifier::CreateAPIResourceEvent( } void APIResourceEventNotifier::SendEventWithResultCode( + const std::string &extension, APIResourceEventType event_type, int result_code) { if (src_id_ < 0) @@ -139,7 +161,7 @@ void APIResourceEventNotifier::SendEventWithResultCode( DictionaryValue* event = CreateAPIResourceEvent(event_type); event->SetInteger(kResultCodeKey, result_code); - DispatchEvent(event); + DispatchEvent(extension, event); } } // namespace extensions diff --git a/chrome/browser/extensions/api/api_resource_event_notifier.h b/chrome/browser/extensions/api/api_resource_event_notifier.h index 286a9c0..09f4070 100644 --- a/chrome/browser/extensions/api/api_resource_event_notifier.h +++ b/chrome/browser/extensions/api/api_resource_event_notifier.h @@ -25,7 +25,8 @@ namespace extensions { enum APIResourceEventType { API_RESOURCE_EVENT_CONNECT_COMPLETE, API_RESOURCE_EVENT_DATA_READ, - API_RESOURCE_EVENT_WRITE_COMPLETE + API_RESOURCE_EVENT_WRITE_COMPLETE, + API_RESOURCE_EVENT_TRANSFER_COMPLETE, }; extern const char kSrcIdKey[]; @@ -59,6 +60,8 @@ class APIResourceEventNotifier virtual void OnWriteComplete(int result_code); + virtual void OnTransferComplete(int result_code, base::ListValue* data); + static std::string APIResourceEventTypeToString( APIResourceEventType event_type); @@ -68,11 +71,13 @@ class APIResourceEventNotifier virtual ~APIResourceEventNotifier(); - void DispatchEvent(DictionaryValue* event); - void DispatchEventOnUIThread(DictionaryValue* event); + void DispatchEvent(const std::string &extension, DictionaryValue* event); + void DispatchEventOnUIThread(const std::string& extension, + DictionaryValue* event); DictionaryValue* CreateAPIResourceEvent(APIResourceEventType event_type); - void SendEventWithResultCode(APIResourceEventType event_type, + void SendEventWithResultCode(const std::string& extension, + APIResourceEventType event_type, int result_code); ExtensionEventRouter* router_; diff --git a/chrome/browser/extensions/api/usb/usb_api.cc b/chrome/browser/extensions/api/usb/usb_api.cc new file mode 100644 index 0000000..df6d9ee --- /dev/null +++ b/chrome/browser/extensions/api/usb/usb_api.cc @@ -0,0 +1,149 @@ +// Copyright (c) 2012 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/usb/usb_api.h" + +#include <vector> + +#include "base/memory/scoped_ptr.h" +#include "chrome/browser/extensions/api/api_resource_controller.h" +#include "chrome/browser/extensions/api/usb/usb_device_resource.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/usb/usb_service_factory.h" +#include "chrome/browser/usb/usb_service.h" +#include "chrome/common/extensions/api/experimental.usb.h" + +namespace BulkTransfer = extensions::api::experimental_usb::BulkTransfer; +namespace CloseDevice = extensions::api::experimental_usb::CloseDevice; +namespace ControlTransfer = extensions::api::experimental_usb::ControlTransfer; +namespace FindDevice = extensions::api::experimental_usb::FindDevice; +namespace InterruptTransfer = + extensions::api::experimental_usb::InterruptTransfer; +using extensions::api::experimental_usb::Device; +using std::vector; + +namespace extensions { + +UsbFindDeviceFunction::UsbFindDeviceFunction() {} + +UsbFindDeviceFunction::~UsbFindDeviceFunction() {} + +bool UsbFindDeviceFunction::Prepare() { + parameters_ = FindDevice::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + event_notifier_ = CreateEventNotifier(ExtractSrcId(2)); + return true; +} + +void UsbFindDeviceFunction::Work() { + UsbService* const service = + UsbServiceFactory::GetInstance()->GetForProfile(profile()); + DCHECK(service) << "No UsbService associated with profile."; + + UsbDevice* const device = service->FindDevice(parameters_->vendor_id, + parameters_->product_id); + if (!device) { + result_.reset(base::Value::CreateNullValue()); + return; + } + + UsbDeviceResource* const resource = new UsbDeviceResource(event_notifier_, + device); + + Device result; + result.handle = controller()->AddAPIResource(resource); + result.vendor_id = parameters_->vendor_id; + result.product_id = parameters_->product_id; + result_ = result.ToValue(); +} + +bool UsbFindDeviceFunction::Respond() { + return true; +} + +UsbCloseDeviceFunction::UsbCloseDeviceFunction() {} + +UsbCloseDeviceFunction::~UsbCloseDeviceFunction() {} + +bool UsbCloseDeviceFunction::Prepare() { + parameters_ = CloseDevice::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +void UsbCloseDeviceFunction::Work() { + controller()->RemoveAPIResource(parameters_->device.handle); +} + +bool UsbCloseDeviceFunction::Respond() { + return true; +} + +UsbControlTransferFunction::UsbControlTransferFunction() {} + +UsbControlTransferFunction::~UsbControlTransferFunction() {} + +bool UsbControlTransferFunction::Prepare() { + parameters_ = ControlTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +void UsbControlTransferFunction::Work() { + UsbDeviceResource* const device = controller()->GetUsbDeviceResource( + parameters_->device.handle); + if (device) { + device->ControlTransfer(parameters_->transfer_info); + } +} + +bool UsbControlTransferFunction::Respond() { + return true; +} + +bool UsbBulkTransferFunction::Prepare() { + parameters_ = BulkTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +UsbBulkTransferFunction::UsbBulkTransferFunction() {} + +UsbBulkTransferFunction::~UsbBulkTransferFunction() {} + +void UsbBulkTransferFunction::Work() { + UsbDeviceResource* const device = controller()->GetUsbDeviceResource( + parameters_->device.handle); + if (device) { + device->BulkTransfer(parameters_->transfer_info); + } +} + +bool UsbBulkTransferFunction::Respond() { + return true; +} + +UsbInterruptTransferFunction::UsbInterruptTransferFunction() {} + +UsbInterruptTransferFunction::~UsbInterruptTransferFunction() {} + +bool UsbInterruptTransferFunction::Prepare() { + parameters_ = InterruptTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +void UsbInterruptTransferFunction::Work() { + UsbDeviceResource* const device = controller()->GetUsbDeviceResource( + parameters_->device.handle); + if (device) { + device->InterruptTransfer(parameters_->transfer_info); + } +} + +bool UsbInterruptTransferFunction::Respond() { + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/usb/usb_api.h b/chrome/browser/extensions/api/usb/usb_api.h new file mode 100644 index 0000000..b182f65 --- /dev/null +++ b/chrome/browser/extensions/api/usb/usb_api.h @@ -0,0 +1,105 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_ +#pragma once + +#include <string> + +#include "chrome/browser/extensions/api/api_function.h" +#include "chrome/common/extensions/api/experimental.usb.h" + +namespace extensions { + +class APIResourceEventNotifier; + +class UsbFindDeviceFunction : public AsyncIOAPIFunction { + public: + UsbFindDeviceFunction(); + virtual ~UsbFindDeviceFunction(); + + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + scoped_ptr<extensions::api::experimental_usb::FindDevice::Params> parameters_; + APIResourceEventNotifier* event_notifier_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.findDevice"); +}; + +class UsbCloseDeviceFunction : public AsyncIOAPIFunction { + public: + UsbCloseDeviceFunction(); + virtual ~UsbCloseDeviceFunction(); + + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + scoped_ptr<extensions::api::experimental_usb::CloseDevice::Params> + parameters_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.closeDevice"); +}; + +class UsbControlTransferFunction : public AsyncIOAPIFunction { + public: + UsbControlTransferFunction(); + virtual ~UsbControlTransferFunction(); + + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + scoped_ptr<extensions::api::experimental_usb::ControlTransfer::Params> + parameters_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.controlTransfer"); +}; + +class UsbBulkTransferFunction : public AsyncIOAPIFunction { + public: + UsbBulkTransferFunction(); + virtual ~UsbBulkTransferFunction(); + + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + scoped_ptr<extensions::api::experimental_usb::BulkTransfer::Params> + parameters_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.bulkTransfer"); +}; + +class UsbInterruptTransferFunction : public AsyncIOAPIFunction { + public: + UsbInterruptTransferFunction(); + virtual ~UsbInterruptTransferFunction(); + + protected: + virtual bool Prepare() OVERRIDE; + virtual void Work() OVERRIDE; + virtual bool Respond() OVERRIDE; + + private: + scoped_ptr<extensions::api::experimental_usb::InterruptTransfer::Params> + parameters_; + + DECLARE_EXTENSION_FUNCTION_NAME("experimental.usb.interruptTransfer"); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_USB_USB_API_H_ diff --git a/chrome/browser/extensions/api/usb/usb_device_resource.cc b/chrome/browser/extensions/api/usb/usb_device_resource.cc new file mode 100644 index 0000000..a659d4fb --- /dev/null +++ b/chrome/browser/extensions/api/usb/usb_device_resource.cc @@ -0,0 +1,199 @@ +// Copyright (c) 2012 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/usb/usb_device_resource.h" + +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/synchronization/lock.h" +#include "chrome/browser/extensions/api/api_resource_event_notifier.h" +#include "chrome/browser/extensions/api/api_resource.h" +#include "chrome/browser/usb/usb_device.h" +#include "chrome/common/extensions/api/experimental.usb.h" + +using extensions::api::experimental_usb::ControlTransferInfo; +using extensions::api::experimental_usb::GenericTransferInfo; +using std::string; +using std::vector; + +static const char* kDirectionIn = "in"; +static const char* kDirectionOut = "out"; + +static const char* kRequestTypeStandard = "standard"; +static const char* kRequestTypeClass = "class"; +static const char* kRequestTypeVendor = "vendor"; +static const char* kRequestTypeReserved = "reserved"; + +static const char* kRecipientDevice = "device"; +static const char* kRecipientInterface = "interface"; +static const char* kRecipientEndpoint = "endpoint"; +static const char* kRecipientOther = "other"; + +namespace { + +static bool ConvertDirection(const string& input, + UsbDevice::TransferDirection* output) { + if (input == kDirectionIn) { + *output = UsbDevice::INBOUND; + return true; + } else if (input == kDirectionOut) { + *output = UsbDevice::OUTBOUND; + return true; + } + return false; +} + +static bool ConvertRequestType(const string& input, + UsbDevice::TransferRequestType* output) { + if (input == kRequestTypeStandard) { + *output = UsbDevice::STANDARD; + return true; + } else if (input == kRequestTypeClass) { + *output = UsbDevice::CLASS; + return true; + } else if (input == kRequestTypeVendor) { + *output = UsbDevice::VENDOR; + return true; + } else if (input == kRequestTypeReserved) { + *output = UsbDevice::RESERVED; + return true; + } + return false; +} + +static bool ConvertRecipient(const string& input, + UsbDevice::TransferRecipient* output) { + if (input == kRecipientDevice) { + *output = UsbDevice::DEVICE; + return true; + } else if (input == kRecipientInterface) { + *output = UsbDevice::INTERFACE; + return true; + } else if (input == kRecipientEndpoint) { + *output = UsbDevice::ENDPOINT; + return true; + } else if (input == kRecipientOther) { + *output = UsbDevice::OTHER; + return true; + } + return false; +} + +template<class T> +static bool GetTransferSize(const T& input, unsigned int* output) { + if (input.direction == kDirectionIn) { + const int* length = input.length.get(); + if (length) { + *output = *length; + return true; + } + } else if (input.direction == kDirectionOut) { + if (input.data.get()) { + *output = input.data->size(); + return true; + } + } + return false; +} + +template<class T> +static scoped_refptr<net::IOBuffer> CreateBufferForTransfer(const T& input) { + unsigned int size = 0; + if (!GetTransferSize(input, &size)) { + return NULL; + } + + scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size); + if (!input.data.get()) { + return buffer; + } + + const vector<int>& input_buffer = *input.data.get(); + for (unsigned int i = 0; i < size; ++i) { + buffer->data()[i] = input_buffer[i]; + } + + return buffer; +} + +} // namespace + +namespace extensions { + +UsbDeviceResource::UsbDeviceResource(APIResourceEventNotifier* notifier, + UsbDevice* device) + : APIResource(APIResource::UsbDeviceResource, notifier), device_(device) {} + +UsbDeviceResource::~UsbDeviceResource() {} + +void UsbDeviceResource::ControlTransfer(const ControlTransferInfo& transfer) { + UsbDevice::TransferDirection direction; + UsbDevice::TransferRequestType request_type; + UsbDevice::TransferRecipient recipient; + unsigned int size; + scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer); + + if (!ConvertDirection(transfer.direction, &direction) || + !ConvertRequestType(transfer.request_type, &request_type) || + !ConvertRecipient(transfer.recipient, &recipient) || + !GetTransferSize(transfer, &size) || !buffer) { + LOG(INFO) << "Malformed transfer parameters."; + return; + } + + device_->ControlTransfer(direction, request_type, recipient, transfer.request, + transfer.value, transfer.index, buffer, size, 0, + base::Bind(&UsbDeviceResource::TransferComplete, + base::Unretained(this), buffer, size)); +} + +void UsbDeviceResource::InterruptTransfer(const GenericTransferInfo& transfer) { + unsigned int size; + UsbDevice::TransferDirection direction; + scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer); + + if (!ConvertDirection(transfer.direction, &direction) || + !GetTransferSize(transfer, &size) || !buffer) { + LOG(INFO) << "Malformed transfer parameters."; + return; + } + + device_->InterruptTransfer(direction, transfer.endpoint, buffer, size, 0, + base::Bind(&UsbDeviceResource::TransferComplete, + base::Unretained(this), buffer, size)); +} + +void UsbDeviceResource::BulkTransfer(const GenericTransferInfo& transfer) { + unsigned int size; + UsbDevice::TransferDirection direction; + scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer); + + if (!ConvertDirection(transfer.direction, &direction) || + !GetTransferSize(transfer, &size) || !buffer) { + LOG(INFO) << "Malformed transfer parameters."; + return; + } + + device_->BulkTransfer(direction, transfer.endpoint, buffer, size, 0, + base::Bind(&UsbDeviceResource::TransferComplete, + base::Unretained(this), buffer, size)); +} + +void UsbDeviceResource::TransferComplete(net::IOBuffer* buffer, + const size_t length, + int success) { + if (buffer) { + base::ListValue *const response_buffer = new base::ListValue(); + for (unsigned int i = 0; i < length; ++i) { + const uint8_t value = buffer->data()[i] & 0xFF; + response_buffer->Append(base::Value::CreateIntegerValue(value)); + } + event_notifier()->OnTransferComplete(success, response_buffer); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/usb/usb_device_resource.h b/chrome/browser/extensions/api/usb/usb_device_resource.h new file mode 100644 index 0000000..03c3793 --- /dev/null +++ b/chrome/browser/extensions/api/usb/usb_device_resource.h @@ -0,0 +1,58 @@ +// Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_ +#define CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_ +#pragma once + +#include <set> + +#include "base/basictypes.h" +#include "base/memory/linked_ptr.h" +#include "base/synchronization/lock.h" +#include "chrome/browser/extensions/api/api_resource.h" +#include "chrome/common/extensions/api/experimental.usb.h" + +class UsbDevice; + +namespace net { +class IOBuffer; +} // namespace net + +namespace extensions { + +class APIResourceEventNotifier; + +// A UsbDeviceResource is an APIResource wrapper for a UsbDevice. When invoking +// transfers on the underlying device it will use the APIResourceEventNotifier +// associated with the underlying APIResource to deliver completion messages. +class UsbDeviceResource : public APIResource { + public: + UsbDeviceResource(APIResourceEventNotifier* notifier, UsbDevice* device); + virtual ~UsbDeviceResource(); + + // All of the *Transfer variants that are exposed here adapt their arguments + // for the underlying UsbDevice's interface and invoke the corresponding + // methods with completion callbacks that call OnTransferComplete on the event + // notifier. + void ControlTransfer( + const api::experimental_usb::ControlTransferInfo& transfer); + void InterruptTransfer( + const api::experimental_usb::GenericTransferInfo& transfer); + void BulkTransfer(const api::experimental_usb::GenericTransferInfo& transfer); + + private: + // Invoked by the underlying device's transfer callbacks. Indicates transfer + // completion to the APIResource's event notifier. + void TransferComplete(net::IOBuffer* buffer, const size_t length, + int success); + + scoped_refptr<UsbDevice> device_; + + DISALLOW_COPY_AND_ASSIGN(UsbDeviceResource); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_USB_USB_DEVICE_RESOURCE_H_ diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index e968452..8b3b9c8 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -150,6 +150,10 @@ 'browser/extensions/api/terminal/terminal_extension_helper.h', 'browser/extensions/api/terminal/terminal_private_api.cc', 'browser/extensions/api/terminal/terminal_private_api.h', + 'browser/extensions/api/usb/usb_api.cc', + 'browser/extensions/api/usb/usb_api.h', + 'browser/extensions/api/usb/usb_device_resource.cc', + 'browser/extensions/api/usb/usb_device_resource.h', 'browser/extensions/api/web_navigation/web_navigation_api.cc', 'browser/extensions/api/web_navigation/web_navigation_api.h', 'browser/extensions/api/web_navigation/web_navigation_api_constants.cc', @@ -640,6 +644,14 @@ }], ], }], + ['OS=="android"', { + 'sources!': [ + 'browser/extensions/api/usb/usb_api.cc', + 'browser/extensions/api/usb/usb_api.h', + 'browser/extensions/api/usb/usb_device_resource.cc', + 'browser/extensions/api/usb/usb_device_resource.h', + ], + }], ], }, ], diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index cc41699..06ff06e 100644 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -78,6 +78,8 @@ 'renderer/extensions/event_bindings.h', 'renderer/extensions/experimental.socket_custom_bindings.cc', 'renderer/extensions/experimental.socket_custom_bindings.h', + 'renderer/extensions/experimental.usb_custom_bindings.cc', + 'renderer/extensions/experimental.usb_custom_bindings.h', 'renderer/extensions/extension_custom_bindings.cc', 'renderer/extensions/extension_custom_bindings.h', 'renderer/extensions/extension_dispatcher.cc', @@ -145,6 +147,7 @@ 'renderer/resources/extensions/experimental.offscreenTabs_custom_bindings.js', 'renderer/resources/extensions/experimental.runtime_custom_bindings.js', 'renderer/resources/extensions/experimental.socket_custom_bindings.js', + 'renderer/resources/extensions/experimental.usb_custom_bindings.js', 'renderer/resources/extensions/experimental.webrequest_custom_bindings.js', 'renderer/resources/extensions/extension_custom_bindings.js', 'renderer/resources/extensions/file_browser_handler_custom_bindings.js', @@ -299,6 +302,13 @@ }], ], }], + ['OS=="android"', { + 'sources!': [ + 'renderer/extensions/experimental.usb_custom_bindings.cc', + 'renderer/extensions/experimental.usb_custom_bindings.h', + 'renderer/resources/extensions/experimental.usb_custom_bindings.js', + ], + }], ], }, ], diff --git a/chrome/common/extensions/api/_permission_features.json b/chrome/common/extensions/api/_permission_features.json index 04dea29..9df9c02 100644 --- a/chrome/common/extensions/api/_permission_features.json +++ b/chrome/common/extensions/api/_permission_features.json @@ -220,6 +220,10 @@ "extension", "packaged_app", "hosted_app", "platform_app" ] }, + "usb": { + "channel": "stable", + "extension_types": ["platform_app"] + }, "webNavigation": { "channel": "stable", "extension_types": ["extension", "packaged_app"] diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp index 7646399..863bfd5 100644 --- a/chrome/common/extensions/api/api.gyp +++ b/chrome/common/extensions/api/api.gyp @@ -31,10 +31,18 @@ 'experimental.dns.idl', 'experimental.serial.idl', 'experimental.socket.idl', + 'experimental.usb.idl', ], 'cc_dir': 'chrome/common/extensions/api', 'root_namespace': 'extensions::api', }, + 'conditions': [ + ['OS=="android"', { + 'idl_schema_files!': [ + 'experimental.usb.idl', + ], + }], + ], }, ], } diff --git a/chrome/common/extensions/api/experimental.usb.idl b/chrome/common/extensions/api/experimental.usb.idl new file mode 100644 index 0000000..82d0a02 --- /dev/null +++ b/chrome/common/extensions/api/experimental.usb.idl @@ -0,0 +1,146 @@ +// Copyright (c) 2012 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. + +// TODO(gdk): The string-style enumerations are temporary, and will be removed +// once full enumeration support is added. Also, the array-of-longs are +// temporary and will be removed once there is full ArrayBuffer support. + +[nodoc] namespace experimental.usb { + + // A Device encapsulates everything that is needed to communicate with a USB + // device. They are returned by findDevice calls and have all of their + // fields populated before being returned. + dictionary Device { + long handle; + long vendorId; + long productId; + }; + + // ControlTransferInfo represents that parameters to a single USB control + // transfer. + dictionary ControlTransferInfo { + // The direction of this transfer. Must be one of either in or out. + DOMString direction; + + // The intended recipient for this transfer. Must be one of device, + // interface, endpoint, or other. + DOMString recipient; + + // The type of this request. Must be one of standard, class, vendor, + // or reserved. + DOMString 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 tranfer + // then this field must be set. + long[]? data; + }; + + // GenericTransferInfo is used by both bulk and interrupt transfers to + // specify the parameters of the transfer. + dictionary GenericTransferInfo { + // The direction of this transfer. Must be one of in or out. + DOMString 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. + long[]? data; + }; + + // When a USB event occurs the event handler specified by the DeviceOptions + // provided to findDevice will have a UsbEvent delivered to it which will + // contain the result of a transfer, including returned data. + dictionary UsbEvent { + // A string indicating the type of the event. Currently will only contain + // the value 'transferResult'. + DOMString type; + + // 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. + long[]? data; + + // The following fields are used for internal event routing and can be + // ignored. + [nodoc] boolean isFinalEvent; + [nodoc] long srcId; + }; + + callback OnEventCallback = void (UsbEvent event); + + dictionary DeviceOptions { + // The schema generator does not support dictionaries with only events. + // Ignore this field. + [nodoc] long? dummyValue; + + // Invoked by the extension API whenever an event occurs for the device(s) + // that this DeviceOptions is associated with. + OnEventCallback? onEvent; + }; + + callback FindDeviceCallback = void (optional Device device); + callback TransferCallback = void (); + + interface Functions { + // Finds the first instance of the USB device specified by the vendorId/ + // productId pair and, if permissions allow, opens it for use. + // Upon successfully opening a device the callback is invoked with a + // populated Device object. On failure, the callback is invoked with null. + // |vendorId|: The vendor ID of the USB device to find. + // |productId|: The product ID of the USB device to find. + // |callback|: Invoked with the opened Device on success. + static void findDevice(long vendorId, long productId, + DeviceOptions options, FindDeviceCallback callback); + + // Closes an open device instance. Invoking operations on a device after it + // has been closed is a safe operation, but causes no action to be taken. + // |device|: The device to close. + static void closeDevice(Device device); + + // Performs a control transfer on the specified device. See the + // ControlTransferInfo structure for the parameters required to make a + // transfer. + // |device|: An open device to make the transfer on. + // |transferInfo|: The parameters to the transfer. See ControlTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void controlTransfer(Device device, + ControlTransferInfo transferInfo, optional TransferCallback callback); + + // Performs a bulk transfer on the specified device. + // |device|: An open device to make the transfer on. + // |transferInfo|: The paramters to the transfer. See GenericTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void bulkTransfer(Device device, GenericTransferInfo transferInfo, + optional TransferCallback callback); + + // Performs an interrupt transfer on the specified device. + // |device|: An open device to make the transfer on. + // |transferInfo|: The paramters to the transfer. See GenericTransferInfo. + // |callback|: Invoked once the transfer has completed. + static void interruptTransfer(Device device, + GenericTransferInfo transferInfo, optional TransferCallback callback); + }; + + interface Events { + static void onEvent(UsbEvent event); + }; + +}; diff --git a/chrome/common/extensions/extension_permission_set.cc b/chrome/common/extensions/extension_permission_set.cc index 1f51c72..b34a79a 100644 --- a/chrome/common/extensions/extension_permission_set.cc +++ b/chrome/common/extensions/extension_permission_set.cc @@ -262,6 +262,9 @@ void ExtensionAPIPermission::RegisterAllPermissions( ExtensionPermissionMessage::kTtsEngine, kFlagCannotBeOptional); info->RegisterPermission( + kUsb, "usb", IDS_EXTENSION_PROMPT_WARNING_USB, + ExtensionPermissionMessage::kNone, kFlagNone); + info->RegisterPermission( kWebNavigation, "webNavigation", IDS_EXTENSION_PROMPT_WARNING_TABS, ExtensionPermissionMessage::kTabs, kFlagNone); diff --git a/chrome/common/extensions/extension_permission_set.h b/chrome/common/extensions/extension_permission_set.h index 79e8742f..75c4370 100644 --- a/chrome/common/extensions/extension_permission_set.h +++ b/chrome/common/extensions/extension_permission_set.h @@ -136,6 +136,7 @@ class ExtensionAPIPermission { kTts, kTtsEngine, kUnlimitedStorage, + kUsb, kWebNavigation, kWebRequest, kWebRequestBlocking, diff --git a/chrome/common/extensions/extension_permission_set_unittest.cc b/chrome/common/extensions/extension_permission_set_unittest.cc index dfd5e85..026858f 100644 --- a/chrome/common/extensions/extension_permission_set_unittest.cc +++ b/chrome/common/extensions/extension_permission_set_unittest.cc @@ -614,6 +614,7 @@ TEST(ExtensionPermissionsTest, PermissionMessages) { // Platform apps. TODO(miket): must we skip? skip.insert(ExtensionAPIPermission::kSocket); + skip.insert(ExtensionAPIPermission::kUsb); ExtensionPermissionsInfo* info = ExtensionPermissionsInfo::GetInstance(); ExtensionAPIPermissionSet permissions = info->GetAll(); diff --git a/chrome/renderer/extensions/experimental.usb_custom_bindings.cc b/chrome/renderer/extensions/experimental.usb_custom_bindings.cc new file mode 100644 index 0000000..a188f06 --- /dev/null +++ b/chrome/renderer/extensions/experimental.usb_custom_bindings.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2012 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/renderer/extensions/experimental.usb_custom_bindings.h" + +#include "base/logging.h" +#include "v8/include/v8.h" + +namespace { + +v8::Handle<v8::Value> GetNextUsbEventId(const v8::Arguments &arguments) { + static int next_event_id = 1; + return v8::Integer::New(next_event_id++); +} + +} // namespace + +namespace extensions { + +ExperimentalUsbCustomBindings::ExperimentalUsbCustomBindings() + : ChromeV8Extension(NULL) { + RouteStaticFunction("GetNextUsbEventId", &GetNextUsbEventId); +} + +} // namespace extensions diff --git a/chrome/renderer/extensions/experimental.usb_custom_bindings.h b/chrome/renderer/extensions/experimental.usb_custom_bindings.h new file mode 100644 index 0000000..05cbb87 --- /dev/null +++ b/chrome/renderer/extensions/experimental.usb_custom_bindings.h @@ -0,0 +1,20 @@ +// Copyright (c) 2012 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 CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_ +#define CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_ +#pragma once + +#include "chrome/renderer/extensions/chrome_v8_extension.h" + +namespace extensions { + +class ExperimentalUsbCustomBindings : public ChromeV8Extension { + public: + ExperimentalUsbCustomBindings(); +}; + +} // namespace extensions + +#endif // CHROME_RENDERER_EXTENSIONS_EXPERIMENTAL_USB_CUSTOM_BINDINGS_H_ diff --git a/chrome/renderer/extensions/extension_dispatcher.cc b/chrome/renderer/extensions/extension_dispatcher.cc index c808efa..90e86b5 100644 --- a/chrome/renderer/extensions/extension_dispatcher.cc +++ b/chrome/renderer/extensions/extension_dispatcher.cc @@ -24,6 +24,7 @@ #include "chrome/renderer/extensions/context_menus_custom_bindings.h" #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/experimental.socket_custom_bindings.h" +#include "chrome/renderer/extensions/experimental.usb_custom_bindings.h" #include "chrome/renderer/extensions/extension_custom_bindings.h" #include "chrome/renderer/extensions/extension_groups.h" #include "chrome/renderer/extensions/extension_helper.h" @@ -70,6 +71,7 @@ using content::RenderThread; using extensions::ApiDefinitionsNatives; using extensions::ContextMenusCustomBindings; using extensions::ExperimentalSocketCustomBindings; +using extensions::ExperimentalUsbCustomBindings; using extensions::ExtensionAPI; using extensions::ExtensionCustomBindings; using extensions::Feature; @@ -471,6 +473,8 @@ void ExtensionDispatcher::RegisterNativeHandlers(ModuleSystem* module_system, new ExtensionCustomBindings(this))); module_system->RegisterNativeHandler("experimental_socket", scoped_ptr<NativeHandler>(new ExperimentalSocketCustomBindings())); + module_system->RegisterNativeHandler("experimental_usb", + scoped_ptr<NativeHandler>(new ExperimentalUsbCustomBindings())); module_system->RegisterNativeHandler("file_browser_handler", scoped_ptr<NativeHandler>(new FileBrowserHandlerCustomBindings())); module_system->RegisterNativeHandler("file_browser_private", @@ -522,6 +526,8 @@ void ExtensionDispatcher::PopulateSourceMap() { IDR_EXPERIMENTAL_RUNTIME_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("experimental.socket", IDR_EXPERIMENTAL_SOCKET_CUSTOM_BINDINGS_JS); + source_map_.RegisterSource("experimental.usb", + IDR_EXPERIMENTAL_USB_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("experimental.webRequest", IDR_EXPERIMENTAL_WEBREQUEST_CUSTOM_BINDINGS_JS); source_map_.RegisterSource("extension", IDR_EXTENSION_CUSTOM_BINDINGS_JS); diff --git a/chrome/renderer/renderer_resources.grd b/chrome/renderer/renderer_resources.grd index 60eb28f5..e7ba3c6 100644 --- a/chrome/renderer/renderer_resources.grd +++ b/chrome/renderer/renderer_resources.grd @@ -41,6 +41,7 @@ without changes to the corresponding grd file. fb9 --> <include name="IDR_EXPERIMENTAL_OFFSCREENTABS_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.offscreenTabs_custom_bindings.js" type="BINDATA" /> <include name="IDR_EXPERIMENTAL_RUNTIME_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.runtime_custom_bindings.js" type="BINDATA" /> <include name="IDR_EXPERIMENTAL_SOCKET_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.socket_custom_bindings.js" type="BINDATA" /> + <include name="IDR_EXPERIMENTAL_USB_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.usb_custom_bindings.js" type="BINDATA" /> <include name="IDR_EXPERIMENTAL_WEBREQUEST_CUSTOM_BINDINGS_JS" file="resources\extensions\experimental.webrequest_custom_bindings.js" type="BINDATA" /> <include name="IDR_EXTENSION_CUSTOM_BINDINGS_JS" file="resources\extensions\extension_custom_bindings.js" type="BINDATA" /> <include name="IDR_FILE_BROWSER_HANDLER_CUSTOM_BINDINGS_JS" file="resources\extensions\file_browser_handler_custom_bindings.js" type="BINDATA" /> diff --git a/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js b/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js new file mode 100644 index 0000000..758af52 --- /dev/null +++ b/chrome/renderer/resources/extensions/experimental.usb_custom_bindings.js @@ -0,0 +1,46 @@ +// Copyright (c) 2012 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. + +// TODO(gdk): This all looks very similar to the socket custom bindings, and for +// a good reason, because they essentially do the same work. Refactor onEvent +// bindings out of a per-extension mechanism into a generalized mechanism. + + var experimentalUsbNatives = requireNative('experimental_usb'); + var GetNextUsbEventId = experimentalUsbNatives.GetNextUsbEventId; + + var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); + var sendRequest = require('sendRequest').sendRequest; + var lazyBG = requireNative('lazy_background_page'); + + chromeHidden.registerCustomHook('experimental.usb', function(api) { + var apiFunctions = api.apiFunctions; + + apiFunctions.setHandleRequest('findDevice', function() { + var args = arguments; + if (args[2] && args[2].onEvent) { + var id = GetNextUsbEventId(); + args[2].srcId = id; + chromeHidden.usb.handlers[id] = args[2].onEvent; + lazyBG.IncrementKeepaliveCount(); + } + sendRequest(this.name, args, this.definition.parameters); + return id; + }); + + chromeHidden.usb = {}; + chromeHidden.usb.handlers = {}; + chrome.experimental.usb.onEvent.addListener(function(event) { + var eventHandler = chromeHidden.usb.handlers[event.srcId]; + if (eventHandler) { + switch (event.type) { + case 'transferComplete': + eventHandler({resultCode: event.resultCode, data: event.data}); + break; + default: + console.error('Unexpected UsbEvent, type ' + event.type); + break; + } + } + }); + }); |