Commit 43c5c906 authored by reillyg's avatar reillyg Committed by Commit bot

Move string descriptor getters from UsbDeviceHandle to UsbDevice.

The common string descriptors: iManufacturer, iProduct and iSerialNumber
should be accessible without opening the device. On Linux these can be
read out of sysfs without having access to the usbfs device node. This
is critical on Chrome OS because otherwise the permission broker needs
to be asked for permission to access the device. On other platforms we
fall back to opening the device temporarily. This will stop being the
case on OS X when libusb is no longer used and on Windows this will be
part of the initial enumeration process.

BUG=
TBR=dgozman@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#296802}
parent d0956cf3
......@@ -158,11 +158,7 @@ bool SavedDevicesService::SavedDevices::IsRegistered(
continue;
}
if (!have_serial_number) {
scoped_refptr<UsbDeviceHandle> device_handle = device->Open();
if (!device_handle.get()) {
break;
}
if (!device_handle->GetSerial(&serial_number)) {
if (!device->GetSerialNumber(&serial_number)) {
break;
}
have_serial_number = true;
......
......@@ -25,88 +25,27 @@ using device::UsbEndpointDirection;
using device::UsbTransferCallback;
using testing::Return;
class MockUsbDeviceHandle : public UsbDeviceHandle {
public:
MockUsbDeviceHandle(const std::string& serial_number)
: UsbDeviceHandle(), serial_number_(serial_number) {}
MOCK_CONST_METHOD0(GetDevice, scoped_refptr<UsbDevice>());
MOCK_METHOD0(Close, void());
MOCK_METHOD10(ControlTransfer,
void(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
uint8 request,
uint16 value,
uint16 index,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(BulkTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD6(InterruptTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD8(IsochronousTransfer,
void(UsbEndpointDirection direction,
uint8 endpoint,
net::IOBuffer* buffer,
size_t length,
unsigned int packets,
unsigned int packet_length,
unsigned int timeout,
const UsbTransferCallback& callback));
MOCK_METHOD0(ResetDevice, bool());
MOCK_METHOD1(ClaimInterface, bool(int interface_number));
MOCK_METHOD1(ReleaseInterface, bool(int interface_number));
MOCK_METHOD2(SetInterfaceAlternateSetting,
bool(int interface_number, int alternate_setting));
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
bool GetSerial(base::string16* serial) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
*serial = base::UTF8ToUTF16(serial_number_);
return true;
}
private:
virtual ~MockUsbDeviceHandle() {}
const std::string serial_number_;
};
class MockUsbDevice : public UsbDevice {
public:
MockUsbDevice(const std::string& serial_number, uint32 unique_id)
: UsbDevice(0, 0, unique_id), serial_number_(serial_number) {}
MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>());
MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>));
#if defined(OS_CHROMEOS)
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif
MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, bool(base::string16*));
bool GetSerialNumber(base::string16* serial) OVERRIDE {
if (serial_number_.empty()) {
return false;
}
scoped_refptr<UsbDeviceHandle> Open() OVERRIDE {
return new MockUsbDeviceHandle(serial_number_);
*serial = base::UTF8ToUTF16(serial_number_);
return true;
}
void NotifyDisconnect() { UsbDevice::NotifyDisconnect(); }
......
......@@ -144,20 +144,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
}
virtual bool ResetDevice() OVERRIDE { return true; }
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE {
*manufacturer = base::UTF8ToUTF16(kDeviceManufacturer);
return true;
}
virtual bool GetProduct(base::string16* product) OVERRIDE {
*product = base::UTF8ToUTF16(kDeviceModel);
return true;
}
virtual bool GetSerial(base::string16* serial) OVERRIDE {
*serial = base::UTF8ToUTF16(kDeviceSerial);
return true;
virtual bool GetStringDescriptor(uint8_t string_id, base::string16* content) {
return false;
}
// Async IO. Can be called on any thread.
......@@ -379,6 +367,21 @@ class MockUsbDevice : public UsbDevice {
return config_desc_;
}
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE {
*manufacturer = base::UTF8ToUTF16(kDeviceManufacturer);
return true;
}
virtual bool GetProduct(base::string16* product) OVERRIDE {
*product = base::UTF8ToUTF16(kDeviceModel);
return true;
}
virtual bool GetSerialNumber(base::string16* serial) OVERRIDE {
*serial = base::UTF8ToUTF16(kDeviceSerial);
return true;
}
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE {
return true;
}
......
......@@ -96,7 +96,7 @@ scoped_refptr<AndroidUsbDevice> ClaimInterface(
return NULL;
base::string16 serial;
if (!usb_handle->GetSerial(&serial) || serial.empty())
if (!usb_handle->GetDevice()->GetSerialNumber(&serial) || serial.empty())
return NULL;
return new AndroidUsbDevice(rsa_key,
......
......@@ -100,7 +100,7 @@ class HidConnectionTest : public testing::Test {
for (std::vector<HidDeviceInfo>::iterator it = devices.begin();
it != devices.end();
++it) {
if (it->serial_number == test_gadget_->GetSerial()) {
if (it->serial_number == test_gadget_->GetSerialNumber()) {
device_id_ = it->device_id;
break;
}
......
......@@ -35,7 +35,7 @@ class UsbTestGadget {
virtual bool SetType(Type type) = 0;
virtual UsbDevice* GetDevice() const = 0;
virtual std::string GetSerial() const = 0;
virtual std::string GetSerialNumber() const = 0;
protected:
UsbTestGadget() {}
......
......@@ -71,7 +71,7 @@ class UsbTestGadgetImpl : public UsbTestGadget {
virtual bool Reconnect() OVERRIDE;
virtual bool SetType(Type type) OVERRIDE;
virtual UsbDevice* GetDevice() const OVERRIDE;
virtual std::string GetSerial() const OVERRIDE;
virtual std::string GetSerialNumber() const OVERRIDE;
protected:
UsbTestGadgetImpl();
......@@ -166,7 +166,7 @@ UsbDevice* UsbTestGadgetImpl::GetDevice() const {
return device_.get();
}
std::string UsbTestGadgetImpl::GetSerial() const {
std::string UsbTestGadgetImpl::GetSerialNumber() const {
return device_address_;
}
......@@ -205,13 +205,8 @@ bool UsbTestGadgetImpl::FindUnclaimed() {
devices.begin(); iter != devices.end(); ++iter) {
const scoped_refptr<UsbDevice> &device = *iter;
if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) {
scoped_refptr<UsbDeviceHandle> handle = device->Open();
if (handle.get() == NULL) {
continue;
}
base::string16 serial_utf16;
if (!handle->GetSerial(&serial_utf16)) {
if (!device->GetSerialNumber(&serial_utf16)) {
continue;
}
......@@ -340,7 +335,7 @@ bool UsbTestGadgetImpl::Update() {
bool UsbTestGadgetImpl::FindClaimed() {
CHECK(!device_.get());
std::string expected_serial = GetSerial();
std::string expected_serial = GetSerialNumber();
std::vector<scoped_refptr<UsbDevice> > devices;
usb_service_->GetDevices(&devices);
......@@ -362,13 +357,8 @@ bool UsbTestGadgetImpl::FindClaimed() {
continue;
}
scoped_refptr<UsbDeviceHandle> handle(device->Open());
if (handle.get() == NULL) {
continue;
}
base::string16 serial_utf16;
if (!handle->GetSerial(&serial_utf16)) {
if (!device->GetSerialNumber(&serial_utf16)) {
continue;
}
......
......@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/observer_list.h"
#include "base/strings/string16.h"
namespace device {
......@@ -54,6 +55,18 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> {
// Blocking method. Must be called on FILE thread.
virtual const UsbConfigDescriptor& GetConfiguration() = 0;
// Gets the manufacturer string of the device, or returns false.
// Blocking method. Must be called on FILE thread.
virtual bool GetManufacturer(base::string16* manufacturer) = 0;
// Gets the product string of the device, or returns false.
// Blocking method. Must be called on FILE thread.
virtual bool GetProduct(base::string16* product) = 0;
// Gets the serial number string of the device, or returns false.
// Blocking method. Must be called on FILE thread.
virtual bool GetSerialNumber(base::string16* serial) = 0;
void AddObserver(Observer* obs) { observer_list_.AddObserver(obs); }
void RemoveObserver(Observer* obs) { observer_list_.RemoveObserver(obs); }
......
......@@ -30,6 +30,9 @@ class MockUsbDevice : public UsbDevice {
MOCK_METHOD2(RequestUsbAccess, void(int, const base::Callback<void(bool)>&));
#endif
MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16*));
MOCK_METHOD1(GetProduct, bool(base::string16*));
MOCK_METHOD1(GetSerialNumber, bool(base::string16*));
private:
virtual ~MockUsbDevice() {}
......
......@@ -56,9 +56,10 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> {
virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) = 0;
virtual bool ResetDevice() = 0;
virtual bool GetManufacturer(base::string16* manufacturer) = 0;
virtual bool GetProduct(base::string16* product) = 0;
virtual bool GetSerial(base::string16* serial) = 0;
// Gets the string descriptor with the given index from the device, or returns
// false. This method is blocking and must be called on the FILE thread.
virtual bool GetStringDescriptor(uint8 string_id, base::string16* string) = 0;
// Async IO. Can be called on any thread.
virtual void ControlTransfer(UsbEndpointDirection direction,
......
This diff is collapsed.
......@@ -40,9 +40,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
virtual bool SetInterfaceAlternateSetting(int interface_number,
int alternate_setting) OVERRIDE;
virtual bool ResetDevice() OVERRIDE;
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
virtual bool GetProduct(base::string16* product) OVERRIDE;
virtual bool GetSerial(base::string16* serial) OVERRIDE;
virtual bool GetStringDescriptor(uint8 string_id,
base::string16* string) OVERRIDE;
virtual void ControlTransfer(UsbEndpointDirection direction,
TransferRequestType request_type,
TransferRecipient recipient,
......@@ -132,7 +132,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle {
void CompleteTransfer(PlatformUsbTransferHandle transfer);
bool GetSupportedLanguages();
bool GetStringDescriptor(uint8 string_id, base::string16* string);
// Informs the object to drop internal references.
void InternalClose();
......
......@@ -10,6 +10,8 @@
#include "base/location.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/thread_task_runner_handle.h"
#include "device/usb/usb_context.h"
#include "device/usb/usb_descriptors.h"
......@@ -23,6 +25,10 @@
#include "chromeos/dbus/permission_broker_client.h"
#endif // defined(OS_CHROMEOS)
#if defined(OS_LINUX)
#include "device/udev_linux/udev.h"
#endif // defined(OS_LINUX)
namespace device {
namespace {
......@@ -123,6 +129,44 @@ UsbDeviceImpl::UsbDeviceImpl(
ui_task_runner_(ui_task_runner) {
CHECK(platform_device) << "platform_device cannot be NULL";
libusb_ref_device(platform_device);
#if defined(OS_LINUX)
ScopedUdevPtr udev(udev_new());
ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev.get()));
udev_enumerate_add_match_subsystem(enumerate.get(), "usb");
if (udev_enumerate_scan_devices(enumerate.get()) != 0) {
return;
}
std::string bus_number =
base::IntToString(libusb_get_bus_number(platform_device));
std::string device_address =
base::IntToString(libusb_get_device_address(platform_device));
udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get());
for (udev_list_entry* i = devices; i != NULL;
i = udev_list_entry_get_next(i)) {
ScopedUdevDevicePtr device(
udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i)));
if (device) {
const char* value = udev_device_get_sysattr_value(device.get(), "busnum");
if (!value || bus_number != value) {
continue;
}
value = udev_device_get_sysattr_value(device.get(), "devnum");
if (!value || device_address != value) {
continue;
}
value = udev_device_get_sysattr_value(device.get(), "manufacturer");
manufacturer_ = value ? value : "";
value = udev_device_get_sysattr_value(device.get(), "product");
product_ = value ? value : "";
value = udev_device_get_sysattr_value(device.get(), "serial");
serial_number_ = value ? value : "";
break;
}
}
#endif
}
UsbDeviceImpl::~UsbDeviceImpl() {
......@@ -275,6 +319,100 @@ const UsbConfigDescriptor& UsbDeviceImpl::GetConfiguration() {
return current_configuration_;
}
bool UsbDeviceImpl::GetManufacturer(base::string16* manufacturer) {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_LINUX)
if (manufacturer_.empty()) {
return false;
}
*manufacturer = base::UTF8ToUTF16(manufacturer_);
return true;
#else
// This is a non-blocking call as libusb has the descriptor in memory.
libusb_device_descriptor desc;
const int rv = libusb_get_device_descriptor(platform_device_, &desc);
if (rv != LIBUSB_SUCCESS) {
VLOG(1) << "Failed to read device descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
return false;
}
if (desc.iManufacturer == 0) {
return false;
}
scoped_refptr<UsbDeviceHandle> device_handle = Open();
if (device_handle.get()) {
return device_handle->GetStringDescriptor(desc.iManufacturer, manufacturer);
}
return false;
#endif
}
bool UsbDeviceImpl::GetProduct(base::string16* product) {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_LINUX)
if (product_.empty()) {
return false;
}
*product = base::UTF8ToUTF16(product_);
return true;
#else
// This is a non-blocking call as libusb has the descriptor in memory.
libusb_device_descriptor desc;
const int rv = libusb_get_device_descriptor(platform_device_, &desc);
if (rv != LIBUSB_SUCCESS) {
VLOG(1) << "Failed to read device descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
return false;
}
if (desc.iProduct == 0) {
return false;
}
scoped_refptr<UsbDeviceHandle> device_handle = Open();
if (device_handle.get()) {
return device_handle->GetStringDescriptor(desc.iProduct, product);
}
return false;
#endif
}
bool UsbDeviceImpl::GetSerialNumber(base::string16* serial_number) {
DCHECK(thread_checker_.CalledOnValidThread());
#if defined(OS_LINUX)
if (serial_number_.empty()) {
return false;
}
*serial_number = base::UTF8ToUTF16(serial_number_);
return true;
#else
// This is a non-blocking call as libusb has the descriptor in memory.
libusb_device_descriptor desc;
const int rv = libusb_get_device_descriptor(platform_device_, &desc);
if (rv != LIBUSB_SUCCESS) {
VLOG(1) << "Failed to read device descriptor: "
<< ConvertPlatformUsbErrorToString(rv);
return false;
}
if (desc.iSerialNumber == 0) {
return false;
}
scoped_refptr<UsbDeviceHandle> device_handle = Open();
if (device_handle.get()) {
return device_handle->GetStringDescriptor(desc.iSerialNumber,
serial_number);
}
return false;
#endif
}
void UsbDeviceImpl::OnDisconnect() {
DCHECK(thread_checker_.CalledOnValidThread());
HandlesVector handles;
......
......@@ -39,6 +39,9 @@ class UsbDeviceImpl : public UsbDevice {
virtual scoped_refptr<UsbDeviceHandle> Open() OVERRIDE;
virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) OVERRIDE;
virtual const UsbConfigDescriptor& GetConfiguration() OVERRIDE;
virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE;
virtual bool GetProduct(base::string16* product) OVERRIDE;
virtual bool GetSerialNumber(base::string16* serial_number) OVERRIDE;
protected:
friend class UsbServiceImpl;
......@@ -60,6 +63,15 @@ class UsbDeviceImpl : public UsbDevice {
base::ThreadChecker thread_checker_;
PlatformUsbDevice platform_device_;
#if defined(OS_LINUX)
// On Linux these properties are read from sysfs when the device is enumerated
// to avoid hitting the permission broker on Chrome OS for a real string
// descriptor request.
std::string manufacturer_;
std::string product_;
std::string serial_number_;
#endif
// The active configuration descriptor is not read immediately but cached for
// later use.
bool current_configuration_cached_;
......
......@@ -29,23 +29,16 @@ TEST_F(UsbServiceTest, ClaimGadget) {
scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim();
ASSERT_TRUE(gadget.get());
scoped_refptr<UsbDeviceHandle> handle = gadget->GetDevice()->Open();
scoped_refptr<UsbDevice> device = gadget->GetDevice();
base::string16 utf16;
ASSERT_TRUE(handle->GetManufacturer(&utf16));
ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_TRUE(device->GetManufacturer(&utf16));
ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16));
ASSERT_TRUE(handle->GetProduct(&utf16));
ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_TRUE(device->GetProduct(&utf16));
ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16));
ASSERT_TRUE(handle->GetSerial(&utf16));
ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16));
// Check again to make sure string descriptor caching works.
ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16));
ASSERT_TRUE(device->GetSerialNumber(&utf16));
ASSERT_EQ(gadget->GetSerialNumber(), base::UTF16ToUTF8(utf16));
}
TEST_F(UsbServiceTest, DisconnectAndReconnect) {
......
......@@ -87,13 +87,11 @@ class MockUsbDeviceHandle : public UsbDeviceHandle {
const UsbTransferCallback& callback));
MOCK_METHOD0(ResetDevice, bool());
MOCK_METHOD2(GetStringDescriptor, bool(uint8_t, base::string16*));
MOCK_METHOD1(ClaimInterface, bool(const int interface_number));
MOCK_METHOD1(ReleaseInterface, bool(const int interface_number));
MOCK_METHOD2(SetInterfaceAlternateSetting,
bool(const int interface_number, const int alternate_setting));
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
MOCK_METHOD1(GetSerial, bool(base::string16* serial));
virtual scoped_refptr<UsbDevice> GetDevice() const OVERRIDE {
return device_;
......@@ -133,6 +131,9 @@ class MockUsbDevice : public UsbDevice {
#endif // OS_CHROMEOS
MOCK_METHOD0(GetConfiguration, const UsbConfigDescriptor&());
MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer));
MOCK_METHOD1(GetProduct, bool(base::string16* product));
MOCK_METHOD1(GetSerialNumber, bool(base::string16* serial_number));
private:
MockUsbDeviceHandle* mock_handle_;
......
......@@ -33,7 +33,6 @@ namespace {
const char kErrorInitService[] = "Failed to initialize USB service.";
const char kErrorNoDevice[] = "No such device.";
const char kErrorOpen[] = "Failed to open device.";
} // namespace
......@@ -120,23 +119,17 @@ void UsbPrivateGetDeviceInfoFunction::AsyncWorkStart() {
device_info.product_name.reset(new std::string(name));
}
scoped_refptr<UsbDeviceHandle> device_handle = device->Open();
if (!device_handle.get()) {
CompleteWithError(kErrorOpen);
return;
}
base::string16 utf16;
if (device_handle->GetManufacturer(&utf16)) {
if (device->GetManufacturer(&utf16)) {
device_info.manufacturer_string.reset(
new std::string(base::UTF16ToUTF8(utf16)));
}
if (device_handle->GetProduct(&utf16)) {
if (device->GetProduct(&utf16)) {
device_info.product_string.reset(new std::string(base::UTF16ToUTF8(utf16)));
}
if (device_handle->GetSerial(&utf16)) {
if (device->GetSerialNumber(&utf16)) {
device_info.serial_string.reset(new std::string(base::UTF16ToUTF8(utf16)));
}
......
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