Commit 7c403e06 authored by reillyg's avatar reillyg Committed by Commit bot

Add more generic filters to the chrome.usb.getDevices API.

Instead of specifying each of the USB vendor and product ID pairs an
app has permission to open this API extension allows filtering of
devices by vendor and product IDs as well as USB interface classes, sub-
classes and protocols. The 'vendorId' and 'productId' properties of
EnumerateDevicesOptions are still supported for backwards compatibility.

BUG=

Review URL: https://codereview.chromium.org/517923002

Cr-Commit-Position: refs/heads/master@{#293032}
parent 95aa6b1a
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
#include "base/values.h" #include "base/values.h"
#include "components/usb_service/usb_device.h" #include "components/usb_service/usb_device.h"
#include "components/usb_service/usb_device_handle.h"
#include "components/usb_service/usb_interface.h" #include "components/usb_service/usb_interface.h"
namespace usb_service { namespace usb_service {
...@@ -57,7 +56,7 @@ void UsbDeviceFilter::SetInterfaceProtocol(uint8 interface_protocol) { ...@@ -57,7 +56,7 @@ void UsbDeviceFilter::SetInterfaceProtocol(uint8 interface_protocol) {
interface_protocol_ = interface_protocol; interface_protocol_ = interface_protocol;
} }
bool UsbDeviceFilter::Matches(scoped_refptr<UsbDevice> device) { bool UsbDeviceFilter::Matches(scoped_refptr<UsbDevice> device) const {
if (vendor_id_set_) { if (vendor_id_set_) {
if (device->vendor_id() != vendor_id_) { if (device->vendor_id() != vendor_id_) {
return false; return false;
...@@ -125,4 +124,17 @@ base::Value* UsbDeviceFilter::ToValue() const { ...@@ -125,4 +124,17 @@ base::Value* UsbDeviceFilter::ToValue() const {
return obj.release(); return obj.release();
} }
// static
bool UsbDeviceFilter::MatchesAny(scoped_refptr<UsbDevice> device,
const std::vector<UsbDeviceFilter>& filters) {
for (std::vector<UsbDeviceFilter>::const_iterator i = filters.begin();
i != filters.end();
++i) {
if (i->Matches(device)) {
return true;
}
}
return false;
}
} // namespace usb_service } // namespace usb_service
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_ #ifndef COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_
#define COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_ #define COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_
#include <vector>
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "components/usb_service/usb_service_export.h" #include "components/usb_service/usb_service_export.h"
...@@ -27,9 +29,12 @@ class USB_SERVICE_EXPORT UsbDeviceFilter { ...@@ -27,9 +29,12 @@ class USB_SERVICE_EXPORT UsbDeviceFilter {
void SetInterfaceSubclass(uint8 interface_subclass); void SetInterfaceSubclass(uint8 interface_subclass);
void SetInterfaceProtocol(uint8 interface_protocol); void SetInterfaceProtocol(uint8 interface_protocol);
bool Matches(scoped_refptr<UsbDevice> device); bool Matches(scoped_refptr<UsbDevice> device) const;
base::Value* ToValue() const; base::Value* ToValue() const;
static bool MatchesAny(scoped_refptr<UsbDevice> device,
const std::vector<UsbDeviceFilter>& filters);
private: private:
uint16 vendor_id_; uint16 vendor_id_;
uint16 product_id_; uint16 product_id_;
......
...@@ -237,3 +237,20 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) { ...@@ -237,3 +237,20 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) {
filter.SetInterfaceProtocol(0x02); filter.SetInterfaceProtocol(0x02);
ASSERT_FALSE(filter.Matches(android_phone_)); ASSERT_FALSE(filter.Matches(android_phone_));
} }
TEST_F(UsbFilterTest, MatchAnyEmptyListNegative) {
std::vector<UsbDeviceFilter> filters;
ASSERT_FALSE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
}
TEST_F(UsbFilterTest, MatchesAnyVendorId) {
std::vector<UsbDeviceFilter> filters(1);
filters.back().SetVendorId(0x18d1);
ASSERT_TRUE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
}
TEST_F(UsbFilterTest, MatchesAnyVendorIdNegative) {
std::vector<UsbDeviceFilter> filters(1);
filters.back().SetVendorId(0x1d6b);
ASSERT_FALSE(UsbDeviceFilter::MatchesAny(android_phone_, filters));
}
...@@ -52,6 +52,7 @@ using usb::TransferType; ...@@ -52,6 +52,7 @@ using usb::TransferType;
using usb::UsageType; using usb::UsageType;
using usb_service::UsbConfigDescriptor; using usb_service::UsbConfigDescriptor;
using usb_service::UsbDevice; using usb_service::UsbDevice;
using usb_service::UsbDeviceFilter;
using usb_service::UsbDeviceHandle; using usb_service::UsbDeviceHandle;
using usb_service::UsbEndpointDescriptor; using usb_service::UsbEndpointDescriptor;
using usb_service::UsbEndpointDirection; using usb_service::UsbEndpointDirection;
...@@ -417,35 +418,51 @@ bool UsbAsyncApiFunction::Respond() { ...@@ -417,35 +418,51 @@ bool UsbAsyncApiFunction::Respond() {
return error_.empty(); return error_.empty();
} }
scoped_refptr<UsbDevice> UsbAsyncApiFunction::GetDeviceOrOrCompleteWithError( // static
const Device& input_device) { void UsbAsyncApiFunction::CreateDeviceFilter(const usb::DeviceFilter& input,
const uint16_t vendor_id = input_device.vendor_id; UsbDeviceFilter* output) {
const uint16_t product_id = input_device.product_id; if (input.vendor_id) {
UsbDevicePermission::CheckParam param( output->SetVendorId(*input.vendor_id);
vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE); }
if (!extension()->permissions_data()->CheckAPIPermissionWithParam( if (input.product_id) {
APIPermission::kUsbDevice, &param)) { output->SetProductId(*input.product_id);
LOG(WARNING) << "Insufficient permissions to access device."; }
CompleteWithError(kErrorPermissionDenied); if (input.interface_class) {
return NULL; output->SetInterfaceClass(*input.interface_class);
} }
if (input.interface_subclass) {
output->SetInterfaceSubclass(*input.interface_subclass);
}
if (input.interface_protocol) {
output->SetInterfaceProtocol(*input.interface_protocol);
}
}
bool UsbAsyncApiFunction::HasDevicePermission(
scoped_refptr<usb_service::UsbDevice> device) {
UsbDevicePermission::CheckParam param(
device->vendor_id(),
device->product_id(),
UsbDevicePermissionData::UNSPECIFIED_INTERFACE);
return extension()->permissions_data()->CheckAPIPermissionWithParam(
APIPermission::kUsbDevice, &param);
}
scoped_refptr<UsbDevice> UsbAsyncApiFunction::GetDeviceOrCompleteWithError(
const Device& input_device) {
UsbService* service = device::DeviceClient::Get()->GetUsbService(); UsbService* service = device::DeviceClient::Get()->GetUsbService();
if (!service) { if (!service) {
CompleteWithError(kErrorInitService); CompleteWithError(kErrorInitService);
return NULL; return NULL;
} }
scoped_refptr<UsbDevice> device;
device = service->GetDeviceById(input_device.device);
scoped_refptr<UsbDevice> device = service->GetDeviceById(input_device.device);
if (!device.get()) { if (!device.get()) {
CompleteWithError(kErrorNoDevice); CompleteWithError(kErrorNoDevice);
return NULL; return NULL;
} }
if (device->vendor_id() != input_device.vendor_id || if (!HasDevicePermission(device)) {
device->product_id() != input_device.product_id) {
// Must act as if there is no such a device. // Must act as if there is no such a device.
// Otherwise can be used to finger print unauthorized devices. // Otherwise can be used to finger print unauthorized devices.
CompleteWithError(kErrorNoDevice); CompleteWithError(kErrorNoDevice);
...@@ -624,17 +641,20 @@ bool UsbGetDevicesFunction::Prepare() { ...@@ -624,17 +641,20 @@ bool UsbGetDevicesFunction::Prepare() {
} }
void UsbGetDevicesFunction::AsyncWorkStart() { void UsbGetDevicesFunction::AsyncWorkStart() {
scoped_ptr<base::ListValue> result(new base::ListValue()); std::vector<UsbDeviceFilter> filters;
if (parameters_->options.filters) {
const uint16_t vendor_id = parameters_->options.vendor_id; filters.resize(parameters_->options.filters->size());
const uint16_t product_id = parameters_->options.product_id; for (size_t i = 0; i < parameters_->options.filters->size(); ++i) {
UsbDevicePermission::CheckParam param( CreateDeviceFilter(*parameters_->options.filters->at(i).get(),
vendor_id, product_id, UsbDevicePermissionData::UNSPECIFIED_INTERFACE); &filters[i]);
if (!extension()->permissions_data()->CheckAPIPermissionWithParam( }
APIPermission::kUsbDevice, &param)) { }
LOG(WARNING) << "Insufficient permissions to access device."; if (parameters_->options.vendor_id) {
CompleteWithError(kErrorPermissionDenied); filters.resize(filters.size() + 1);
return; filters.back().SetVendorId(*parameters_->options.vendor_id);
if (parameters_->options.product_id) {
filters.back().SetProductId(*parameters_->options.product_id);
}
} }
UsbService* service = device::DeviceClient::Get()->GetUsbService(); UsbService* service = device::DeviceClient::Get()->GetUsbService();
...@@ -646,18 +666,15 @@ void UsbGetDevicesFunction::AsyncWorkStart() { ...@@ -646,18 +666,15 @@ void UsbGetDevicesFunction::AsyncWorkStart() {
DeviceVector devices; DeviceVector devices;
service->GetDevices(&devices); service->GetDevices(&devices);
for (DeviceVector::iterator it = devices.begin(); it != devices.end();) { scoped_ptr<base::ListValue> result(new base::ListValue());
if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) { for (DeviceVector::iterator it = devices.begin(); it != devices.end(); ++it) {
it = devices.erase(it); scoped_refptr<UsbDevice> device = *it;
} else { if ((filters.empty() || UsbDeviceFilter::MatchesAny(device, filters)) &&
++it; HasDevicePermission(device)) {
result->Append(PopulateDevice(it->get()));
} }
} }
for (size_t i = 0; i < devices.size(); ++i) {
result->Append(PopulateDevice(devices[i].get()));
}
SetResult(result.release()); SetResult(result.release());
AsyncWorkCompleted(); AsyncWorkCompleted();
} }
...@@ -677,7 +694,7 @@ bool UsbRequestAccessFunction::Prepare() { ...@@ -677,7 +694,7 @@ bool UsbRequestAccessFunction::Prepare() {
void UsbRequestAccessFunction::AsyncWorkStart() { void UsbRequestAccessFunction::AsyncWorkStart() {
#if defined(OS_CHROMEOS) #if defined(OS_CHROMEOS)
scoped_refptr<UsbDevice> device = scoped_refptr<UsbDevice> device =
GetDeviceOrOrCompleteWithError(parameters_->device); GetDeviceOrCompleteWithError(parameters_->device);
if (!device) if (!device)
return; return;
...@@ -709,7 +726,7 @@ bool UsbOpenDeviceFunction::Prepare() { ...@@ -709,7 +726,7 @@ bool UsbOpenDeviceFunction::Prepare() {
void UsbOpenDeviceFunction::AsyncWorkStart() { void UsbOpenDeviceFunction::AsyncWorkStart() {
scoped_refptr<UsbDevice> device = scoped_refptr<UsbDevice> device =
GetDeviceOrOrCompleteWithError(parameters_->device); GetDeviceOrCompleteWithError(parameters_->device);
if (!device.get()) if (!device.get())
return; return;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "components/usb_service/usb_device.h" #include "components/usb_service/usb_device.h"
#include "components/usb_service/usb_device_filter.h"
#include "components/usb_service/usb_device_handle.h" #include "components/usb_service/usb_device_handle.h"
#include "extensions/browser/api/api_resource_manager.h" #include "extensions/browser/api/api_resource_manager.h"
#include "extensions/browser/api/async_api_function.h" #include "extensions/browser/api/async_api_function.h"
...@@ -31,7 +32,13 @@ class UsbAsyncApiFunction : public AsyncApiFunction { ...@@ -31,7 +32,13 @@ class UsbAsyncApiFunction : public AsyncApiFunction {
virtual bool PrePrepare() OVERRIDE; virtual bool PrePrepare() OVERRIDE;
virtual bool Respond() OVERRIDE; virtual bool Respond() OVERRIDE;
scoped_refptr<usb_service::UsbDevice> GetDeviceOrOrCompleteWithError( static void CreateDeviceFilter(
const extensions::core_api::usb::DeviceFilter& input,
usb_service::UsbDeviceFilter* output);
bool HasDevicePermission(scoped_refptr<usb_service::UsbDevice> device);
scoped_refptr<usb_service::UsbDevice> GetDeviceOrCompleteWithError(
const extensions::core_api::usb::Device& input_device); const extensions::core_api::usb::Device& input_device);
scoped_refptr<usb_service::UsbDeviceHandle> scoped_refptr<usb_service::UsbDeviceHandle>
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "device/usb/usb_ids.h" #include "device/usb/usb_ids.h"
#include "extensions/common/api/usb_private.h" #include "extensions/common/api/usb_private.h"
namespace usb = extensions::core_api::usb;
namespace usb_private = extensions::core_api::usb_private; namespace usb_private = extensions::core_api::usb_private;
namespace GetDevices = usb_private::GetDevices; namespace GetDevices = usb_private::GetDevices;
namespace GetDeviceInfo = usb_private::GetDeviceInfo; namespace GetDeviceInfo = usb_private::GetDeviceInfo;
...@@ -26,6 +27,8 @@ using usb_service::UsbDeviceFilter; ...@@ -26,6 +27,8 @@ using usb_service::UsbDeviceFilter;
using usb_service::UsbDeviceHandle; using usb_service::UsbDeviceHandle;
using usb_service::UsbService; using usb_service::UsbService;
typedef std::vector<scoped_refptr<UsbDevice> > DeviceVector;
namespace { namespace {
const char kErrorInitService[] = "Failed to initialize USB service."; const char kErrorInitService[] = "Failed to initialize USB service.";
...@@ -58,46 +61,16 @@ void UsbPrivateGetDevicesFunction::AsyncWorkStart() { ...@@ -58,46 +61,16 @@ void UsbPrivateGetDevicesFunction::AsyncWorkStart() {
std::vector<UsbDeviceFilter> filters; std::vector<UsbDeviceFilter> filters;
filters.resize(parameters_->filters.size()); filters.resize(parameters_->filters.size());
for (size_t i = 0; i < parameters_->filters.size(); ++i) { for (size_t i = 0; i < parameters_->filters.size(); ++i) {
UsbDeviceFilter& filter = filters[i]; CreateDeviceFilter(*parameters_->filters[i].get(), &filters[i]);
const usb_private::DeviceFilter* filter_param =
parameters_->filters[i].get();
if (filter_param->vendor_id) {
filter.SetVendorId(*filter_param->vendor_id);
}
if (filter_param->product_id) {
filter.SetProductId(*filter_param->product_id);
}
if (filter_param->interface_class) {
filter.SetInterfaceClass(*filter_param->interface_class);
}
if (filter_param->interface_subclass) {
filter.SetInterfaceSubclass(*filter_param->interface_subclass);
}
if (filter_param->interface_protocol) {
filter.SetInterfaceProtocol(*filter_param->interface_protocol);
}
} }
std::vector<scoped_refptr<UsbDevice> > devices; DeviceVector devices;
service->GetDevices(&devices); service->GetDevices(&devices);
scoped_ptr<base::ListValue> result(new base::ListValue()); scoped_ptr<base::ListValue> result(new base::ListValue());
for (size_t i = 0; i < devices.size(); ++i) { for (DeviceVector::iterator it = devices.begin(); it != devices.end(); ++it) {
scoped_refptr<UsbDevice> device = devices[i]; scoped_refptr<UsbDevice> device = *it;
bool matched = false; if (filters.empty() || UsbDeviceFilter::MatchesAny(device, filters)) {
if (filters.empty()) {
matched = true;
} else {
for (size_t j = 0; !matched && j < filters.size(); ++j) {
if (filters[j].Matches(device)) {
matched = true;
}
}
}
if (matched) {
result->Append(new base::FundamentalValue((int)device->unique_id())); result->Append(new base::FundamentalValue((int)device->unique_id()));
} }
} }
......
This diff is collapsed.
...@@ -7,22 +7,6 @@ ...@@ -7,22 +7,6 @@
// API which should only be available to trusted pages. // API which should only be available to trusted pages.
namespace usbPrivate { namespace usbPrivate {
// Properties for matching devices. A device matches of any of its interfaces
// match the given properties. An empty dictionary matches any device.
dictionary DeviceFilter {
// Device-level matching criteria:
long? vendorId;
// Checked only if the vendorId matches.
long? productId;
// Per-interface matching criteria:
long? interfaceClass;
// Checked only if the interfaceClass matches.
long? interfaceSubclass;
// Checked only if the interfaceSubclass matches.
long? interfaceProtocol;
};
dictionary DeviceInfo { dictionary DeviceInfo {
long vendorId; // idVendor from the device long vendorId; // idVendor from the device
long productId; // idProduct from the device long productId; // idProduct from the device
...@@ -44,7 +28,7 @@ namespace usbPrivate { ...@@ -44,7 +28,7 @@ namespace usbPrivate {
// Lists USB devices matching any of the given filters. // Lists USB devices matching any of the given filters.
// |filters|: The properties to search for on target devices. // |filters|: The properties to search for on target devices.
// |callback|: Invoked with a list of device IDs on complete. // |callback|: Invoked with a list of device IDs on complete.
static void getDevices(DeviceFilter[] filters, static void getDevices(usb.DeviceFilter[] filters,
GetDevicesCallback callback); GetDevicesCallback callback);
// Gets basic display information about a device. // Gets basic display information about a device.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment