diff options
36 files changed, 2887 insertions, 2869 deletions
diff --git a/chrome/browser/chrome_device_client.cc b/chrome/browser/chrome_device_client.cc index f93fb90..835a4c3 100644 --- a/chrome/browser/chrome_device_client.cc +++ b/chrome/browser/chrome_device_client.cc @@ -9,18 +9,20 @@ #include "device/hid/hid_service.h" #include "device/usb/usb_service.h" +using content::BrowserThread; + ChromeDeviceClient::ChromeDeviceClient() {} ChromeDeviceClient::~ChromeDeviceClient() {} device::UsbService* ChromeDeviceClient::GetUsbService() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); return device::UsbService::GetInstance( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI)); + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); } device::HidService* ChromeDeviceClient::GetHidService() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); return device::HidService::GetInstance( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::FILE)); + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); } diff --git a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc index c7f99ed..1eab6e8 100644 --- a/chrome/browser/devtools/device/usb/android_usb_browsertest.cc +++ b/chrome/browser/devtools/device/usb/android_usb_browsertest.cc @@ -29,7 +29,6 @@ using device::UsbEndpointDirection; using device::UsbInterfaceDescriptor; using device::UsbService; using device::UsbSynchronizationType; -using device::UsbTransferCallback; using device::UsbTransferType; using device::UsbUsageType; @@ -127,15 +126,22 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { virtual void Close() override { device_ = nullptr; } - bool SetConfiguration(int configuration_value) override { return true; } + void SetConfiguration(int configuration_value, + const ResultCallback& callback) override { + NOTIMPLEMENTED(); + } - bool ClaimInterface(int interface_number) override { - if (device_->claimed_interfaces_.find(interface_number) != - device_->claimed_interfaces_.end()) - return false; + void ClaimInterface(int interface_number, + const ResultCallback& callback) override { + bool success = false; + if (device_->claimed_interfaces_.find(interface_number) == + device_->claimed_interfaces_.end()) { + device_->claimed_interfaces_.insert(interface_number); + success = true; + } - device_->claimed_interfaces_.insert(interface_number); - return true; + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(callback, success)); } bool ReleaseInterface(int interface_number) override { @@ -147,35 +153,34 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { return true; } - virtual bool SetInterfaceAlternateSetting(int interface_number, - int alternate_setting) override { - return true; + void SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + const ResultCallback& callback) override { + NOTIMPLEMENTED(); } - virtual bool ResetDevice() override { return true; } - bool GetStringDescriptor(uint8_t string_id, - base::string16* content) override { - return false; + void ResetDevice(const ResultCallback& callback) override { + NOTIMPLEMENTED(); } // Async IO. Can be called on any thread. - virtual void ControlTransfer(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) override {} - - virtual void BulkTransfer(UsbEndpointDirection direction, - uint8 endpoint, - net::IOBuffer* buffer, - size_t length, - unsigned int timeout, - const UsbTransferCallback& callback) override { + void ControlTransfer(UsbEndpointDirection direction, + TransferRequestType request_type, + TransferRecipient recipient, + uint8 request, + uint16 value, + uint16 index, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) override {} + + void BulkTransfer(UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) override { if (direction == device::USB_DIRECTION_OUTBOUND) { if (remaining_body_length_ == 0) { std::vector<uint32> header(6); @@ -201,11 +206,10 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { device::UsbTransferStatus status = broken_ ? device::USB_TRANSFER_ERROR : device::USB_TRANSFER_COMPLETED; base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(callback, status, scoped_refptr<net::IOBuffer>(), 0)); + FROM_HERE, base::Bind(callback, status, nullptr, 0)); ProcessQueries(); } else if (direction == device::USB_DIRECTION_INBOUND) { - queries_.push(Query(callback, make_scoped_refptr(buffer), length)); + queries_.push(Query(callback, buffer, length)); ProcessQueries(); } } @@ -336,36 +340,34 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { } - virtual void InterruptTransfer(UsbEndpointDirection direction, - uint8 endpoint, - net::IOBuffer* buffer, - size_t length, - unsigned int timeout, - const UsbTransferCallback& callback) override { - } + void InterruptTransfer(UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) override {} - virtual void IsochronousTransfer( - UsbEndpointDirection direction, - uint8 endpoint, - net::IOBuffer* buffer, - size_t length, - unsigned int packets, - unsigned int packet_length, - unsigned int timeout, - const UsbTransferCallback& callback) override {} + void IsochronousTransfer(UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int packets, + unsigned int packet_length, + unsigned int timeout, + const TransferCallback& callback) override {} protected: virtual ~MockUsbDeviceHandle() {} struct Query { - UsbTransferCallback callback; + TransferCallback callback; scoped_refptr<net::IOBuffer> buffer; size_t size; - Query(UsbTransferCallback callback, + Query(TransferCallback callback, scoped_refptr<net::IOBuffer> buffer, int size) - : callback(callback), buffer(buffer), size(size) {}; + : callback(callback), buffer(buffer), size(size) {} }; scoped_refptr<MockUsbDevice<T> > device_; @@ -381,7 +383,13 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { template <class T> class MockUsbDevice : public UsbDevice { public: - MockUsbDevice() : UsbDevice(0, 0, 0) { + MockUsbDevice() + : UsbDevice(0, + 0, + 0, + base::UTF8ToUTF16(kDeviceManufacturer), + base::UTF8ToUTF16(kDeviceModel), + base::UTF8ToUTF16(kDeviceSerial)) { UsbEndpointDescriptor bulk_in; bulk_in.address = 0x81; bulk_in.direction = device::USB_DIRECTION_INBOUND; @@ -406,30 +414,17 @@ class MockUsbDevice : public UsbDevice { config_desc_.interfaces.push_back(interface_desc); } - virtual scoped_refptr<UsbDeviceHandle> Open() override { - return new MockUsbDeviceHandle<T>(this); + void Open(const OpenCallback& callback) override { + base::MessageLoop::current()->PostTask( + FROM_HERE, base::Bind(callback, make_scoped_refptr( + new MockUsbDeviceHandle<T>(this)))); } - virtual const UsbConfigDescriptor* GetConfiguration() override { + const UsbConfigDescriptor* GetConfiguration() override { return T::kConfigured ? &config_desc_ : nullptr; } - 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 { + bool Close(scoped_refptr<UsbDeviceHandle> handle) override { return true; } @@ -453,52 +448,34 @@ class MockUsbService : public UsbService { return nullptr; } - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override { - STLClearObject(devices); - std::copy(devices_.begin(), devices_.end(), back_inserter(*devices)); + void GetDevices(const GetDevicesCallback& callback) override { + callback.Run(devices_); } std::vector<scoped_refptr<UsbDevice> > devices_; }; -class MockBreakingUsbService : public UsbService { +class MockBreakingUsbService : public MockUsbService { public: - scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override { - NOTIMPLEMENTED(); - return nullptr; - } - - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override { - STLClearObject(devices); - devices->push_back(new MockUsbDevice<BreakingAndroidTraits>()); + MockBreakingUsbService() { + devices_.clear(); + devices_.push_back(new MockUsbDevice<BreakingAndroidTraits>()); } }; -class MockNoConfigUsbService : public UsbService { +class MockNoConfigUsbService : public MockUsbService { public: - scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override { - NOTIMPLEMENTED(); - return nullptr; - } - - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override { - STLClearObject(devices); - devices->push_back(new MockUsbDevice<AndroidTraits>()); - devices->push_back(new MockUsbDevice<NoConfigTraits>()); + MockNoConfigUsbService() { + devices_.push_back(new MockUsbDevice<NoConfigTraits>()); } }; -class MockUsbServiceForCheckingTraits : public UsbService { +class MockUsbServiceForCheckingTraits : public MockUsbService { public: MockUsbServiceForCheckingTraits() : step_(0) {} - scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override { - NOTIMPLEMENTED(); - return nullptr; - } - - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override { - STLClearObject(devices); + void GetDevices(const GetDevicesCallback& callback) override { + std::vector<scoped_refptr<UsbDevice>> devices; // This switch should be kept in sync with // AndroidUsbBrowserTest::DeviceCountChanged. switch (step_) { @@ -507,19 +484,20 @@ class MockUsbServiceForCheckingTraits : public UsbService { break; case 1: // Android device. - devices->push_back(new MockUsbDevice<AndroidTraits>()); + devices.push_back(new MockUsbDevice<AndroidTraits>()); break; case 2: // Android and non-android device. - devices->push_back(new MockUsbDevice<AndroidTraits>()); - devices->push_back(new MockUsbDevice<NonAndroidTraits>()); + devices.push_back(new MockUsbDevice<AndroidTraits>()); + devices.push_back(new MockUsbDevice<NonAndroidTraits>()); break; case 3: // Non-android device. - devices->push_back(new MockUsbDevice<NonAndroidTraits>()); + devices.push_back(new MockUsbDevice<NonAndroidTraits>()); break; } step_++; + callback.Run(devices); } private: @@ -548,15 +526,7 @@ class AndroidUsbDiscoveryTest : public InProcessBrowserTest { : scheduler_invoked_(0) { } void SetUpOnMainThread() override { - scoped_refptr<content::MessageLoopRunner> runner = - new content::MessageLoopRunner; - - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&AndroidUsbDiscoveryTest::SetUpService, this), - runner->QuitClosure()); - runner->Run(); + mock_usb_service_.reset(CreateMockService()); adb_bridge_ = DevToolsAndroidBridge::Factory::GetForProfile(browser()->profile()); @@ -579,23 +549,10 @@ class AndroidUsbDiscoveryTest : public InProcessBrowserTest { BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, request); } - virtual void SetUpService() { - UsbService::SetInstanceForTest(new MockUsbService()); - } - - void TearDownOnMainThread() override { - scoped_refptr<content::MessageLoopRunner> runner = - new content::MessageLoopRunner; - UsbService* service = nullptr; - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&UsbService::SetInstanceForTest, service), - runner->QuitClosure()); - runner->Run(); - } + virtual MockUsbService* CreateMockService() { return new MockUsbService(); } scoped_refptr<content::MessageLoopRunner> runner_; + scoped_ptr<MockUsbService> mock_usb_service_; DevToolsAndroidBridge* adb_bridge_; int scheduler_invoked_; }; @@ -613,22 +570,22 @@ class AndroidUsbCountTest : public AndroidUsbDiscoveryTest { class AndroidUsbTraitsTest : public AndroidUsbDiscoveryTest { protected: - void SetUpService() override { - UsbService::SetInstanceForTest(new MockUsbServiceForCheckingTraits()); + MockUsbService* CreateMockService() override { + return new MockUsbServiceForCheckingTraits(); } }; class AndroidBreakingUsbTest : public AndroidUsbDiscoveryTest { protected: - void SetUpService() override { - UsbService::SetInstanceForTest(new MockBreakingUsbService()); + MockUsbService* CreateMockService() override { + return new MockBreakingUsbService(); } }; class AndroidNoConfigUsbTest : public AndroidUsbDiscoveryTest { protected: - void SetUpService() override { - UsbService::SetInstanceForTest(new MockNoConfigUsbService()); + MockUsbService* CreateMockService() override { + return new MockNoConfigUsbService(); } }; @@ -662,10 +619,7 @@ class MockListListener : public DevToolsAndroidBridge::DeviceListListener { class MockCountListener : public DevToolsAndroidBridge::DeviceCountListener { public: explicit MockCountListener(DevToolsAndroidBridge* adb_bridge) - : adb_bridge_(adb_bridge), - reposts_left_(10), - invoked_(0) { - } + : adb_bridge_(adb_bridge), invoked_(0) {} void DeviceCountChanged(int count) override { ++invoked_; @@ -673,33 +627,9 @@ class MockCountListener : public DevToolsAndroidBridge::DeviceCountListener { Shutdown(); } - void Shutdown() { - ShutdownOnUIThread(); - }; - - void ShutdownOnUIThread() { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (reposts_left_-- == 0) { - base::MessageLoop::current()->Quit(); - } else { - BrowserThread::PostTask( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&MockCountListener::ShutdownOnFileThread, - base::Unretained(this))); - } - } - - void ShutdownOnFileThread() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(&MockCountListener::ShutdownOnUIThread, - base::Unretained(this))); - } + void Shutdown() { base::MessageLoop::current()->Quit(); } DevToolsAndroidBridge* adb_bridge_; - int reposts_left_; int invoked_; }; @@ -820,6 +750,7 @@ IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, runner_->Run(); EXPECT_EQ(1, listener.invoked_); EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); } IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, @@ -829,6 +760,7 @@ IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, runner_->Run(); EXPECT_EQ(3, listener.invoked_); EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); } IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, @@ -840,6 +772,7 @@ IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, runner_->Run(); EXPECT_EQ(1, listener.invoked_); EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); } IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, @@ -849,6 +782,7 @@ IN_PROC_BROWSER_TEST_F(AndroidUsbCountTest, runner_->Run(); EXPECT_EQ(2, listener.invoked_); EXPECT_EQ(listener.invoked_ - 1, scheduler_invoked_); + EXPECT_TRUE(base::MessageLoop::current()->IsIdleForTesting()); } IN_PROC_BROWSER_TEST_F(AndroidUsbTraitsTest, TestDeviceCounting) { diff --git a/chrome/browser/devtools/device/usb/android_usb_device.cc b/chrome/browser/devtools/device/usb/android_usb_device.cc index 9e5d700..5da4458 100644 --- a/chrome/browser/devtools/device/usb/android_usb_device.cc +++ b/chrome/browser/devtools/device/usb/android_usb_device.cc @@ -9,11 +9,12 @@ #include "base/barrier_closure.h" #include "base/base64.h" #include "base/lazy_instance.h" -#include "base/message_loop/message_loop.h" +#include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/thread_task_runner_handle.h" #include "chrome/browser/devtools/device/usb/android_rsa.h" #include "chrome/browser/devtools/device/usb/android_usb_socket.h" #include "content/public/browser/browser_thread.h" @@ -69,38 +70,22 @@ bool IsAndroidInterface(const UsbInterfaceDescriptor& interface) { return true; } -scoped_refptr<AndroidUsbDevice> ClaimInterface( - crypto::RSAPrivateKey* rsa_key, - scoped_refptr<UsbDeviceHandle> usb_handle, - const base::string16& serial, - const UsbInterfaceDescriptor& interface) { - int inbound_address = 0; - int outbound_address = 0; - int zero_mask = 0; - - for (const UsbEndpointDescriptor& endpoint : interface.endpoints) { - if (endpoint.transfer_type != device::USB_TRANSFER_BULK) - continue; - if (endpoint.direction == device::USB_DIRECTION_INBOUND) - inbound_address = endpoint.address; - else - outbound_address = endpoint.address; - zero_mask = endpoint.maximum_packet_size - 1; +void CountAndroidDevices(const base::Callback<void(int)>& callback, + const UsbDevices& devices) { + int device_count = 0; + for (const scoped_refptr<UsbDevice>& device : devices) { + const UsbConfigDescriptor* config = device->GetConfiguration(); + if (config) { + for (const UsbInterfaceDescriptor& iface : config->interfaces) { + if (IsAndroidInterface(iface)) { + ++device_count; + } + } + } } - if (inbound_address == 0 || outbound_address == 0) - return NULL; - - if (!usb_handle->ClaimInterface(interface.interface_number)) - return NULL; - - return new AndroidUsbDevice(rsa_key, - usb_handle, - base::UTF16ToASCII(serial), - inbound_address, - outbound_address, - zero_mask, - interface.interface_number); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(callback, device_count)); } uint32 Checksum(const std::string& data) { @@ -147,23 +132,8 @@ void ReleaseInterface(scoped_refptr<UsbDeviceHandle> usb_device, usb_device->Close(); } -} // namespace - -AdbMessage::AdbMessage(uint32 command, - uint32 arg0, - uint32 arg1, - const std::string& body) - : command(command), - arg0(arg0), - arg1(arg1), - body(body) { -} - -AdbMessage::~AdbMessage() { -} - -static void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback, - AndroidUsbDevices* new_devices) { +void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback, + AndroidUsbDevices* new_devices) { scoped_ptr<AndroidUsbDevices> devices(new_devices); // Add raw pointers to the newly claimed devices. @@ -176,82 +146,115 @@ static void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback, callback.Run(result); } -static void RespondOnFileThread( +void RespondOnUIThread( const AndroidUsbDevicesCallback& callback, AndroidUsbDevices* devices, - scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - caller_message_loop_proxy->PostTask( - FROM_HERE, - base::Bind(&RespondOnCallerThread, callback, devices)); + scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + caller_task_runner->PostTask( + FROM_HERE, base::Bind(&RespondOnCallerThread, callback, devices)); } -static void OpenAndroidDeviceOnFileThread( - AndroidUsbDevices* devices, - crypto::RSAPrivateKey* rsa_key, - const base::Closure& barrier, - scoped_refptr<UsbDevice> device, - int interface_id, - bool success) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); +void CreateDeviceOnInterfaceClaimed(AndroidUsbDevices* devices, + crypto::RSAPrivateKey* rsa_key, + scoped_refptr<UsbDeviceHandle> usb_handle, + int inbound_address, + int outbound_address, + int zero_mask, + int interface_number, + const base::Closure& barrier, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); if (success) { - base::string16 serial; - if (device->GetSerialNumber(&serial) && !serial.empty()) { - const UsbConfigDescriptor* config = device->GetConfiguration(); - if (config) { - scoped_refptr<UsbDeviceHandle> usb_handle = device->Open(); - if (usb_handle.get()) { - scoped_refptr<AndroidUsbDevice> android_device = ClaimInterface( - rsa_key, usb_handle, serial, config->interfaces[interface_id]); - if (android_device.get()) - devices->push_back(android_device); - else - usb_handle->Close(); - } - } - } + devices->push_back(new AndroidUsbDevice( + rsa_key, usb_handle, + base::UTF16ToASCII(usb_handle->GetDevice()->serial_number()), + inbound_address, outbound_address, zero_mask, interface_number)); + } else { + usb_handle->Close(); } barrier.Run(); } -static int CountOnFileThread() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - UsbService* service = device::DeviceClient::Get()->GetUsbService(); - UsbDevices usb_devices; - if (service != NULL) - service->GetDevices(&usb_devices); - int device_count = 0; - for (const scoped_refptr<UsbDevice>& device : usb_devices) { - const UsbConfigDescriptor* config = device->GetConfiguration(); - if (config) { - for (const UsbInterfaceDescriptor& iface : config->interfaces) { - if (IsAndroidInterface(iface)) { - ++device_count; - } - } - } +void OnDeviceOpened(AndroidUsbDevices* devices, + crypto::RSAPrivateKey* rsa_key, + int inbound_address, + int outbound_address, + int zero_mask, + int interface_number, + const base::Closure& barrier, + scoped_refptr<UsbDeviceHandle> usb_handle) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (usb_handle.get()) { + usb_handle->ClaimInterface( + interface_number, + base::Bind(&CreateDeviceOnInterfaceClaimed, devices, rsa_key, + usb_handle, inbound_address, outbound_address, zero_mask, + interface_number, barrier)); + } else { + barrier.Run(); } - return device_count; } -static void EnumerateOnFileThread( - crypto::RSAPrivateKey* rsa_key, - const AndroidUsbDevicesCallback& callback, - scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); +void OpenAndroidDevice(AndroidUsbDevices* devices, + crypto::RSAPrivateKey* rsa_key, + const base::Closure& barrier, + scoped_refptr<UsbDevice> device, + int interface_id, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!success) { + barrier.Run(); + return; + } - UsbService* service = device::DeviceClient::Get()->GetUsbService(); - UsbDevices usb_devices; - if (service != NULL) - service->GetDevices(&usb_devices); + if (device->serial_number().empty()) { + barrier.Run(); + return; + } + + const UsbConfigDescriptor* config = device->GetConfiguration(); + if (!config) { + barrier.Run(); + return; + } + const UsbInterfaceDescriptor& interface = config->interfaces[interface_id]; + int inbound_address = 0; + int outbound_address = 0; + int zero_mask = 0; + + for (const UsbEndpointDescriptor& endpoint : interface.endpoints) { + if (endpoint.transfer_type != device::USB_TRANSFER_BULK) + continue; + if (endpoint.direction == device::USB_DIRECTION_INBOUND) + inbound_address = endpoint.address; + else + outbound_address = endpoint.address; + zero_mask = endpoint.maximum_packet_size - 1; + } + + if (inbound_address == 0 || outbound_address == 0) { + barrier.Run(); + return; + } + + device->Open(base::Bind(&OnDeviceOpened, devices, rsa_key, inbound_address, + outbound_address, zero_mask, + interface.interface_number, barrier)); +} + +void OpenAndroidDevices( + crypto::RSAPrivateKey* rsa_key, + const AndroidUsbDevicesCallback& callback, + scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner, + const UsbDevices& usb_devices) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add new devices. AndroidUsbDevices* devices = new AndroidUsbDevices(); base::Closure barrier = base::BarrierClosure( - usb_devices.size(), base::Bind(&RespondOnFileThread, - callback, - devices, - caller_message_loop_proxy)); + usb_devices.size(), + base::Bind(&RespondOnUIThread, callback, devices, caller_task_runner)); for (const scoped_refptr<UsbDevice>& device : usb_devices) { const UsbConfigDescriptor* config = device->GetConfiguration(); @@ -265,45 +268,73 @@ static void EnumerateOnFileThread( continue; } - device->RequestUsbAccess( - j, base::Bind(&OpenAndroidDeviceOnFileThread, devices, rsa_key, - barrier, device, j)); + device->RequestUsbAccess(j, base::Bind(&OpenAndroidDevice, devices, + rsa_key, barrier, device, j)); has_android_interface = true; break; } - if (!has_android_interface) + if (!has_android_interface) { barrier.Run(); + } + } +} + +void EnumerateOnUIThread( + crypto::RSAPrivateKey* rsa_key, + const AndroidUsbDevicesCallback& callback, + scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + UsbService* service = device::DeviceClient::Get()->GetUsbService(); + if (service == NULL) { + caller_task_runner->PostTask(FROM_HERE, + base::Bind(callback, AndroidUsbDevices())); + } else { + service->GetDevices( + base::Bind(&OpenAndroidDevices, rsa_key, callback, caller_task_runner)); } } +} // namespace + +AdbMessage::AdbMessage(uint32 command, + uint32 arg0, + uint32 arg1, + const std::string& body) + : command(command), arg0(arg0), arg1(arg1), body(body) { +} + +AdbMessage::~AdbMessage() { +} + // static -void AndroidUsbDevice::CountDevices( - const base::Callback<void(int)>& callback) { - BrowserThread::PostTaskAndReplyWithResult( - BrowserThread::FILE, - FROM_HERE, - base::Bind(&CountOnFileThread), - callback); +void AndroidUsbDevice::CountDevices(const base::Callback<void(int)>& callback) { + UsbService* service = device::DeviceClient::Get()->GetUsbService(); + if (service != NULL) { + service->GetDevices(base::Bind(&CountAndroidDevices, callback)); + } else { + base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, + base::Bind(callback, 0)); + } } // static void AndroidUsbDevice::Enumerate(crypto::RSAPrivateKey* rsa_key, const AndroidUsbDevicesCallback& callback) { - // Collect devices with closed handles. for (AndroidUsbDevice* device : g_devices.Get()) { if (device->usb_handle_.get()) { - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&AndroidUsbDevice::TerminateIfReleased, device, - device->usb_handle_)); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&AndroidUsbDevice::TerminateIfReleased, + device, device->usb_handle_)); } } // Then look for the new devices. - BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, - base::Bind(&EnumerateOnFileThread, rsa_key, callback, - base::MessageLoopProxy::current())); + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&EnumerateOnUIThread, rsa_key, callback, + base::ThreadTaskRunnerHandle::Get())); } AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key, @@ -313,8 +344,7 @@ AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key, int outbound_address, int zero_mask, int interface_id) - : message_loop_(NULL), - rsa_key_(rsa_key->Copy()), + : rsa_key_(rsa_key->Copy()), usb_handle_(usb_device), serial_(serial), inbound_address_(inbound_address), @@ -328,9 +358,9 @@ AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key, } void AndroidUsbDevice::InitOnCallerThread() { - if (message_loop_) + if (task_runner_) return; - message_loop_ = base::MessageLoop::current(); + task_runner_ = base::ThreadTaskRunnerHandle::Get(); Queue(make_scoped_ptr(new AdbMessage(AdbMessage::kCommandCNXN, kVersion, kMaxPayload, kHostConnectMessage))); ReadHeader(); @@ -360,12 +390,12 @@ void AndroidUsbDevice::Send(uint32 command, } AndroidUsbDevice::~AndroidUsbDevice() { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); Terminate(); } void AndroidUsbDevice::Queue(scoped_ptr<AdbMessage> message) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); // Queue header. std::vector<uint32> header; @@ -407,7 +437,7 @@ void AndroidUsbDevice::Queue(scoped_ptr<AdbMessage> message) { } void AndroidUsbDevice::ProcessOutgoing() { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); if (outgoing_queue_.empty() || !usb_handle_.get()) return; @@ -415,11 +445,9 @@ void AndroidUsbDevice::ProcessOutgoing() { BulkMessage message = outgoing_queue_.front(); outgoing_queue_.pop(); DumpMessage(true, message->data(), message->size()); - usb_handle_->BulkTransfer(device::USB_DIRECTION_OUTBOUND, - outbound_address_, - message.get(), - message->size(), - kUsbTimeout, + + usb_handle_->BulkTransfer(device::USB_DIRECTION_OUTBOUND, outbound_address_, + message, message->size(), kUsbTimeout, base::Bind(&AndroidUsbDevice::OutgoingMessageSent, weak_factory_.GetWeakPtr())); } @@ -427,25 +455,24 @@ void AndroidUsbDevice::ProcessOutgoing() { void AndroidUsbDevice::OutgoingMessageSent(UsbTransferStatus status, scoped_refptr<net::IOBuffer> buffer, size_t result) { - DCHECK(message_loop_ == base::MessageLoop::current()); - - if (status != device::USB_TRANSFER_COMPLETED) + if (status != device::USB_TRANSFER_COMPLETED) { return; - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::ProcessOutgoing, this)); + } + + task_runner_->PostTask(FROM_HERE, + base::Bind(&AndroidUsbDevice::ProcessOutgoing, this)); } void AndroidUsbDevice::ReadHeader() { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); - if (!usb_handle_.get()) + if (!usb_handle_.get()) { return; + } + scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kHeaderSize); usb_handle_->BulkTransfer( - device::USB_DIRECTION_INBOUND, - inbound_address_, - buffer.get(), - kHeaderSize, + device::USB_DIRECTION_INBOUND, inbound_address_, buffer, kHeaderSize, kUsbTimeout, base::Bind(&AndroidUsbDevice::ParseHeader, weak_factory_.GetWeakPtr())); } @@ -453,11 +480,11 @@ void AndroidUsbDevice::ReadHeader() { void AndroidUsbDevice::ParseHeader(UsbTransferStatus status, scoped_refptr<net::IOBuffer> buffer, size_t result) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); if (status == device::USB_TRANSFER_TIMEOUT) { - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::ReadHeader, this)); + task_runner_->PostTask(FROM_HERE, + base::Bind(&AndroidUsbDevice::ReadHeader, this)); return; } @@ -480,35 +507,31 @@ void AndroidUsbDevice::ParseHeader(UsbTransferStatus status, } if (data_length == 0) { - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::HandleIncoming, this, - base::Passed(&message))); - return; + task_runner_->PostTask(FROM_HERE, + base::Bind(&AndroidUsbDevice::HandleIncoming, this, + base::Passed(&message))); + } else { + task_runner_->PostTask( + FROM_HERE, base::Bind(&AndroidUsbDevice::ReadBody, this, + base::Passed(&message), data_length, data_check)); } - - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::ReadBody, this, - base::Passed(&message), data_length, data_check)); } void AndroidUsbDevice::ReadBody(scoped_ptr<AdbMessage> message, uint32 data_length, uint32 data_check) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); - if (!usb_handle_.get()) + if (!usb_handle_.get()) { return; + } + scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_length); - usb_handle_->BulkTransfer(device::USB_DIRECTION_INBOUND, - inbound_address_, - buffer.get(), - data_length, - kUsbTimeout, - base::Bind(&AndroidUsbDevice::ParseBody, - weak_factory_.GetWeakPtr(), - base::Passed(&message), - data_length, - data_check)); + usb_handle_->BulkTransfer( + device::USB_DIRECTION_INBOUND, inbound_address_, buffer, data_length, + kUsbTimeout, + base::Bind(&AndroidUsbDevice::ParseBody, weak_factory_.GetWeakPtr(), + base::Passed(&message), data_length, data_check)); } void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message, @@ -517,12 +540,12 @@ void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message, UsbTransferStatus status, scoped_refptr<net::IOBuffer> buffer, size_t result) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); if (status == device::USB_TRANSFER_TIMEOUT) { - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::ReadBody, this, - base::Passed(&message), data_length, data_check)); + task_runner_->PostTask( + FROM_HERE, base::Bind(&AndroidUsbDevice::ReadBody, this, + base::Passed(&message), data_length, data_check)); return; } @@ -539,13 +562,13 @@ void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message, return; } - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::HandleIncoming, this, - base::Passed(&message))); + task_runner_->PostTask(FROM_HERE, + base::Bind(&AndroidUsbDevice::HandleIncoming, this, + base::Passed(&message))); } void AndroidUsbDevice::HandleIncoming(scoped_ptr<AdbMessage> message) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); switch (message->command) { case AdbMessage::kCommandAUTH: @@ -599,23 +622,24 @@ void AndroidUsbDevice::HandleIncoming(scoped_ptr<AdbMessage> message) { } void AndroidUsbDevice::TransferError(UsbTransferStatus status) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::Terminate, this)); + Terminate(); } void AndroidUsbDevice::TerminateIfReleased( scoped_refptr<UsbDeviceHandle> usb_handle) { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - if (usb_handle->GetDevice().get()) + DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (usb_handle->GetDevice().get()) { return; - message_loop_->PostTask(FROM_HERE, - base::Bind(&AndroidUsbDevice::Terminate, this)); + } + + task_runner_->PostTask(FROM_HERE, + base::Bind(&AndroidUsbDevice::Terminate, this)); } void AndroidUsbDevice::Terminate() { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); std::vector<AndroidUsbDevice*>::iterator it = std::find(g_devices.Get().begin(), g_devices.Get().end(), this); @@ -639,12 +663,12 @@ void AndroidUsbDevice::Terminate() { DCHECK(sockets_.empty()); BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, + BrowserThread::UI, FROM_HERE, base::Bind(&ReleaseInterface, usb_handle, interface_id_)); } void AndroidUsbDevice::SocketDeleted(uint32 socket_id) { - DCHECK(message_loop_ == base::MessageLoop::current()); + DCHECK(task_runner_->BelongsToCurrentThread()); sockets_.erase(socket_id); } diff --git a/chrome/browser/devtools/device/usb/android_usb_device.h b/chrome/browser/devtools/device/usb/android_usb_device.h index 3400a97..af976a0 100644 --- a/chrome/browser/devtools/device/usb/android_usb_device.h +++ b/chrome/browser/devtools/device/usb/android_usb_device.h @@ -15,7 +15,7 @@ #include "device/usb/usb_device_handle.h" namespace base { -class MessageLoop; +class SingleThreadTaskRunner; } namespace crypto { @@ -68,11 +68,10 @@ typedef base::Callback<void(const AndroidUsbDevices&)> class AndroidUsbDevice : public base::RefCountedThreadSafe<AndroidUsbDevice> { public: + static void CountDevices(const base::Callback<void(int)>& callback); static void Enumerate(crypto::RSAPrivateKey* rsa_key, const AndroidUsbDevicesCallback& callback); - static void CountDevices(const base::Callback<void(int)>& callback); - AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key, scoped_refptr<device::UsbDeviceHandle> device, const std::string& serial, @@ -130,7 +129,7 @@ class AndroidUsbDevice : public base::RefCountedThreadSafe<AndroidUsbDevice> { void SocketDeleted(uint32 socket_id); - base::MessageLoop* message_loop_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_ptr<crypto::RSAPrivateKey> rsa_key_; diff --git a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc index 0140bf6..32296bc 100644 --- a/chrome/browser/extensions/api/device_permissions_manager_unittest.cc +++ b/chrome/browser/extensions/api/device_permissions_manager_unittest.cc @@ -30,121 +30,86 @@ using testing::SetArgPointee; int next_id; +class MockDeviceClient : device::DeviceClient { + public: + MockDeviceClient() {} + + // device::DeviceClient implementation: + UsbService* GetUsbService() override { + DCHECK(usb_service_); + return usb_service_; + } + + void set_usb_service(UsbService* service) { usb_service_ = service; } + + private: + UsbService* usb_service_ = nullptr; +}; + class MockUsbService : public UsbService { public: - MockUsbService() : mock_device_client(this) {} + MockUsbService() {} MOCK_METHOD1(GetDeviceById, scoped_refptr<UsbDevice>(uint32)); - MOCK_METHOD1(GetDevices, void(std::vector<scoped_refptr<UsbDevice>>*)); + MOCK_METHOD1(GetDevices, void(const GetDevicesCallback& callback)); // Public wrapper for the protected NotifyDeviceRemove function. void NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { UsbService::NotifyDeviceRemoved(device); } - - private: - class MockDeviceClient : device::DeviceClient { - public: - explicit MockDeviceClient(UsbService* usb_service) - : usb_service_(usb_service) {} - - UsbService* GetUsbService() override { return usb_service_; } - - private: - UsbService* usb_service_; - }; - - MockDeviceClient mock_device_client; }; class MockUsbDevice : public UsbDevice { public: explicit MockUsbDevice(const std::string& serial_number) - : UsbDevice(0, 0, next_id++) { - if (serial_number.empty()) { - EXPECT_CALL(*this, GetSerialNumber(_)).WillRepeatedly(Return(false)); - } else { - EXPECT_CALL(*this, GetSerialNumber(_)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(base::ASCIIToUTF16(serial_number)), - Return(true))); - } - - EXPECT_CALL(*this, GetProduct(_)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(base::ASCIIToUTF16("Test Product")), - Return(true))); - EXPECT_CALL(*this, GetManufacturer(_)) - .WillRepeatedly( - DoAll(SetArgPointee<0>(base::ASCIIToUTF16("Test Manufacturer")), - Return(true))); - } - - MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>()); + : UsbDevice(0, + 0, + next_id++, + base::ASCIIToUTF16("Test Manufacturer"), + base::ASCIIToUTF16("Test Product"), + base::ASCIIToUTF16(serial_number)) {} + + MOCK_METHOD1(Open, void(const OpenCallback&)); MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>)); MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor*()); - MOCK_METHOD1(GetManufacturer, bool(base::string16*)); - MOCK_METHOD1(GetProduct, bool(base::string16*)); - MOCK_METHOD1(GetSerialNumber, bool(base::string16*)); private: virtual ~MockUsbDevice() {} }; -void AllowUsbDevice(DevicePermissionsManager* manager, - const Extension* extension, - scoped_refptr<UsbDevice> device) { - // If the device cannot provide any of these strings they will simply by - // empty. - base::string16 product; - device->GetProduct(&product); - base::string16 manufacturer; - device->GetManufacturer(&manufacturer); - base::string16 serial_number; - device->GetSerialNumber(&serial_number); - - manager->AllowUsbDevice( - extension->id(), device, product, manufacturer, serial_number); -} - -scoped_refptr<DevicePermissionEntry> FindEntry( - DevicePermissions* device_permissions, - scoped_refptr<UsbDevice> device) { - base::string16 serial_number; - device->GetSerialNumber(&serial_number); - - return device_permissions->FindEntry(device, serial_number); -} - } // namespace class DevicePermissionsManagerTest : public testing::Test { protected: void SetUp() override { testing::Test::SetUp(); - env_.GetExtensionPrefs(); // Force creation before adding extensions. - extension_ = env_.MakeExtension(*base::test::ParseJson( - "{" - " \"app\": {" - " \"background\": {" - " \"scripts\": [\"background.js\"]" - " }" - " }," - " \"permissions\": [" - " \"usb\"" - " ]" - "}")); + env_.reset(new extensions::TestExtensionEnvironment()); + env_->GetExtensionPrefs(); // Force creation before adding extensions. + extension_ = + env_->MakeExtension(*base::test::ParseJson( + "{" + " \"app\": {" + " \"background\": {" + " \"scripts\": [\"background.js\"]" + " }" + " }," + " \"permissions\": [" + " \"usb\"" + " ]" + "}")); device0_ = new MockUsbDevice("ABCDE"); device1_ = new MockUsbDevice(""); device2_ = new MockUsbDevice("12345"); device3_ = new MockUsbDevice(""); - usb_service_ = new MockUsbService(); - UsbService::SetInstanceForTest(usb_service_); + mock_device_client_.set_usb_service(&usb_service_); } - extensions::TestExtensionEnvironment env_; + void TearDown() override { env_.reset(nullptr); } + + scoped_ptr<extensions::TestExtensionEnvironment> env_; const extensions::Extension* extension_; - MockUsbService* usb_service_; + MockDeviceClient mock_device_client_; + MockUsbService usb_service_; scoped_refptr<MockUsbDevice> device0_; scoped_refptr<MockUsbDevice> device1_; scoped_refptr<MockUsbDevice> device2_; @@ -153,20 +118,20 @@ class DevicePermissionsManagerTest : public testing::Test { TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - AllowUsbDevice(manager, extension_, device0_); - AllowUsbDevice(manager, extension_, device1_); + DevicePermissionsManager::Get(env_->profile()); + manager->AllowUsbDevice(extension_->id(), device0_); + manager->AllowUsbDevice(extension_->id(), device1_); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); scoped_refptr<DevicePermissionEntry> device0_entry = - FindEntry(device_permissions.get(), device0_); + device_permissions->FindEntry(device0_); ASSERT_TRUE(device0_entry.get()); scoped_refptr<DevicePermissionEntry> device1_entry = - FindEntry(device_permissions.get(), device1_); + device_permissions->FindEntry(device1_); ASSERT_TRUE(device1_entry.get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); ASSERT_EQ(2U, device_permissions->entries().size()); ASSERT_EQ(base::ASCIIToUTF16( @@ -176,132 +141,123 @@ TEST_F(DevicePermissionsManagerTest, AllowAndClearDevices) { device1_entry->GetPermissionMessageString()); manager->Clear(extension_->id()); - + // The device_permissions object is deleted by Clear. device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + + ASSERT_FALSE(device_permissions->FindEntry(device0_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); ASSERT_EQ(0U, device_permissions->entries().size()); // After clearing device it should be possible to grant permission again. - AllowUsbDevice(manager, extension_, device0_); - AllowUsbDevice(manager, extension_, device1_); + manager->AllowUsbDevice(extension_->id(), device0_); + manager->AllowUsbDevice(extension_->id(), device1_); - device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); } TEST_F(DevicePermissionsManagerTest, SuspendExtension) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - AllowUsbDevice(manager, extension_, device0_); - AllowUsbDevice(manager, extension_, device1_); + DevicePermissionsManager::Get(env_->profile()); + manager->AllowUsbDevice(extension_->id(), device0_); + manager->AllowUsbDevice(extension_->id(), device1_); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); manager->OnBackgroundHostClose(extension_->id()); - device_permissions = manager->GetForExtension(extension_->id()); // Device 0 is still registered because its serial number has been stored in // ExtensionPrefs, it is "persistent". - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); // Device 1 does not have uniquely identifying traits and so permission to // open it has been dropped when the app's windows have closed and the // background page has been suspended. - ASSERT_FALSE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); } -// TODO(reillyg): Until crbug.com/427985 is resolved device removal -// notifications are delivered asynchronously and so this test must be disabled. -TEST_F(DevicePermissionsManagerTest, DISABLED_DisconnectDevice) { +TEST_F(DevicePermissionsManagerTest, DisconnectDevice) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - AllowUsbDevice(manager, extension_, device0_); - AllowUsbDevice(manager, extension_, device1_); + DevicePermissionsManager::Get(env_->profile()); + manager->AllowUsbDevice(extension_->id(), device0_); + manager->AllowUsbDevice(extension_->id(), device1_); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); - usb_service_->NotifyDeviceRemoved(device0_); - usb_service_->NotifyDeviceRemoved(device1_); + usb_service_.NotifyDeviceRemoved(device0_); + usb_service_.NotifyDeviceRemoved(device1_); - device_permissions = manager->GetForExtension(extension_->id()); // Device 0 will be accessible when it is reconnected because it can be // recognized by its serial number. - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); // Device 1 does not have a serial number and cannot be distinguished from // any other device of the same model so the app must request permission again // when it is reconnected. - ASSERT_FALSE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); } TEST_F(DevicePermissionsManagerTest, RevokeAndRegrantAccess) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - AllowUsbDevice(manager, extension_, device0_); - AllowUsbDevice(manager, extension_, device1_); + DevicePermissionsManager::Get(env_->profile()); + manager->AllowUsbDevice(extension_->id(), device0_); + manager->AllowUsbDevice(extension_->id(), device1_); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); scoped_refptr<DevicePermissionEntry> device0_entry = - FindEntry(device_permissions.get(), device0_); + device_permissions->FindEntry(device0_); ASSERT_TRUE(device0_entry.get()); scoped_refptr<DevicePermissionEntry> device1_entry = - FindEntry(device_permissions.get(), device1_); + device_permissions->FindEntry(device1_); ASSERT_TRUE(device1_entry.get()); manager->RemoveEntry(extension_->id(), device0_entry); - device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); - AllowUsbDevice(manager, extension_, device0_); - device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); + manager->AllowUsbDevice(extension_->id(), device0_); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); manager->RemoveEntry(extension_->id(), device1_entry); - device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device1_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device1_).get()); - AllowUsbDevice(manager, extension_, device1_); - device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device1_).get()); + manager->AllowUsbDevice(extension_->id(), device1_); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device1_).get()); } TEST_F(DevicePermissionsManagerTest, UpdateLastUsed) { DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - AllowUsbDevice(manager, extension_, device0_); + DevicePermissionsManager::Get(env_->profile()); + manager->AllowUsbDevice(extension_->id(), device0_); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); scoped_refptr<DevicePermissionEntry> device0_entry = - FindEntry(device_permissions.get(), device0_); + device_permissions->FindEntry(device0_); ASSERT_TRUE(device0_entry->last_used().is_null()); manager->UpdateLastUsed(extension_->id(), device0_entry); - device_permissions = manager->GetForExtension(extension_->id()); - device0_entry = FindEntry(device_permissions.get(), device0_); + device0_entry = device_permissions->FindEntry(device0_); ASSERT_FALSE(device0_entry->last_used().is_null()); } @@ -315,17 +271,17 @@ TEST_F(DevicePermissionsManagerTest, LoadPrefs) { " \"vendor_id\": 0" " }" "]"); - env_.GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices", - prefs_value.release()); + env_->GetExtensionPrefs()->UpdateExtensionPref(extension_->id(), "devices", + prefs_value.release()); DevicePermissionsManager* manager = - DevicePermissionsManager::Get(env_.profile()); - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissionsManager::Get(env_->profile()); + DevicePermissions* device_permissions = manager->GetForExtension(extension_->id()); - ASSERT_TRUE(FindEntry(device_permissions.get(), device0_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device1_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device2_).get()); - ASSERT_FALSE(FindEntry(device_permissions.get(), device3_).get()); + ASSERT_TRUE(device_permissions->FindEntry(device0_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device1_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device2_).get()); + ASSERT_FALSE(device_permissions->FindEntry(device3_).get()); } } // namespace extensions diff --git a/device/hid/hid_connection_unittest.cc b/device/hid/hid_connection_unittest.cc index e474a62..b036198 100644 --- a/device/hid/hid_connection_unittest.cc +++ b/device/hid/hid_connection_unittest.cc @@ -10,10 +10,12 @@ #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" #include "base/scoped_observer.h" +#include "base/strings/utf_string_conversions.h" #include "base/test/test_io_thread.h" #include "device/hid/hid_connection.h" #include "device/hid/hid_service.h" #include "device/test/usb_test_gadget.h" +#include "device/usb/usb_device.h" #include "net/base/io_buffer.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,20 +25,6 @@ namespace { using net::IOBufferWithSize; -void ClaimTestDevice(scoped_ptr<UsbTestGadget>* gadget) { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - *gadget = UsbTestGadget::Claim(); - ASSERT_TRUE(*gadget); - ASSERT_TRUE((*gadget)->SetType(UsbTestGadget::HID_ECHO)); -} - -void UnclaimTestDevice(scoped_ptr<UsbTestGadget> gadget) { - base::MessageLoop::ScopedNestableTaskAllower allow( - base::MessageLoop::current()); - ASSERT_TRUE(gadget->Unclaim()); -} - // Helper class that can be used to block until a HID device with a particular // serial number is available. Example usage: // @@ -46,10 +34,8 @@ void UnclaimTestDevice(scoped_ptr<UsbTestGadget> gadget) { // class DeviceCatcher : HidService::Observer { public: - DeviceCatcher(const std::string& serial_number) - : serial_number_(serial_number), observer_(this) { - HidService* hid_service = HidService::GetInstance( - base::MessageLoop::current()->message_loop_proxy()); + DeviceCatcher(HidService* hid_service, const base::string16& serial_number) + : serial_number_(base::UTF16ToUTF8(serial_number)), observer_(this) { observer_.Add(hid_service); hid_service->GetDevices(base::Bind(&DeviceCatcher::OnEnumerationComplete, base::Unretained(this))); @@ -168,23 +154,16 @@ class HidConnectionTest : public testing::Test { service_ = HidService::GetInstance(io_thread_->task_runner()); ASSERT_TRUE(service_); - io_thread_->PostTaskAndWait(FROM_HERE, - base::Bind(&ClaimTestDevice, &test_gadget_)); + test_gadget_ = UsbTestGadget::Claim(io_thread_->task_runner()); ASSERT_TRUE(test_gadget_); + ASSERT_TRUE(test_gadget_->SetType(UsbTestGadget::HID_ECHO)); - DeviceCatcher device_catcher(test_gadget_->GetSerialNumber()); + DeviceCatcher device_catcher(service_, + test_gadget_->GetDevice()->serial_number()); device_id_ = device_catcher.WaitForDevice(); ASSERT_NE(device_id_, kInvalidHidDeviceId); } - void TearDown() override { - if (io_thread_) { - io_thread_->PostTaskAndWait( - FROM_HERE, - base::Bind(&UnclaimTestDevice, base::Passed(&test_gadget_))); - } - } - scoped_ptr<base::MessageLoopForUI> message_loop_; scoped_ptr<base::TestIOThread> io_thread_; HidService* service_; diff --git a/device/hid/hid_service.cc b/device/hid/hid_service.cc index 4f20235..fc50a41 100644 --- a/device/hid/hid_service.cc +++ b/device/hid/hid_service.cc @@ -106,9 +106,11 @@ void HidService::AddDevice(scoped_refptr<HidDeviceInfo> device_info) { HID_LOG(USER) << "HID device " << (enumeration_ready_ ? "added" : "detected") - << ": vendorId = " << device_info->vendor_id() - << ", productId = " << device_info->product_id() - << ", deviceId = '" << device_info->device_id() << "'"; + << ": vendorId=" << device_info->vendor_id() + << ", productId=" << device_info->product_id() << ", name='" + << device_info->product_name() << "', serial='" + << device_info->serial_number() << "', deviceId='" + << device_info->device_id() << "'"; if (enumeration_ready_) { FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device_info)); @@ -120,7 +122,7 @@ void HidService::RemoveDevice(const HidDeviceId& device_id) { DCHECK(thread_checker_.CalledOnValidThread()); DeviceMap::iterator it = devices_.find(device_id); if (it != devices_.end()) { - HID_LOG(USER) << "HID device removed: deviceId = '" << device_id << "'"; + HID_LOG(USER) << "HID device removed: deviceId='" << device_id << "'"; if (enumeration_ready_) { FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(it->second)); diff --git a/device/test/usb_test_gadget.h b/device/test/usb_test_gadget.h index 99110a1..b1a8642 100644 --- a/device/test/usb_test_gadget.h +++ b/device/test/usb_test_gadget.h @@ -8,8 +8,13 @@ #include <string> #include "base/macros.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +namespace base { +class SingleThreadTaskRunner; +} + namespace device { class UsbDevice; @@ -27,7 +32,8 @@ class UsbTestGadget { virtual ~UsbTestGadget() {} static bool IsTestEnabled(); - static scoped_ptr<UsbTestGadget> Claim(); + static scoped_ptr<UsbTestGadget> Claim( + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner); virtual bool Unclaim() = 0; virtual bool Disconnect() = 0; @@ -35,7 +41,6 @@ class UsbTestGadget { virtual bool SetType(Type type) = 0; virtual UsbDevice* GetDevice() const = 0; - virtual const std::string& GetSerialNumber() const = 0; protected: UsbTestGadget() {} diff --git a/device/test/usb_test_gadget_impl.cc b/device/test/usb_test_gadget_impl.cc index d9371b4..2eea058 100644 --- a/device/test/usb_test_gadget_impl.cc +++ b/device/test/usb_test_gadget_impl.cc @@ -18,9 +18,10 @@ #include "base/path_service.h" #include "base/process/process_handle.h" #include "base/run_loop.h" -#include "base/strings/string_number_conversions.h" +#include "base/scoped_observer.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/thread_task_runner_handle.h" #include "base/time/time.h" #include "device/usb/usb_device.h" #include "device/usb/usb_device_handle.h" @@ -33,28 +34,35 @@ #include "net/url_request/url_request_context_getter.h" #include "url/gurl.h" -using ::base::PlatformThread; -using ::base::TimeDelta; - namespace device { +class UsbTestGadgetImpl : public UsbTestGadget { + public: + UsbTestGadgetImpl( + scoped_refptr<net::URLRequestContextGetter> request_context_getter, + UsbService* usb_service, + scoped_refptr<UsbDevice> device); + ~UsbTestGadgetImpl() override; + + bool Unclaim() override; + bool Disconnect() override; + bool Reconnect() override; + bool SetType(Type type) override; + UsbDevice* GetDevice() const override; + + private: + std::string device_address_; + scoped_refptr<UsbDevice> device_; + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; + UsbService* usb_service_; + + DISALLOW_COPY_AND_ASSIGN(UsbTestGadgetImpl); +}; + namespace { static const char kCommandLineSwitch[] = "enable-gadget-tests"; -static const int kClaimRetries = 100; // 5 seconds -static const int kDisconnectRetries = 100; // 5 seconds -static const int kRetryPeriod = 50; // 0.05 seconds -static const int kReconnectRetries = 100; // 5 seconds -static const int kUpdateRetries = 100; // 5 seconds - -// Wait for the given time delta while still running the main loop. This is -// necessary so that device add/remove events are processed by the UsbService. -void SleepWithRunLoop(base::TimeDelta delta) { - base::RunLoop run_loop; - base::MessageLoop::current()->PostDelayedTask(FROM_HERE, - run_loop.QuitClosure(), delta); - run_loop.Run(); -} +static const int kReenumeratePeriod = 100; // 0.1 seconds struct UsbTestGadgetConfiguration { UsbTestGadget::Type type; @@ -70,131 +78,107 @@ static const struct UsbTestGadgetConfiguration kConfigurations[] = { {UsbTestGadget::ECHO, "/echo/configure", 0x58F4}, }; -class UsbTestGadgetImpl : public UsbTestGadget { - public: - ~UsbTestGadgetImpl() override; - - bool Unclaim() override; - bool Disconnect() override; - bool Reconnect() override; - bool SetType(Type type) override; - UsbDevice* GetDevice() const override; - const std::string& GetSerialNumber() const override; - - protected: - UsbTestGadgetImpl(); - - private: - scoped_ptr<net::URLFetcher> CreateURLFetcher( - const GURL& url, - net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* delegate); - int SimplePOSTRequest(const GURL& url, const std::string& form_data); - bool FindUnclaimed(); - bool GetVersion(std::string* version); - bool Update(); - bool FindClaimed(); - bool ReadLocalVersion(std::string* version); - bool ReadLocalPackage(std::string* package); - bool ReadFile(const base::FilePath& file_path, std::string* content); - - class Delegate : public net::URLFetcherDelegate { - public: - Delegate() {} - ~Delegate() override {} - - void WaitForCompletion() { - run_loop_.Run(); - } +bool ReadFile(const base::FilePath& file_path, std::string* content) { + base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); + if (!file.IsValid()) { + LOG(ERROR) << "Cannot open " << file_path.MaybeAsASCII() << ": " + << base::File::ErrorToString(file.error_details()); + return false; + } - void OnURLFetchComplete(const net::URLFetcher* source) override { - run_loop_.Quit(); + STLClearObject(content); + int rv; + do { + char buf[4096]; + rv = file.ReadAtCurrentPos(buf, sizeof buf); + if (rv == -1) { + LOG(ERROR) << "Cannot read " << file_path.MaybeAsASCII() << ": " + << base::File::ErrorToString(file.error_details()); + return false; } + content->append(buf, rv); + } while (rv > 0); - private: - base::RunLoop run_loop_; - - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - scoped_refptr<UsbDevice> device_; - std::string device_address_; - scoped_ptr<net::URLRequestContext> request_context_; - std::string session_id_; - UsbService* usb_service_; + return true; +} - friend class UsbTestGadget; +bool ReadLocalVersion(std::string* version) { + base::FilePath file_path; + CHECK(PathService::Get(base::DIR_EXE, &file_path)); + file_path = file_path.AppendASCII("usb_gadget.zip.md5"); - DISALLOW_COPY_AND_ASSIGN(UsbTestGadgetImpl); -}; + return ReadFile(file_path, version); +} -} // namespace +bool ReadLocalPackage(std::string* package) { + base::FilePath file_path; + CHECK(PathService::Get(base::DIR_EXE, &file_path)); + file_path = file_path.AppendASCII("usb_gadget.zip"); -bool UsbTestGadget::IsTestEnabled() { - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - return command_line->HasSwitch(kCommandLineSwitch); + return ReadFile(file_path, package); } -scoped_ptr<UsbTestGadget> UsbTestGadget::Claim() { - scoped_ptr<UsbTestGadgetImpl> gadget(new UsbTestGadgetImpl); +scoped_ptr<net::URLFetcher> CreateURLFetcher( + scoped_refptr<net::URLRequestContextGetter> request_context_getter, + const GURL& url, + net::URLFetcher::RequestType request_type, + net::URLFetcherDelegate* delegate) { + scoped_ptr<net::URLFetcher> url_fetcher( + net::URLFetcher::Create(url, request_type, delegate)); - int retries = kClaimRetries; - while (!gadget->FindUnclaimed()) { - if (--retries == 0) { - LOG(ERROR) << "Failed to find an unclaimed device."; - return scoped_ptr<UsbTestGadget>(); - } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); - } - VLOG(1) << "It took " << (kClaimRetries - retries) - << " retries to find an unclaimed device."; + url_fetcher->SetRequestContext(request_context_getter.get()); - return gadget.Pass(); + return url_fetcher; } -UsbTestGadgetImpl::UsbTestGadgetImpl() { - net::URLRequestContextBuilder context_builder; - context_builder.set_proxy_service(net::ProxyService::CreateDirect()); - request_context_.reset(context_builder.Build()); +class URLRequestContextGetter : public net::URLRequestContextGetter { + public: + URLRequestContextGetter( + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner) + : network_task_runner_(network_task_runner) {} - base::ProcessId process_id = base::GetCurrentProcId(); - session_id_ = base::StringPrintf( - "%s:%p", base::HexEncode(&process_id, sizeof(process_id)).c_str(), this); + private: + ~URLRequestContextGetter() override {} - usb_service_ = UsbService::GetInstance(NULL); -} + // net::URLRequestContextGetter implementation + net::URLRequestContext* GetURLRequestContext() override { + context_builder_.set_proxy_service(net::ProxyService::CreateDirect()); + return context_builder_.Build(); + } -UsbTestGadgetImpl::~UsbTestGadgetImpl() { - if (!device_address_.empty()) { - Unclaim(); + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() + const override { + return network_task_runner_; } -} -UsbDevice* UsbTestGadgetImpl::GetDevice() const { - return device_.get(); -} + net::URLRequestContextBuilder context_builder_; + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; +}; -const std::string& UsbTestGadgetImpl::GetSerialNumber() const { - return device_address_; -} +class URLFetcherDelegate : public net::URLFetcherDelegate { + public: + URLFetcherDelegate() {} + ~URLFetcherDelegate() override {} -scoped_ptr<net::URLFetcher> UsbTestGadgetImpl::CreateURLFetcher( - const GURL& url, net::URLFetcher::RequestType request_type, - net::URLFetcherDelegate* delegate) { - scoped_ptr<net::URLFetcher> url_fetcher( - net::URLFetcher::Create(url, request_type, delegate)); + void WaitForCompletion() { run_loop_.Run(); } - url_fetcher->SetRequestContext(new net::TrivialURLRequestContextGetter( - request_context_.get(), base::MessageLoop::current()->task_runner())); + void OnURLFetchComplete(const net::URLFetcher* source) override { + run_loop_.Quit(); + } - return url_fetcher; -} + private: + base::RunLoop run_loop_; + + DISALLOW_COPY_AND_ASSIGN(URLFetcherDelegate); +}; -int UsbTestGadgetImpl::SimplePOSTRequest(const GURL& url, - const std::string& form_data) { - Delegate delegate; - scoped_ptr<net::URLFetcher> url_fetcher = - CreateURLFetcher(url, net::URLFetcher::POST, &delegate); +int SimplePOSTRequest( + scoped_refptr<net::URLRequestContextGetter> request_context_getter, + const GURL& url, + const std::string& form_data) { + URLFetcherDelegate delegate; + scoped_ptr<net::URLFetcher> url_fetcher = CreateURLFetcher( + request_context_getter, url, net::URLFetcher::POST, &delegate); url_fetcher->SetUploadData("application/x-www-form-urlencoded", form_data); url_fetcher->Start(); @@ -203,230 +187,360 @@ int UsbTestGadgetImpl::SimplePOSTRequest(const GURL& url, return url_fetcher->GetResponseCode(); } -bool UsbTestGadgetImpl::FindUnclaimed() { - std::vector<scoped_refptr<UsbDevice> > devices; - usb_service_->GetDevices(&devices); - - for (std::vector<scoped_refptr<UsbDevice> >::const_iterator iter = - devices.begin(); iter != devices.end(); ++iter) { - const scoped_refptr<UsbDevice> &device = *iter; - if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) { - base::string16 serial_utf16; - if (!device->GetSerialNumber(&serial_utf16)) { - continue; - } +class UsbGadgetFactory : public UsbService::Observer, + public net::URLFetcherDelegate { + public: + UsbGadgetFactory(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) + : observer_(this), weak_factory_(this) { + usb_service_ = UsbService::GetInstance(io_task_runner); + request_context_getter_ = new URLRequestContextGetter(io_task_runner); - const std::string serial = base::UTF16ToUTF8(serial_utf16); - const GURL url("http://" + serial + "/claim"); - const std::string form_data = base::StringPrintf( - "session_id=%s", - net::EscapeUrlEncodedData(session_id_, true).c_str()); - const int response_code = SimplePOSTRequest(url, form_data); + static uint32 next_session_id; + base::ProcessId process_id = base::GetCurrentProcId(); + session_id_ = base::StringPrintf("%d-%d", process_id, next_session_id++); - if (response_code == 200) { - device_address_ = serial; - device_ = device; - break; - } + observer_.Add(usb_service_); + } - // The device is probably claimed by another process. - if (response_code != 403) { - LOG(WARNING) << "Unexpected HTTP " << response_code << " from /claim."; - } - } + ~UsbGadgetFactory() override {} + + scoped_ptr<UsbTestGadget> WaitForDevice() { + EnumerateDevices(); + run_loop_.Run(); + return make_scoped_ptr( + new UsbTestGadgetImpl(request_context_getter_, usb_service_, device_)); } - std::string local_version; - std::string version; - if (!ReadLocalVersion(&local_version) || - !GetVersion(&version)) { - return false; + private: + void EnumerateDevices() { + if (!device_) { + usb_service_->GetDevices(base::Bind( + &UsbGadgetFactory::OnDevicesEnumerated, weak_factory_.GetWeakPtr())); + } } - if (version == local_version) { - return true; + void OnDevicesEnumerated( + const std::vector<scoped_refptr<UsbDevice>>& devices) { + for (const scoped_refptr<UsbDevice>& device : devices) { + OnDeviceAdded(device); + } + + if (!device_) { + // TODO(reillyg): This timer could be replaced by a way to use long- + // polling to wait for claimed devices to become unclaimed. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&UsbGadgetFactory::EnumerateDevices, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kReenumeratePeriod)); + } } - return Update(); -} + void OnDeviceAdded(scoped_refptr<UsbDevice> device) override { + if (device_.get()) { + // Already trying to claim a device. + return; + } -bool UsbTestGadgetImpl::GetVersion(std::string* version) { - Delegate delegate; - const GURL url("http://" + device_address_ + "/version"); - scoped_ptr<net::URLFetcher> url_fetcher = - CreateURLFetcher(url, net::URLFetcher::GET, &delegate); + if (device->vendor_id() != 0x18D1 || device->product_id() != 0x58F0 || + device->serial_number().empty()) { + return; + } - url_fetcher->Start(); - delegate.WaitForCompletion(); + std::string serial_number = base::UTF16ToUTF8(device->serial_number()); + if (serial_number == serial_number_) { + // We were waiting for the device to reappear after upgrade. + device_ = device; + run_loop_.Quit(); + return; + } - const int response_code = url_fetcher->GetResponseCode(); - if (response_code != 200) { - VLOG(2) << "Unexpected HTTP " << response_code << " from /version."; - return false; + device_ = device; + serial_number_ = serial_number; + Claim(); } - STLClearObject(version); - if (!url_fetcher->GetResponseAsString(version)) { - VLOG(2) << "Failed to read body from /version."; - return false; - } - return true; -} + void Claim() { + VLOG(1) << "Trying to claim " << serial_number_ << "."; -bool UsbTestGadgetImpl::Update() { - std::string version; - if (!ReadLocalVersion(&version)) { - return false; - } - LOG(INFO) << "Updating " << device_address_ << " to " << version << "..."; - - Delegate delegate; - const GURL url("http://" + device_address_ + "/update"); - scoped_ptr<net::URLFetcher> url_fetcher = - CreateURLFetcher(url, net::URLFetcher::POST, &delegate); - - const std::string mime_header = - base::StringPrintf( - "--foo\r\n" - "Content-Disposition: form-data; name=\"file\"; " - "filename=\"usb_gadget-%s.zip\"\r\n" - "Content-Type: application/octet-stream\r\n" - "\r\n", version.c_str()); - const std::string mime_footer("\r\n--foo--\r\n"); - - std::string package; - if (!ReadLocalPackage(&package)) { - return false; + GURL url("http://" + serial_number_ + "/claim"); + std::string form_data = base::StringPrintf( + "session_id=%s", net::EscapeUrlEncodedData(session_id_, true).c_str()); + url_fetcher_ = CreateURLFetcher(request_context_getter_, url, + net::URLFetcher::POST, this); + url_fetcher_->SetUploadData("application/x-www-form-urlencoded", form_data); + url_fetcher_->Start(); } - url_fetcher->SetUploadData("multipart/form-data; boundary=foo", - mime_header + package + mime_footer); - url_fetcher->Start(); - delegate.WaitForCompletion(); - - const int response_code = url_fetcher->GetResponseCode(); - if (response_code != 200) { - LOG(ERROR) << "Unexpected HTTP " << response_code << " from /update."; - return false; + void GetVersion() { + GURL url("http://" + serial_number_ + "/version"); + url_fetcher_ = CreateURLFetcher(request_context_getter_, url, + net::URLFetcher::GET, this); + url_fetcher_->Start(); } - int retries = kUpdateRetries; - std::string new_version; - while (!GetVersion(&new_version) || new_version != version) { - if (--retries == 0) { - LOG(ERROR) << "Device not responding with new version."; + bool Update(const std::string& version) { + LOG(INFO) << "Updating " << serial_number_ << " to " << version << "..."; + + GURL url("http://" + serial_number_ + "/update"); + url_fetcher_ = CreateURLFetcher(request_context_getter_, url, + net::URLFetcher::POST, this); + std::string mime_header = base::StringPrintf( + "--foo\r\n" + "Content-Disposition: form-data; name=\"file\"; " + "filename=\"usb_gadget-%s.zip\"\r\n" + "Content-Type: application/octet-stream\r\n" + "\r\n", + version.c_str()); + std::string mime_footer("\r\n--foo--\r\n"); + + std::string package; + if (!ReadLocalPackage(&package)) { return false; } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); + + url_fetcher_->SetUploadData("multipart/form-data; boundary=foo", + mime_header + package + mime_footer); + url_fetcher_->Start(); + device_ = nullptr; + return true; } - VLOG(1) << "It took " << (kUpdateRetries - retries) - << " retries to see the new version."; - // Release the old reference to the device and try to open a new one. - device_ = NULL; - retries = kReconnectRetries; - while (!FindClaimed()) { - if (--retries == 0) { - LOG(ERROR) << "Failed to find updated device."; - return false; + void OnURLFetchComplete(const net::URLFetcher* source) override { + DCHECK(!serial_number_.empty()); + + int response_code = source->GetResponseCode(); + if (!claimed_) { + // Just completed a /claim request. + if (response_code == 200) { + claimed_ = true; + GetVersion(); + } else { + if (response_code != 403) { + LOG(WARNING) << "Unexpected HTTP " << response_code + << " from /claim."; + } + Reset(); + } + } else if (version_.empty()) { + // Just completed a /version request. + if (response_code != 200) { + LOG(WARNING) << "Unexpected HTTP " << response_code + << " from /version."; + Reset(); + return; + } + + if (!source->GetResponseAsString(&version_)) { + LOG(WARNING) << "Failed to read body from /version."; + Reset(); + return; + } + + std::string local_version; + if (!ReadLocalVersion(&local_version)) { + Reset(); + return; + } + + if (version_ == local_version) { + run_loop_.Quit(); + } else { + if (!Update(local_version)) { + Reset(); + } + } + } else { + // Just completed an /update request. + if (response_code != 200) { + LOG(WARNING) << "Unexpected HTTP " << response_code << " from /update."; + Reset(); + return; + } + + // Must wait for the device to reconnect. } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); } - VLOG(1) << "It took " << (kReconnectRetries - retries) - << " retries to find the updated device."; - return true; -} + void Reset() { + device_ = nullptr; + serial_number_.clear(); + claimed_ = false; + version_.clear(); + + // Wait a bit and then try again to find an available device. + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, base::Bind(&UsbGadgetFactory::EnumerateDevices, + weak_factory_.GetWeakPtr()), + base::TimeDelta::FromMilliseconds(kReenumeratePeriod)); + } -bool UsbTestGadgetImpl::FindClaimed() { - CHECK(!device_.get()); + UsbService* usb_service_ = nullptr; + scoped_refptr<net::URLRequestContextGetter> request_context_getter_; + std::string session_id_; + scoped_ptr<net::URLFetcher> url_fetcher_; + scoped_refptr<UsbDevice> device_; + std::string serial_number_; + bool claimed_ = false; + std::string version_; + base::RunLoop run_loop_; + ScopedObserver<UsbService, UsbService::Observer> observer_; + base::WeakPtrFactory<UsbGadgetFactory> weak_factory_; +}; - std::string expected_serial = GetSerialNumber(); +class DeviceAddListener : public UsbService::Observer { + public: + DeviceAddListener(UsbService* usb_service, + const std::string& serial_number, + int product_id) + : usb_service_(usb_service), + serial_number_(serial_number), + product_id_(product_id), + observer_(this), + weak_factory_(this) { + observer_.Add(usb_service_); + } + virtual ~DeviceAddListener() {} - std::vector<scoped_refptr<UsbDevice> > devices; - usb_service_->GetDevices(&devices); + scoped_refptr<UsbDevice> WaitForAdd() { + usb_service_->GetDevices(base::Bind(&DeviceAddListener::OnDevicesEnumerated, + weak_factory_.GetWeakPtr())); + run_loop_.Run(); + return device_; + } - for (std::vector<scoped_refptr<UsbDevice> >::iterator iter = - devices.begin(); iter != devices.end(); ++iter) { - scoped_refptr<UsbDevice> &device = *iter; + private: + void OnDevicesEnumerated( + const std::vector<scoped_refptr<UsbDevice>>& devices) { + for (const scoped_refptr<UsbDevice>& device : devices) { + OnDeviceAdded(device); + } + } - if (device->vendor_id() == 0x18D1) { + void OnDeviceAdded(scoped_refptr<UsbDevice> device) override { + if (device->vendor_id() == 0x18D1 && !device->serial_number().empty()) { const uint16 product_id = device->product_id(); - bool found = false; - for (size_t i = 0; i < arraysize(kConfigurations); ++i) { - if (product_id == kConfigurations[i].product_id) { - found = true; - break; + if (product_id_ == -1) { + bool found = false; + for (size_t i = 0; i < arraysize(kConfigurations); ++i) { + if (product_id == kConfigurations[i].product_id) { + found = true; + break; + } + } + if (!found) { + return; + } + } else { + if (product_id_ != product_id) { + return; } - } - if (!found) { - continue; } - base::string16 serial_utf16; - if (!device->GetSerialNumber(&serial_utf16)) { - continue; + if (serial_number_ != base::UTF16ToUTF8(device->serial_number())) { + return; } - std::string serial = base::UTF16ToUTF8(serial_utf16); - if (serial != expected_serial) { - continue; + device_ = device; + run_loop_.Quit(); + } + } + + UsbService* usb_service_; + const std::string serial_number_; + const int product_id_; + base::RunLoop run_loop_; + scoped_refptr<UsbDevice> device_; + ScopedObserver<UsbService, UsbService::Observer> observer_; + base::WeakPtrFactory<DeviceAddListener> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DeviceAddListener); +}; + +class DeviceRemoveListener : public UsbService::Observer { + public: + DeviceRemoveListener(UsbService* usb_service, scoped_refptr<UsbDevice> device) + : usb_service_(usb_service), + device_(device), + observer_(this), + weak_factory_(this) { + observer_.Add(usb_service_); + } + virtual ~DeviceRemoveListener() {} + + void WaitForRemove() { + usb_service_->GetDevices( + base::Bind(&DeviceRemoveListener::OnDevicesEnumerated, + weak_factory_.GetWeakPtr())); + run_loop_.Run(); + } + + private: + void OnDevicesEnumerated( + const std::vector<scoped_refptr<UsbDevice>>& devices) { + bool found = false; + for (const scoped_refptr<UsbDevice>& device : devices) { + if (device_ == device) { + found = true; } + } + if (!found) { + run_loop_.Quit(); + } + } - device_ = device; - return true; + void OnDeviceRemoved(scoped_refptr<UsbDevice> device) override { + if (device_ == device) { + run_loop_.Quit(); } } - return false; -} + UsbService* usb_service_; + base::RunLoop run_loop_; + scoped_refptr<UsbDevice> device_; + ScopedObserver<UsbService, UsbService::Observer> observer_; + base::WeakPtrFactory<DeviceRemoveListener> weak_factory_; -bool UsbTestGadgetImpl::ReadLocalVersion(std::string* version) { - base::FilePath file_path; - CHECK(PathService::Get(base::DIR_EXE, &file_path)); - file_path = file_path.AppendASCII("usb_gadget.zip.md5"); + DISALLOW_COPY_AND_ASSIGN(DeviceRemoveListener); +}; - return ReadFile(file_path, version); +} // namespace + +bool UsbTestGadget::IsTestEnabled() { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + return command_line->HasSwitch(kCommandLineSwitch); } -bool UsbTestGadgetImpl::ReadLocalPackage(std::string* package) { - base::FilePath file_path; - CHECK(PathService::Get(base::DIR_EXE, &file_path)); - file_path = file_path.AppendASCII("usb_gadget.zip"); +scoped_ptr<UsbTestGadget> UsbTestGadget::Claim( + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { + UsbGadgetFactory gadget_factory(io_task_runner); + return gadget_factory.WaitForDevice().Pass(); +} - return ReadFile(file_path, package); +UsbTestGadgetImpl::UsbTestGadgetImpl( + scoped_refptr<net::URLRequestContextGetter> request_context_getter_, + UsbService* usb_service, + scoped_refptr<UsbDevice> device) + : device_address_(base::UTF16ToUTF8(device->serial_number())), + device_(device), + request_context_getter_(request_context_getter_), + usb_service_(usb_service) { } -bool UsbTestGadgetImpl::ReadFile(const base::FilePath& file_path, - std::string* content) { - base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); - if (!file.IsValid()) { - LOG(ERROR) << "Cannot open " << file_path.MaybeAsASCII() << ": " - << base::File::ErrorToString(file.error_details()); - return false; +UsbTestGadgetImpl::~UsbTestGadgetImpl() { + if (!device_address_.empty()) { + Unclaim(); } +} - STLClearObject(content); - int rv; - do { - char buf[4096]; - rv = file.ReadAtCurrentPos(buf, sizeof buf); - if (rv == -1) { - LOG(ERROR) << "Cannot read " << file_path.MaybeAsASCII() << ": " - << base::File::ErrorToString(file.error_details()); - return false; - } - content->append(buf, rv); - } while (rv > 0); - - return true; +UsbDevice* UsbTestGadgetImpl::GetDevice() const { + return device_.get(); } bool UsbTestGadgetImpl::Unclaim() { VLOG(1) << "Releasing the device at " << device_address_ << "."; - const GURL url("http://" + device_address_ + "/unclaim"); - const int response_code = SimplePOSTRequest(url, ""); + GURL url("http://" + device_address_ + "/unclaim"); + int response_code = SimplePOSTRequest(request_context_getter_, url, ""); if (response_code != 200) { LOG(ERROR) << "Unexpected HTTP " << response_code << " from /unclaim."; @@ -446,8 +560,8 @@ bool UsbTestGadgetImpl::SetType(Type type) { } CHECK(config); - const GURL url("http://" + device_address_ + config->http_resource); - const int response_code = SimplePOSTRequest(url, ""); + GURL url("http://" + device_address_ + config->http_resource); + int response_code = SimplePOSTRequest(request_context_getter_, url, ""); if (response_code != 200) { LOG(ERROR) << "Unexpected HTTP " << response_code @@ -456,75 +570,41 @@ bool UsbTestGadgetImpl::SetType(Type type) { } // Release the old reference to the device and try to open a new one. - int retries = kReconnectRetries; - while (true) { - device_ = NULL; - if (FindClaimed() && device_->product_id() == config->product_id) { - break; - } - if (--retries == 0) { - LOG(ERROR) << "Failed to find updated device."; - return false; - } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); - } - VLOG(1) << "It took " << (kReconnectRetries - retries) - << " retries to find the updated device."; - + DeviceAddListener add_listener(usb_service_, device_address_, + config->product_id); + device_ = add_listener.WaitForAdd(); + DCHECK(device_.get()); return true; } bool UsbTestGadgetImpl::Disconnect() { - const GURL url("http://" + device_address_ + "/disconnect"); - const int response_code = SimplePOSTRequest(url, ""); + GURL url("http://" + device_address_ + "/disconnect"); + int response_code = SimplePOSTRequest(request_context_getter_, url, ""); if (response_code != 200) { - LOG(ERROR) << "Unexpected HTTP " << response_code << " from /disconnect."; + LOG(ERROR) << "Unexpected HTTP " << response_code << " from " << url << "."; return false; } // Release the old reference to the device and wait until it can't be found. - int retries = kDisconnectRetries; - while (true) { - device_ = NULL; - if (!FindClaimed()) { - break; - } - if (--retries == 0) { - LOG(ERROR) << "Device did not disconnect."; - return false; - } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); - } - VLOG(1) << "It took " << (kDisconnectRetries - retries) - << " retries for the device to disconnect."; - + DeviceRemoveListener remove_listener(usb_service_, device_); + remove_listener.WaitForRemove(); + device_ = nullptr; return true; } bool UsbTestGadgetImpl::Reconnect() { - const GURL url("http://" + device_address_ + "/reconnect"); - const int response_code = SimplePOSTRequest(url, ""); + GURL url("http://" + device_address_ + "/reconnect"); + int response_code = SimplePOSTRequest(request_context_getter_, url, ""); if (response_code != 200) { - LOG(ERROR) << "Unexpected HTTP " << response_code << " from /reconnect."; + LOG(ERROR) << "Unexpected HTTP " << response_code << " from " << url << "."; return false; } - int retries = kDisconnectRetries; - while (true) { - if (FindClaimed()) { - break; - } - if (--retries == 0) { - LOG(ERROR) << "Device did not reconnect."; - return false; - } - SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); - } - VLOG(1) << "It took " << (kDisconnectRetries - retries) - << " retries for the device to reconnect."; - + DeviceAddListener add_listener(usb_service_, device_address_, -1); + device_ = add_listener.WaitForAdd(); + DCHECK(device_.get()); return true; } diff --git a/device/usb/usb_context.cc b/device/usb/usb_context.cc index cb8214f..7661469 100644 --- a/device/usb/usb_context.cc +++ b/device/usb/usb_context.cc @@ -6,7 +6,6 @@ #include "base/atomicops.h" #include "base/logging.h" -#include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "device/usb/usb_error.h" #include "third_party/libusb/src/libusb/interrupt.h" @@ -25,35 +24,30 @@ class UsbContext::UsbEventHandler : public base::PlatformThread::Delegate { // base::PlatformThread::Delegate void ThreadMain() override; + void Stop(); + private: base::subtle::Atomic32 running_; libusb_context* context_; base::PlatformThreadHandle thread_handle_; - base::WaitableEvent start_polling_; DISALLOW_COPY_AND_ASSIGN(UsbEventHandler); }; UsbContext::UsbEventHandler::UsbEventHandler(libusb_context* context) - : context_(context), thread_handle_(0), start_polling_(false, false) { + : context_(context), thread_handle_(0) { base::subtle::Release_Store(&running_, 1); bool success = base::PlatformThread::Create(0, this, &thread_handle_); DCHECK(success) << "Failed to create USB IO handling thread."; - start_polling_.Wait(); } UsbContext::UsbEventHandler::~UsbEventHandler() { - base::subtle::Release_Store(&running_, 0); - libusb_interrupt_handle_event(context_); - base::PlatformThread::Join(thread_handle_); + libusb_exit(context_); } void UsbContext::UsbEventHandler::ThreadMain() { base::PlatformThread::SetName("UsbEventHandler"); VLOG(1) << "UsbEventHandler started."; - if (base::subtle::Acquire_Load(&running_)) { - start_polling_.Signal(); - } while (base::subtle::Acquire_Load(&running_)) { const int rv = libusb_handle_events(context_); if (rv != LIBUSB_SUCCESS) { @@ -61,20 +55,24 @@ void UsbContext::UsbEventHandler::ThreadMain() { << ConvertPlatformUsbErrorToString(rv); } } + VLOG(1) << "UsbEventHandler shutting down."; + delete this; +} + +void UsbContext::UsbEventHandler::Stop() { + base::subtle::Release_Store(&running_, 0); + libusb_interrupt_handle_event(context_); } UsbContext::UsbContext(PlatformUsbContext context) : context_(context) { - DCHECK(thread_checker_.CalledOnValidThread()); + // Ownership of the PlatformUsbContext is passed to the event handler thread. event_handler_ = new UsbEventHandler(context_); } UsbContext::~UsbContext() { - // destruction of UsbEventHandler is a blocking operation. DCHECK(thread_checker_.CalledOnValidThread()); - delete event_handler_; - event_handler_ = NULL; - libusb_exit(context_); + event_handler_->Stop(); } } // namespace device diff --git a/device/usb/usb_device.cc b/device/usb/usb_device.cc index 4b9ed1e..da926bd 100644 --- a/device/usb/usb_device.cc +++ b/device/usb/usb_device.cc @@ -6,8 +6,18 @@ namespace device { -UsbDevice::UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id) - : vendor_id_(vendor_id), product_id_(product_id), unique_id_(unique_id) { +UsbDevice::UsbDevice(uint16 vendor_id, + uint16 product_id, + uint32 unique_id, + const base::string16& manufacturer_string, + const base::string16& product_string, + const base::string16& serial_number) + : vendor_id_(vendor_id), + product_id_(product_id), + unique_id_(unique_id), + manufacturer_string_(manufacturer_string), + product_string_(product_string), + serial_number_(serial_number) { } UsbDevice::~UsbDevice() { diff --git a/device/usb/usb_device.h b/device/usb/usb_device.h index e824078..2286467 100644 --- a/device/usb/usb_device.h +++ b/device/usb/usb_device.h @@ -16,16 +16,24 @@ class UsbDeviceHandle; struct UsbConfigDescriptor; // A UsbDevice object represents a detected USB device, providing basic -// information about it. For further manipulation of the device, a -// UsbDeviceHandle must be created from Open() method. +// information about it. Methods other than simple property accessors must be +// called from the thread on which this object was created. For further +// manipulation of the device, a UsbDeviceHandle must be created from Open() +// method. class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { public: - typedef base::Callback<void(bool success)> ResultCallback; + using OpenCallback = base::Callback<void(scoped_refptr<UsbDeviceHandle>)>; + using ResultCallback = base::Callback<void(bool success)>; // Accessors to basic information. uint16 vendor_id() const { return vendor_id_; } uint16 product_id() const { return product_id_; } uint32 unique_id() const { return unique_id_; } + const base::string16& manufacturer_string() const { + return manufacturer_string_; + } + const base::string16& product_string() const { return product_string_; } + const base::string16& serial_number() const { return serial_number_; } // On ChromeOS the permission_broker service is used to change the ownership // of USB device nodes so that Chrome can open them. On other platforms these @@ -37,37 +45,23 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { const ResultCallback& callback); // Creates a UsbDeviceHandle for further manipulation. - // Blocking method. Must be called on FILE thread. - virtual scoped_refptr<UsbDeviceHandle> Open() = 0; + virtual void Open(const OpenCallback& callback) = 0; // Explicitly closes a device handle. This method will be automatically called // by the destructor of a UsbDeviceHandle as well. - // Closing a closed handle is a safe - // Blocking method. Must be called on FILE thread. virtual bool Close(scoped_refptr<UsbDeviceHandle> handle) = 0; // Gets the UsbConfigDescriptor for the active device configuration or nullptr // if the device is unconfigured. - // Blocking method. Must be called on FILE thread. virtual const UsbConfigDescriptor* GetConfiguration() = 0; - // Gets the manufacturer string of the device, or false and an empty - // string. This is a blocking method and must be called on FILE thread. - // TODO(reillyg): Make this available from the UI thread. crbug.com/427985 - virtual bool GetManufacturer(base::string16* manufacturer) = 0; - - // Gets the product string of the device, or returns false and an empty - // string. This is a blocking method and must be called on FILE thread. - // TODO(reillyg): Make this available from the UI thread. crbug.com/427985 - virtual bool GetProduct(base::string16* product) = 0; - - // Gets the serial number string of the device, or returns false and an empty - // string. This is a blocking method and must be called on FILE thread. - // TODO(reillyg): Make this available from the UI thread. crbug.com/427985 - virtual bool GetSerialNumber(base::string16* serial) = 0; - protected: - UsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id); + UsbDevice(uint16 vendor_id, + uint16 product_id, + uint32 unique_id, + const base::string16& manufacturer_string, + const base::string16& product_string, + const base::string16& serial_number); virtual ~UsbDevice(); private: @@ -76,6 +70,9 @@ class UsbDevice : public base::RefCountedThreadSafe<UsbDevice> { const uint16 vendor_id_; const uint16 product_id_; const uint32 unique_id_; + const base::string16 manufacturer_string_; + const base::string16 product_string_; + const base::string16 serial_number_; DISALLOW_COPY_AND_ASSIGN(UsbDevice); }; diff --git a/device/usb/usb_device_filter_unittest.cc b/device/usb/usb_device_filter_unittest.cc index d66a462..beb12df 100644 --- a/device/usb/usb_device_filter_unittest.cc +++ b/device/usb/usb_device_filter_unittest.cc @@ -21,14 +21,16 @@ using testing::Return; class MockUsbDevice : public UsbDevice { public: MockUsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id) - : UsbDevice(vendor_id, product_id, unique_id) {} - - MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>()); + : UsbDevice(vendor_id, + product_id, + unique_id, + base::string16(), + base::string16(), + base::string16()) {} + + MOCK_METHOD1(Open, void(const OpenCallback&)); MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>)); MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor*()); - MOCK_METHOD1(GetManufacturer, bool(base::string16*)); - MOCK_METHOD1(GetProduct, bool(base::string16*)); - MOCK_METHOD1(GetSerialNumber, bool(base::string16*)); private: virtual ~MockUsbDevice() {} @@ -46,8 +48,6 @@ class UsbFilterTest : public testing::Test { config_.interfaces.push_back(interface); android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, 0); - ON_CALL(*android_phone_.get(), GetConfiguration()) - .WillByDefault(Return(&config_)); } protected: @@ -89,12 +89,16 @@ TEST_F(UsbFilterTest, MatchProductIdNegative) { TEST_F(UsbFilterTest, MatchInterfaceClass) { UsbDeviceFilter filter; filter.SetInterfaceClass(0xff); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_TRUE(filter.Matches(android_phone_)); } TEST_F(UsbFilterTest, MatchInterfaceClassNegative) { UsbDeviceFilter filter; filter.SetInterfaceClass(0xe0); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_FALSE(filter.Matches(android_phone_)); } @@ -102,6 +106,8 @@ TEST_F(UsbFilterTest, MatchInterfaceSubclass) { UsbDeviceFilter filter; filter.SetInterfaceClass(0xff); filter.SetInterfaceSubclass(0x42); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_TRUE(filter.Matches(android_phone_)); } @@ -109,6 +115,8 @@ TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) { UsbDeviceFilter filter; filter.SetInterfaceClass(0xff); filter.SetInterfaceSubclass(0x01); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_FALSE(filter.Matches(android_phone_)); } @@ -117,6 +125,8 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocol) { filter.SetInterfaceClass(0xff); filter.SetInterfaceSubclass(0x42); filter.SetInterfaceProtocol(0x01); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_TRUE(filter.Matches(android_phone_)); } @@ -125,6 +135,8 @@ TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) { filter.SetInterfaceClass(0xff); filter.SetInterfaceSubclass(0x42); filter.SetInterfaceProtocol(0x02); + EXPECT_CALL(*android_phone_.get(), GetConfiguration()) + .WillOnce(Return(&config_)); ASSERT_FALSE(filter.Matches(android_phone_)); } diff --git a/device/usb/usb_device_handle.h b/device/usb/usb_device_handle.h index 7098d68..2c59f25 100644 --- a/device/usb/usb_device_handle.h +++ b/device/usb/usb_device_handle.h @@ -30,13 +30,13 @@ enum UsbTransferStatus { USB_TRANSFER_LENGTH_SHORT, }; -typedef base::Callback< - void(UsbTransferStatus, scoped_refptr<net::IOBuffer>, size_t)> - UsbTransferCallback; - // UsbDeviceHandle class provides basic I/O related functionalities. class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> { public: + using ResultCallback = base::Callback<void(bool)>; + using TransferCallback = base::Callback< + void(UsbTransferStatus, scoped_refptr<net::IOBuffer>, size_t)>; + enum TransferRequestType { STANDARD, CLASS, VENDOR, RESERVED }; enum TransferRecipient { DEVICE, INTERFACE, ENDPOINT, OTHER }; @@ -49,53 +49,52 @@ class UsbDeviceHandle : public base::RefCountedThreadSafe<UsbDeviceHandle> { // The platform device handle will be closed when UsbDeviceHandle destructs. virtual void Close() = 0; - // Device manipulation operations. These methods are blocking and must be - // called on FILE thread. - virtual bool SetConfiguration(int configuration_value) = 0; - virtual bool ClaimInterface(int interface_number) = 0; + // Device manipulation operations. + virtual void SetConfiguration(int configuration_value, + const ResultCallback& callback) = 0; + virtual void ClaimInterface(int interface_number, + const ResultCallback& callback) = 0; virtual bool ReleaseInterface(int interface_number) = 0; - virtual bool SetInterfaceAlternateSetting(int interface_number, - int alternate_setting) = 0; - virtual bool ResetDevice() = 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; + virtual void SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + const ResultCallback& callback) = 0; + virtual void ResetDevice(const ResultCallback& callback) = 0; - // Async IO. Can be called on any thread. + // The transfer functions may be called from any thread. The provided callback + // will be run on the caller's thread. virtual void ControlTransfer(UsbEndpointDirection direction, TransferRequestType request_type, TransferRecipient recipient, uint8 request, uint16 value, uint16 index, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) = 0; + const TransferCallback& callback) = 0; virtual void BulkTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) = 0; + const TransferCallback& callback) = 0; virtual void InterruptTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) = 0; + const TransferCallback& callback) = 0; virtual void IsochronousTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int packets, unsigned int packet_length, unsigned int timeout, - const UsbTransferCallback& callback) = 0; + const TransferCallback& callback) = 0; protected: friend class base::RefCountedThreadSafe<UsbDeviceHandle>; diff --git a/device/usb/usb_device_handle_impl.cc b/device/usb/usb_device_handle_impl.cc index fc14e82c..921af4f 100644 --- a/device/usb/usb_device_handle_impl.cc +++ b/device/usb/usb_device_handle_impl.cc @@ -157,6 +157,7 @@ bool UsbDeviceHandleImpl::InterfaceClaimer::Claim() const { class UsbDeviceHandleImpl::Transfer { public: static scoped_ptr<Transfer> CreateControlTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 type, uint8 request, uint16 value, @@ -164,62 +165,77 @@ class UsbDeviceHandleImpl::Transfer { uint16 length, scoped_refptr<net::IOBuffer> buffer, unsigned int timeout, - const UsbTransferCallback& callback); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); static scoped_ptr<Transfer> CreateBulkTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, int length, unsigned int timeout, - const UsbTransferCallback& callback); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); static scoped_ptr<Transfer> CreateInterruptTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, int length, unsigned int timeout, - const UsbTransferCallback& callback); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); static scoped_ptr<Transfer> CreateIsochronousTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int packets, unsigned int packet_length, unsigned int timeout, - const UsbTransferCallback& callback); + scoped_refptr<base::TaskRunner> task_runner, + const TransferCallback& callback); ~Transfer(); - bool Submit(base::WeakPtr<UsbDeviceHandleImpl> device_handle); + void Submit(); void Cancel(); void ProcessCompletion(); - void Complete(UsbTransferStatus status, size_t bytes_transferred); + void TransferComplete(UsbTransferStatus status, size_t bytes_transferred); const UsbDeviceHandleImpl::InterfaceClaimer* claimed_interface() const { return claimed_interface_.get(); } + scoped_refptr<base::TaskRunner> callback_task_runner() const { + return callback_task_runner_; + } + private: - Transfer(UsbTransferType transfer_type, + Transfer(scoped_refptr<UsbDeviceHandleImpl> device_handle, + scoped_refptr<InterfaceClaimer> claimed_interface, + UsbTransferType transfer_type, scoped_refptr<net::IOBuffer> buffer, size_t length, - const UsbTransferCallback& callback); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); static void LIBUSB_CALL PlatformCallback(PlatformUsbTransferHandle handle); UsbTransferType transfer_type_; - base::WeakPtr<UsbDeviceHandleImpl> device_handle_; - PlatformUsbTransferHandle platform_transfer_; + scoped_refptr<UsbDeviceHandleImpl> device_handle_; + PlatformUsbTransferHandle platform_transfer_ = nullptr; scoped_refptr<net::IOBuffer> buffer_; scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer> claimed_interface_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; size_t length_; - bool cancelled_; - UsbTransferCallback callback_; - scoped_refptr<base::SingleThreadTaskRunner> callback_task_runner_; + bool cancelled_ = false; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + scoped_refptr<base::TaskRunner> callback_task_runner_; + TransferCallback callback_; }; // static scoped_ptr<UsbDeviceHandleImpl::Transfer> UsbDeviceHandleImpl::Transfer::CreateControlTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 type, uint8 request, uint16 value, @@ -227,10 +243,11 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer( uint16 length, scoped_refptr<net::IOBuffer> buffer, unsigned int timeout, - const UsbTransferCallback& callback) { - scoped_ptr<Transfer> transfer(new Transfer(USB_TRANSFER_CONTROL, buffer, - length + LIBUSB_CONTROL_SETUP_SIZE, - callback)); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + scoped_ptr<Transfer> transfer(new Transfer( + device_handle, nullptr, USB_TRANSFER_CONTROL, buffer, + length + LIBUSB_CONTROL_SETUP_SIZE, callback_task_runner, callback)); transfer->platform_transfer_ = libusb_alloc_transfer(0); if (!transfer->platform_transfer_) { @@ -241,7 +258,7 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer( libusb_fill_control_setup(reinterpret_cast<uint8*>(buffer->data()), type, request, value, index, length); libusb_fill_control_transfer(transfer->platform_transfer_, - nullptr, /* filled in by Submit() */ + device_handle->handle_, reinterpret_cast<uint8*>(buffer->data()), &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), timeout); @@ -252,13 +269,16 @@ UsbDeviceHandleImpl::Transfer::CreateControlTransfer( // static scoped_ptr<UsbDeviceHandleImpl::Transfer> UsbDeviceHandleImpl::Transfer::CreateBulkTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, int length, unsigned int timeout, - const UsbTransferCallback& callback) { - scoped_ptr<Transfer> transfer( - new Transfer(USB_TRANSFER_BULK, buffer, length, callback)); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + scoped_ptr<Transfer> transfer(new Transfer( + device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint), + USB_TRANSFER_BULK, buffer, length, callback_task_runner, callback)); transfer->platform_transfer_ = libusb_alloc_transfer(0); if (!transfer->platform_transfer_) { @@ -266,12 +286,11 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer( return nullptr; } - libusb_fill_bulk_transfer(transfer->platform_transfer_, - nullptr, /* filled in by Submit() */ - endpoint, reinterpret_cast<uint8*>(buffer->data()), - static_cast<int>(length), - &UsbDeviceHandleImpl::Transfer::PlatformCallback, - transfer.get(), timeout); + libusb_fill_bulk_transfer( + transfer->platform_transfer_, device_handle->handle_, endpoint, + reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length), + &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), + timeout); return transfer.Pass(); } @@ -279,13 +298,16 @@ UsbDeviceHandleImpl::Transfer::CreateBulkTransfer( // static scoped_ptr<UsbDeviceHandleImpl::Transfer> UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, int length, unsigned int timeout, - const UsbTransferCallback& callback) { - scoped_ptr<Transfer> transfer( - new Transfer(USB_TRANSFER_INTERRUPT, buffer, length, callback)); + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + scoped_ptr<Transfer> transfer(new Transfer( + device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint), + USB_TRANSFER_INTERRUPT, buffer, length, callback_task_runner, callback)); transfer->platform_transfer_ = libusb_alloc_transfer(0); if (!transfer->platform_transfer_) { @@ -294,9 +316,8 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer( } libusb_fill_interrupt_transfer( - transfer->platform_transfer_, nullptr, /* filled in by Submit() */ - endpoint, reinterpret_cast<uint8*>(buffer->data()), - static_cast<int>(length), + transfer->platform_transfer_, device_handle->handle_, endpoint, + reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length), &UsbDeviceHandleImpl::Transfer::PlatformCallback, transfer.get(), timeout); @@ -306,18 +327,22 @@ UsbDeviceHandleImpl::Transfer::CreateInterruptTransfer( // static scoped_ptr<UsbDeviceHandleImpl::Transfer> UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, uint8 endpoint, scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int packets, unsigned int packet_length, unsigned int timeout, - const UsbTransferCallback& callback) { + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { DCHECK(packets <= length && (packets * packet_length) <= length) << "transfer length is too small"; - scoped_ptr<Transfer> transfer( - new Transfer(USB_TRANSFER_ISOCHRONOUS, buffer, length, callback)); + scoped_ptr<Transfer> transfer(new Transfer( + device_handle, device_handle->GetClaimedInterfaceForEndpoint(endpoint), + USB_TRANSFER_ISOCHRONOUS, buffer, length, callback_task_runner, + callback)); transfer->platform_transfer_ = libusb_alloc_transfer(packets); if (!transfer->platform_transfer_) { @@ -326,27 +351,30 @@ UsbDeviceHandleImpl::Transfer::CreateIsochronousTransfer( } libusb_fill_iso_transfer( - transfer->platform_transfer_, nullptr, /* filled in by Submit() */ - endpoint, reinterpret_cast<uint8*>(buffer->data()), - static_cast<int>(length), packets, &Transfer::PlatformCallback, - transfer.get(), timeout); + transfer->platform_transfer_, device_handle->handle_, endpoint, + reinterpret_cast<uint8*>(buffer->data()), static_cast<int>(length), + packets, &Transfer::PlatformCallback, transfer.get(), timeout); libusb_set_iso_packet_lengths(transfer->platform_transfer_, packet_length); return transfer.Pass(); } -UsbDeviceHandleImpl::Transfer::Transfer(UsbTransferType transfer_type, - scoped_refptr<net::IOBuffer> buffer, - size_t length, - const UsbTransferCallback& callback) +UsbDeviceHandleImpl::Transfer::Transfer( + scoped_refptr<UsbDeviceHandleImpl> device_handle, + scoped_refptr<InterfaceClaimer> claimed_interface, + UsbTransferType transfer_type, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) : transfer_type_(transfer_type), + device_handle_(device_handle), buffer_(buffer), + claimed_interface_(claimed_interface), length_(length), - cancelled_(false), + callback_task_runner_(callback_task_runner), callback_(callback) { - // Remember the thread from which this transfer was created so that |callback| - // can be dispatched there. - callback_task_runner_ = base::ThreadTaskRunnerHandle::Get(); + task_runner_ = base::ThreadTaskRunnerHandle::Get(); } UsbDeviceHandleImpl::Transfer::~Transfer() { @@ -355,26 +383,12 @@ UsbDeviceHandleImpl::Transfer::~Transfer() { } } -bool UsbDeviceHandleImpl::Transfer::Submit( - base::WeakPtr<UsbDeviceHandleImpl> device_handle) { - device_handle_ = device_handle; - // Remember the thread from which this transfer was submitted so that it can - // be marked complete there. - task_runner_ = base::ThreadTaskRunnerHandle::Get(); - // GetClaimedInterfaceForEndpoint may return nullptr. libusb_submit_transfer - // will fail if it requires an interface we didn't claim. - claimed_interface_ = device_handle->GetClaimedInterfaceForEndpoint( - platform_transfer_->endpoint); - platform_transfer_->dev_handle = device_handle_->handle_; - +void UsbDeviceHandleImpl::Transfer::Submit() { const int rv = libusb_submit_transfer(platform_transfer_); - if (rv == LIBUSB_SUCCESS) { - return true; - } else { + if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to submit transfer: " << ConvertPlatformUsbErrorToString(rv); - Complete(USB_TRANSFER_ERROR, 0); - return false; + TransferComplete(USB_TRANSFER_ERROR, 0); } } @@ -453,51 +467,26 @@ void UsbDeviceHandleImpl::Transfer::ProcessCompletion() { break; } - Complete(ConvertTransferStatus(platform_transfer_->status), actual_length); -} - -void UsbDeviceHandleImpl::Transfer::Complete(UsbTransferStatus status, - size_t bytes_transferred) { - if (callback_task_runner_->RunsTasksOnCurrentThread()) { - callback_.Run(status, buffer_, bytes_transferred); - } else { - callback_task_runner_->PostTask( - FROM_HERE, base::Bind(callback_, status, buffer_, bytes_transferred)); - } + TransferComplete(ConvertTransferStatus(platform_transfer_->status), + actual_length); } /* static */ void LIBUSB_CALL UsbDeviceHandleImpl::Transfer::PlatformCallback( PlatformUsbTransferHandle platform_transfer) { - scoped_ptr<Transfer> transfer( - reinterpret_cast<Transfer*>(platform_transfer->user_data)); + Transfer* transfer = + reinterpret_cast<Transfer*>(platform_transfer->user_data); DCHECK(transfer->platform_transfer_ == platform_transfer); - - // Because device_handle_ is a weak pointer it is guaranteed that the callback - // will be discarded if the handle has been freed. - Transfer* tmp_transfer = transfer.get(); // base::Passed invalidates transfer - tmp_transfer->task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbDeviceHandleImpl::CompleteTransfer, - tmp_transfer->device_handle_, - base::Passed(&transfer))); -} - -UsbDeviceHandleImpl::UsbDeviceHandleImpl(scoped_refptr<UsbContext> context, - scoped_refptr<UsbDeviceImpl> device, - PlatformUsbDeviceHandle handle) - : device_(device), - handle_(handle), - context_(context), - task_runner_(base::ThreadTaskRunnerHandle::Get()), - weak_factory_(this) { - DCHECK(handle) << "Cannot create device with NULL handle."; + transfer->ProcessCompletion(); } -UsbDeviceHandleImpl::~UsbDeviceHandleImpl() { - DCHECK(thread_checker_.CalledOnValidThread()); - - libusb_close(handle_); - handle_ = NULL; +void UsbDeviceHandleImpl::Transfer::TransferComplete(UsbTransferStatus status, + size_t bytes_transferred) { + task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::TransferComplete, device_handle_, + base::Owned(this), + base::Bind(callback_, status, buffer_, bytes_transferred))); } scoped_refptr<UsbDevice> UsbDeviceHandleImpl::GetDevice() const { @@ -510,10 +499,12 @@ void UsbDeviceHandleImpl::Close() { device_->Close(this); } -bool UsbDeviceHandleImpl::SetConfiguration(int configuration_value) { +void UsbDeviceHandleImpl::SetConfiguration(int configuration_value, + const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) { - return false; + callback.Run(false); + return; } for (Transfer* transfer : transfers_) { @@ -521,36 +512,31 @@ bool UsbDeviceHandleImpl::SetConfiguration(int configuration_value) { } claimed_interfaces_.clear(); - int rv = libusb_set_configuration(handle_, configuration_value); - if (rv == LIBUSB_SUCCESS) { - device_->RefreshConfiguration(); - RefreshEndpointMap(); - } else { - USB_LOG(EVENT) << "Failed to set configuration " << configuration_value - << ": " << ConvertPlatformUsbErrorToString(rv); - } - return rv == LIBUSB_SUCCESS; + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::SetConfigurationOnBlockingThread, this, + handle_, configuration_value, callback)); } -bool UsbDeviceHandleImpl::ClaimInterface(const int interface_number) { +void UsbDeviceHandleImpl::ClaimInterface(int interface_number, + const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - if (!device_) - return false; - if (ContainsKey(claimed_interfaces_, interface_number)) - return true; - - scoped_refptr<InterfaceClaimer> claimer = - new InterfaceClaimer(this, interface_number); - - if (claimer->Claim()) { - claimed_interfaces_[interface_number] = claimer; - RefreshEndpointMap(); - return true; + if (!device_) { + callback.Run(false); + return; + } + if (ContainsKey(claimed_interfaces_, interface_number)) { + callback.Run(true); + return; } - return false; + + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread, this, + handle_, interface_number, callback)); } -bool UsbDeviceHandleImpl::ReleaseInterface(const int interface_number) { +bool UsbDeviceHandleImpl::ReleaseInterface(int interface_number) { DCHECK(thread_checker_.CalledOnValidThread()); if (!device_) return false; @@ -571,103 +557,282 @@ bool UsbDeviceHandleImpl::ReleaseInterface(const int interface_number) { return true; } -bool UsbDeviceHandleImpl::SetInterfaceAlternateSetting( - const int interface_number, - const int alternate_setting) { +void UsbDeviceHandleImpl::SetInterfaceAlternateSetting( + int interface_number, + int alternate_setting, + const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - if (!device_) - return false; - if (!ContainsKey(claimed_interfaces_, interface_number)) - return false; - const int rv = libusb_set_interface_alt_setting( - handle_, interface_number, alternate_setting); - if (rv == LIBUSB_SUCCESS) { - claimed_interfaces_[interface_number]->set_alternate_setting( - alternate_setting); - RefreshEndpointMap(); + if (!device_ || !ContainsKey(claimed_interfaces_, interface_number)) { + callback.Run(false); + return; + } + + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind( + &UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread, + this, handle_, interface_number, alternate_setting, callback)); +} + +void UsbDeviceHandleImpl::ResetDevice(const ResultCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!device_) { + callback.Run(false); + return; + } + + blocking_task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ResetDeviceOnBlockingThread, + this, handle_, callback)); +} + +void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction, + TransferRequestType request_type, + TransferRecipient recipient, + uint8 request, + uint16 value, + uint16 index, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) { + if (task_runner_->BelongsToCurrentThread()) { + ControlTransferInternal(direction, request_type, recipient, request, value, + index, buffer, length, timeout, task_runner_, + callback); + } else { + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ControlTransferInternal, + this, direction, request_type, recipient, request, + value, index, buffer, length, timeout, + base::ThreadTaskRunnerHandle::Get(), callback)); + } +} + +void UsbDeviceHandleImpl::BulkTransfer(UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) { + if (task_runner_->BelongsToCurrentThread()) { + BulkTransferInternal(direction, endpoint, buffer, length, timeout, + task_runner_, callback); } else { + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::BulkTransferInternal, this, + direction, endpoint, buffer, length, timeout, + base::ThreadTaskRunnerHandle::Get(), callback)); + } +} + +void UsbDeviceHandleImpl::InterruptTransfer(UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + const TransferCallback& callback) { + if (task_runner_->BelongsToCurrentThread()) { + InterruptTransferInternal(direction, endpoint, buffer, length, timeout, + task_runner_, callback); + } else { + task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::InterruptTransferInternal, this, + direction, endpoint, buffer, length, timeout, + base::ThreadTaskRunnerHandle::Get(), callback)); + } +} + +void UsbDeviceHandleImpl::IsochronousTransfer( + UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int packets, + unsigned int packet_length, + unsigned int timeout, + const TransferCallback& callback) { + if (task_runner_->BelongsToCurrentThread()) { + IsochronousTransferInternal(direction, endpoint, buffer, length, packets, + packet_length, timeout, task_runner_, callback); + } else { + task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::IsochronousTransferInternal, this, + direction, endpoint, buffer, length, packets, packet_length, + timeout, base::ThreadTaskRunnerHandle::Get(), callback)); + } +} + +UsbDeviceHandleImpl::UsbDeviceHandleImpl( + scoped_refptr<UsbContext> context, + scoped_refptr<UsbDeviceImpl> device, + PlatformUsbDeviceHandle handle, + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) + : device_(device), + handle_(handle), + context_(context), + task_runner_(base::ThreadTaskRunnerHandle::Get()), + blocking_task_runner_(blocking_task_runner) { + DCHECK(handle) << "Cannot create device with NULL handle."; +} + +UsbDeviceHandleImpl::~UsbDeviceHandleImpl() { + DCHECK(thread_checker_.CalledOnValidThread()); + + libusb_close(handle_); + handle_ = NULL; +} + +void UsbDeviceHandleImpl::SetConfigurationOnBlockingThread( + PlatformUsbDeviceHandle handle, + int configuration_value, + const ResultCallback& callback) { + int rv = libusb_set_configuration(handle_, configuration_value); + if (rv != LIBUSB_SUCCESS) { + USB_LOG(EVENT) << "Failed to set configuration " << configuration_value + << ": " << ConvertPlatformUsbErrorToString(rv); + } + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SetConfigurationComplete, + this, rv == LIBUSB_SUCCESS, callback)); +} + +void UsbDeviceHandleImpl::SetConfigurationComplete( + bool success, + const ResultCallback& callback) { + if (success) { + device_->RefreshConfiguration(); + RefreshEndpointMap(); + } + callback.Run(success); +} + +void UsbDeviceHandleImpl::ClaimInterfaceOnBlockingThread( + PlatformUsbDeviceHandle handle, + int interface_number, + const ResultCallback& callback) { + int rv = libusb_claim_interface(handle, interface_number); + if (rv != LIBUSB_SUCCESS) { + VLOG(1) << "Failed to claim interface: " + << ConvertPlatformUsbErrorToString(rv); + } + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ClaimInterfaceComplete, this, + interface_number, rv == LIBUSB_SUCCESS, callback)); +} + +void UsbDeviceHandleImpl::ClaimInterfaceComplete( + int interface_number, + bool success, + const ResultCallback& callback) { + if (success) { + claimed_interfaces_[interface_number] = + new InterfaceClaimer(this, interface_number); + RefreshEndpointMap(); + } + callback.Run(success); +} + +void UsbDeviceHandleImpl::SetInterfaceAlternateSettingOnBlockingThread( + PlatformUsbDeviceHandle handle, + int interface_number, + int alternate_setting, + const ResultCallback& callback) { + int rv = libusb_set_interface_alt_setting(handle, interface_number, + alternate_setting); + if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to set interface " << interface_number << " to alternate setting " << alternate_setting << ": " << ConvertPlatformUsbErrorToString(rv); } - return rv == LIBUSB_SUCCESS; + task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete, + this, interface_number, alternate_setting, + rv == LIBUSB_SUCCESS, callback)); } -bool UsbDeviceHandleImpl::ResetDevice() { - DCHECK(thread_checker_.CalledOnValidThread()); - if (!device_) - return false; +void UsbDeviceHandleImpl::SetInterfaceAlternateSettingComplete( + int interface_number, + int alternate_setting, + bool success, + const ResultCallback& callback) { + if (success) { + claimed_interfaces_[interface_number]->set_alternate_setting( + alternate_setting); + RefreshEndpointMap(); + } + callback.Run(success); +} - const int rv = libusb_reset_device(handle_); +void UsbDeviceHandleImpl::ResetDeviceOnBlockingThread( + PlatformUsbDeviceHandle handle, + const ResultCallback& callback) { + int rv = libusb_reset_device(handle); if (rv != LIBUSB_SUCCESS) { USB_LOG(EVENT) << "Failed to reset device: " << ConvertPlatformUsbErrorToString(rv); } - return rv == LIBUSB_SUCCESS; + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceHandleImpl::ResetDeviceComplete, this, + rv == LIBUSB_SUCCESS, callback)); } -bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id, - base::string16* string) { - if (!GetSupportedLanguages()) { - return false; - } +void UsbDeviceHandleImpl::ResetDeviceComplete(bool success, + const ResultCallback& callback) { + callback.Run(success); +} - std::map<uint8, base::string16>::const_iterator it = strings_.find(string_id); - if (it != strings_.end()) { - *string = it->second; - return true; - } - - for (size_t i = 0; i < languages_.size(); ++i) { - // Get the string using language ID. - uint16 language_id = languages_[i]; - // The 1-byte length field limits the descriptor to 256-bytes (128 char16s). - base::char16 text[128]; - int size = - libusb_get_string_descriptor(handle_, - string_id, - language_id, - reinterpret_cast<unsigned char*>(&text[0]), - sizeof(text)); - if (size < 0) { - USB_LOG(EVENT) << "Failed to get string descriptor " << string_id - << " (langid " << language_id - << "): " << ConvertPlatformUsbErrorToString(size); - continue; - } else if (size < 2) { - USB_LOG(EVENT) << "String descriptor " << string_id << " (langid " - << language_id << ") has no header."; - continue; - // The first 2 bytes of the descriptor are the total length and type tag. - } else if ((text[0] & 0xff) != size) { - USB_LOG(EVENT) << "String descriptor " << string_id << " (langid " - << language_id << ") size mismatch: " << (text[0] & 0xff) - << " != " << size; - continue; - } else if ((text[0] >> 8) != LIBUSB_DT_STRING) { - USB_LOG(EVENT) << "String descriptor " << string_id << " (langid " - << language_id << ") is not a string descriptor."; - continue; - } +void UsbDeviceHandleImpl::RefreshEndpointMap() { + DCHECK(thread_checker_.CalledOnValidThread()); + endpoint_map_.clear(); + const UsbConfigDescriptor* config = device_->GetConfiguration(); + if (config) { + for (const auto& map_entry : claimed_interfaces_) { + int interface_number = map_entry.first; + const scoped_refptr<InterfaceClaimer>& claimed_iface = map_entry.second; - *string = base::string16(text + 1, (size - 2) / 2); - strings_[string_id] = *string; - return true; + for (const UsbInterfaceDescriptor& iface : config->interfaces) { + if (iface.interface_number == interface_number && + iface.alternate_setting == claimed_iface->alternate_setting()) { + for (const UsbEndpointDescriptor& endpoint : iface.endpoints) { + endpoint_map_[endpoint.address] = interface_number; + } + break; + } + } + } } +} - return false; +scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer> +UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(unsigned char endpoint) { + if (ContainsKey(endpoint_map_, endpoint)) + return claimed_interfaces_[endpoint_map_[endpoint]]; + return NULL; } -void UsbDeviceHandleImpl::ControlTransfer(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) { +void UsbDeviceHandleImpl::ControlTransferInternal( + UsbEndpointDirection direction, + TransferRequestType request_type, + TransferRecipient recipient, + uint8 request, + uint16 value, + uint16 index, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!device_) { + callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0); + return; + } + if (length > UINT16_MAX) { USB_LOG(USER) << "Transfer too long."; callback.Run(USB_TRANSFER_ERROR, buffer, 0); @@ -685,22 +850,32 @@ void UsbDeviceHandleImpl::ControlTransfer(UsbEndpointDirection direction, length); scoped_ptr<Transfer> transfer = Transfer::CreateControlTransfer( - CreateRequestType(direction, request_type, recipient), request, value, - index, static_cast<uint16>(length), resized_buffer, timeout, callback); + this, CreateRequestType(direction, request_type, recipient), request, + value, index, static_cast<uint16>(length), resized_buffer, timeout, + callback_task_runner, callback); if (!transfer) { callback.Run(USB_TRANSFER_ERROR, buffer, 0); return; } - PostOrSubmitTransfer(transfer.Pass()); + SubmitTransfer(transfer.Pass()); } -void UsbDeviceHandleImpl::BulkTransfer(const UsbEndpointDirection direction, - const uint8 endpoint, - net::IOBuffer* buffer, - const size_t length, - const unsigned int timeout, - const UsbTransferCallback& callback) { +void UsbDeviceHandleImpl::BulkTransferInternal( + const UsbEndpointDirection direction, + const uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + const size_t length, + const unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!device_) { + callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0); + return; + } + if (length > INT_MAX) { USB_LOG(USER) << "Transfer too long."; callback.Run(USB_TRANSFER_ERROR, buffer, 0); @@ -708,19 +883,27 @@ void UsbDeviceHandleImpl::BulkTransfer(const UsbEndpointDirection direction, } scoped_ptr<Transfer> transfer = Transfer::CreateBulkTransfer( - ConvertTransferDirection(direction) | endpoint, buffer, - static_cast<int>(length), timeout, callback); + this, ConvertTransferDirection(direction) | endpoint, buffer, + static_cast<int>(length), timeout, callback_task_runner, callback); - PostOrSubmitTransfer(transfer.Pass()); + SubmitTransfer(transfer.Pass()); } -void UsbDeviceHandleImpl::InterruptTransfer( +void UsbDeviceHandleImpl::InterruptTransferInternal( UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) { + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!device_) { + callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0); + return; + } + if (length > INT_MAX) { USB_LOG(USER) << "Transfer too long."; callback.Run(USB_TRANSFER_ERROR, buffer, 0); @@ -728,21 +911,29 @@ void UsbDeviceHandleImpl::InterruptTransfer( } scoped_ptr<Transfer> transfer = Transfer::CreateInterruptTransfer( - ConvertTransferDirection(direction) | endpoint, buffer, - static_cast<int>(length), timeout, callback); + this, ConvertTransferDirection(direction) | endpoint, buffer, + static_cast<int>(length), timeout, callback_task_runner, callback); - PostOrSubmitTransfer(transfer.Pass()); + SubmitTransfer(transfer.Pass()); } -void UsbDeviceHandleImpl::IsochronousTransfer( +void UsbDeviceHandleImpl::IsochronousTransferInternal( 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) { + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int packets, + unsigned int packet_length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (!device_) { + callback.Run(USB_TRANSFER_DISCONNECT, buffer, 0); + return; + } + if (length > INT_MAX) { USB_LOG(USER) << "Transfer too long."; callback.Run(USB_TRANSFER_ERROR, buffer, 0); @@ -750,104 +941,35 @@ void UsbDeviceHandleImpl::IsochronousTransfer( } scoped_ptr<Transfer> transfer = Transfer::CreateIsochronousTransfer( - ConvertTransferDirection(direction) | endpoint, buffer, - static_cast<int>(length), packets, packet_length, timeout, callback); + this, ConvertTransferDirection(direction) | endpoint, buffer, + static_cast<int>(length), packets, packet_length, timeout, + callback_task_runner, callback); - PostOrSubmitTransfer(transfer.Pass()); + SubmitTransfer(transfer.Pass()); } -void UsbDeviceHandleImpl::RefreshEndpointMap() { +void UsbDeviceHandleImpl::SubmitTransfer(scoped_ptr<Transfer> transfer) { DCHECK(thread_checker_.CalledOnValidThread()); - endpoint_map_.clear(); - const UsbConfigDescriptor* config = device_->GetConfiguration(); - if (config) { - for (const auto& map_entry : claimed_interfaces_) { - int interface_number = map_entry.first; - const scoped_refptr<InterfaceClaimer>& claimed_iface = map_entry.second; - for (const UsbInterfaceDescriptor& iface : config->interfaces) { - if (iface.interface_number == interface_number && - iface.alternate_setting == claimed_iface->alternate_setting()) { - for (const UsbEndpointDescriptor& endpoint : iface.endpoints) { - endpoint_map_[endpoint.address] = interface_number; - } - break; - } - } - } - } -} - -scoped_refptr<UsbDeviceHandleImpl::InterfaceClaimer> -UsbDeviceHandleImpl::GetClaimedInterfaceForEndpoint(unsigned char endpoint) { - if (ContainsKey(endpoint_map_, endpoint)) - return claimed_interfaces_[endpoint_map_[endpoint]]; - return NULL; -} - -void UsbDeviceHandleImpl::PostOrSubmitTransfer(scoped_ptr<Transfer> transfer) { - if (task_runner_->RunsTasksOnCurrentThread()) { - SubmitTransfer(transfer.Pass()); - } else { - task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbDeviceHandleImpl::SubmitTransfer, this, - base::Passed(&transfer))); - } + // Transfer is owned by libusb until its completion callback is run. This + // object holds a weak reference. + transfers_.insert(transfer.get()); + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&Transfer::Submit, base::Unretained(transfer.release()))); } -void UsbDeviceHandleImpl::SubmitTransfer(scoped_ptr<Transfer> transfer) { +void UsbDeviceHandleImpl::TransferComplete(Transfer* transfer, + const base::Closure& callback) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(ContainsKey(transfers_, transfer)) << "Missing transfer completed"; + transfers_.erase(transfer); - if (device_) { - if (transfer->Submit(weak_factory_.GetWeakPtr())) { - // Transfer is now owned by libusb until its completion callback is run. - // This object holds a weak reference. - transfers_.insert(transfer.release()); - } + if (transfer->callback_task_runner()->RunsTasksOnCurrentThread()) { + callback.Run(); } else { - transfer->Complete(USB_TRANSFER_DISCONNECT, 0); - } -} - -void UsbDeviceHandleImpl::CompleteTransfer(scoped_ptr<Transfer> transfer) { - DCHECK(ContainsKey(transfers_, transfer.get())) - << "Missing transfer completed"; - transfers_.erase(transfer.get()); - transfer->ProcessCompletion(); -} - -bool UsbDeviceHandleImpl::GetSupportedLanguages() { - if (!languages_.empty()) { - return true; + transfer->callback_task_runner()->PostTask(FROM_HERE, callback); } - - // The 1-byte length field limits the descriptor to 256-bytes (128 uint16s). - uint16 languages[128]; - int size = libusb_get_string_descriptor( - handle_, - 0, - 0, - reinterpret_cast<unsigned char*>(&languages[0]), - sizeof(languages)); - if (size < 0) { - USB_LOG(EVENT) << "Failed to get list of supported languages: " - << ConvertPlatformUsbErrorToString(size); - return false; - } else if (size < 2) { - USB_LOG(EVENT) << "String descriptor zero has no header."; - return false; - // The first 2 bytes of the descriptor are the total length and type tag. - } else if ((languages[0] & 0xff) != size) { - USB_LOG(EVENT) << "String descriptor zero size mismatch: " - << (languages[0] & 0xff) << " != " << size; - return false; - } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) { - USB_LOG(EVENT) << "String descriptor zero is not a string descriptor."; - return false; - } - - languages_.assign(languages[1], languages[(size - 2) / 2]); - return true; } void UsbDeviceHandleImpl::InternalClose() { diff --git a/device/usb/usb_device_handle_impl.h b/device/usb/usb_device_handle_impl.h index 688f8b9..a60f502 100644 --- a/device/usb/usb_device_handle_impl.h +++ b/device/usb/usb_device_handle_impl.h @@ -11,15 +11,18 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" -#include "base/memory/weak_ptr.h" -#include "base/strings/string16.h" #include "base/threading/thread_checker.h" #include "device/usb/usb_device_handle.h" -#include "net/base/io_buffer.h" #include "third_party/libusb/src/libusb/libusb.h" namespace base { +class SequencedTaskRunner; class SingleThreadTaskRunner; +class TaskRunner; +} + +namespace net { +class IOBuffer; } namespace device { @@ -37,13 +40,15 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { public: scoped_refptr<UsbDevice> GetDevice() const override; void Close() override; - bool SetConfiguration(int configuration_value) override; - bool ClaimInterface(int interface_number) override; + void SetConfiguration(int configuration_value, + const ResultCallback& callback) override; + void ClaimInterface(int interface_number, + const ResultCallback& callback) override; bool ReleaseInterface(int interface_number) override; - bool SetInterfaceAlternateSetting(int interface_number, - int alternate_setting) override; - bool ResetDevice() override; - bool GetStringDescriptor(uint8 string_id, base::string16* string) override; + void SetInterfaceAlternateSetting(int interface_number, + int alternate_setting, + const ResultCallback& callback) override; + void ResetDevice(const ResultCallback& callback) override; void ControlTransfer(UsbEndpointDirection direction, TransferRequestType request_type, @@ -51,52 +56,75 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { uint8 request, uint16 value, uint16 index, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) override; + const TransferCallback& callback) override; void BulkTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) override; + const TransferCallback& callback) override; void InterruptTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback) override; + const TransferCallback& callback) override; void IsochronousTransfer(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int packets, unsigned int packet_length, unsigned int timeout, - const UsbTransferCallback& callback) override; - - PlatformUsbDeviceHandle handle() const { return handle_; } + const TransferCallback& callback) override; protected: friend class UsbDeviceImpl; - // This constructor is called by UsbDevice. - UsbDeviceHandleImpl(scoped_refptr<UsbContext> context, - scoped_refptr<UsbDeviceImpl> device, - PlatformUsbDeviceHandle handle); + // This constructor is called by UsbDeviceImpl. + UsbDeviceHandleImpl( + scoped_refptr<UsbContext> context, + scoped_refptr<UsbDeviceImpl> device, + PlatformUsbDeviceHandle handle, + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); ~UsbDeviceHandleImpl() override; - private: - friend class Transfer; + PlatformUsbDeviceHandle handle() const { return handle_; } + private: class InterfaceClaimer; class Transfer; + void SetConfigurationOnBlockingThread(PlatformUsbDeviceHandle handle, + int configuration_value, + const ResultCallback& callback); + void SetConfigurationComplete(bool success, const ResultCallback& callback); + void ClaimInterfaceOnBlockingThread(PlatformUsbDeviceHandle handle, + int interface_number, + const ResultCallback& callback); + void ClaimInterfaceComplete(int interface_number, + bool success, + const ResultCallback& callback); + void SetInterfaceAlternateSettingOnBlockingThread( + PlatformUsbDeviceHandle handle, + int interface_number, + int alternate_setting, + const ResultCallback& callback); + void SetInterfaceAlternateSettingComplete(int interface_number, + int alternate_setting, + bool success, + const ResultCallback& callback); + void ResetDeviceOnBlockingThread(PlatformUsbDeviceHandle handle, + const ResultCallback& callback); + void ResetDeviceComplete(bool success, const ResultCallback& callback); + // Refresh endpoint_map_ after ClaimInterface, ReleaseInterface and // SetInterfaceAlternateSetting. void RefreshEndpointMap(); @@ -106,22 +134,56 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { scoped_refptr<InterfaceClaimer> GetClaimedInterfaceForEndpoint( unsigned char endpoint); - // If the device's task runner is on the current thread then the transfer will - // be submitted directly, otherwise a task to do so it posted. The callback - // will be called on the current message loop of the thread where this - // function was called. - void PostOrSubmitTransfer(scoped_ptr<Transfer> transfer); + void ControlTransferInternal( + UsbEndpointDirection direction, + TransferRequestType request_type, + TransferRecipient recipient, + uint8 request, + uint16 value, + uint16 index, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); + + void BulkTransferInternal( + UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); + + void InterruptTransferInternal( + UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); + + void IsochronousTransferInternal( + UsbEndpointDirection direction, + uint8 endpoint, + scoped_refptr<net::IOBuffer> buffer, + size_t length, + unsigned int packets, + unsigned int packet_length, + unsigned int timeout, + scoped_refptr<base::TaskRunner> callback_task_runner, + const TransferCallback& callback); // Submits a transfer and starts tracking it. Retains the buffer and copies // the completion callback until the transfer finishes, whereupon it invokes // the callback then releases the buffer. void SubmitTransfer(scoped_ptr<Transfer> transfer); - // Invokes the callbacks associated with a given transfer, and removes it from - // the in-flight transfer set. - void CompleteTransfer(scoped_ptr<Transfer> transfer); - - bool GetSupportedLanguages(); + // Removes the transfer from the in-flight transfer set and invokes the + // completion callback. + void TransferComplete(Transfer* transfer, const base::Closure& callback); // Informs the object to drop internal references. void InternalClose(); @@ -130,9 +192,6 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { PlatformUsbDeviceHandle handle_; - std::vector<uint16> languages_; - std::map<uint8, base::string16> strings_; - typedef std::map<int, scoped_refptr<InterfaceClaimer>> ClaimedInterfaceMap; ClaimedInterfaceMap claimed_interfaces_; @@ -148,9 +207,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { scoped_refptr<UsbContext> context_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; base::ThreadChecker thread_checker_; - base::WeakPtrFactory<UsbDeviceHandleImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(UsbDeviceHandleImpl); }; diff --git a/device/usb/usb_device_handle_unittest.cc b/device/usb/usb_device_handle_unittest.cc index 4cb4dff..4d5586e 100644 --- a/device/usb/usb_device_handle_unittest.cc +++ b/device/usb/usb_device_handle_unittest.cc @@ -6,6 +6,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/test_io_thread.h" #include "device/test/usb_test_gadget.h" #include "device/usb/usb_device.h" #include "device/usb/usb_device_handle.h" @@ -18,35 +19,63 @@ namespace { class UsbDeviceHandleTest : public ::testing::Test { public: void SetUp() override { - if (!UsbTestGadget::IsTestEnabled()) { - return; - } + message_loop_.reset(new base::MessageLoopForUI); + io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart)); + } - message_loop_.reset(new base::MessageLoopForIO); + protected: + scoped_ptr<base::TestIOThread> io_thread_; - gadget_ = UsbTestGadget::Claim(); - ASSERT_TRUE(gadget_.get()); + private: + scoped_ptr<base::MessageLoop> message_loop_; +}; - ASSERT_TRUE(gadget_->SetType(UsbTestGadget::ECHO)); +class TestOpenCallback { + public: + TestOpenCallback() + : callback_( + base::Bind(&TestOpenCallback::SetResult, base::Unretained(this))) {} - handle_ = gadget_->GetDevice()->Open(); - ASSERT_TRUE(handle_.get()); + scoped_refptr<UsbDeviceHandle> WaitForResult() { + run_loop_.Run(); + return device_handle_; } - void TearDown() override { - if (handle_.get()) { - handle_->Close(); - } - gadget_.reset(NULL); - message_loop_.reset(NULL); + const UsbDevice::OpenCallback& callback() const { return callback_; } + + private: + void SetResult(scoped_refptr<UsbDeviceHandle> device_handle) { + device_handle_ = device_handle; + run_loop_.Quit(); } - protected: - scoped_refptr<UsbDeviceHandle> handle_; + const UsbDevice::OpenCallback callback_; + base::RunLoop run_loop_; + scoped_refptr<UsbDeviceHandle> device_handle_; +}; + +class TestResultCallback { + public: + TestResultCallback() + : callback_(base::Bind(&TestResultCallback::SetResult, + base::Unretained(this))) {} + + bool WaitForResult() { + run_loop_.Run(); + return success_; + } + + const UsbDeviceHandle::ResultCallback& callback() const { return callback_; } private: - scoped_ptr<UsbTestGadget> gadget_; - scoped_ptr<base::MessageLoop> message_loop_; + void SetResult(bool success) { + success_ = success; + run_loop_.Quit(); + } + + const UsbDeviceHandle::ResultCallback callback_; + base::RunLoop run_loop_; + bool success_; }; class TestCompletionCallback { @@ -55,6 +84,15 @@ class TestCompletionCallback { : callback_(base::Bind(&TestCompletionCallback::SetResult, base::Unretained(this))) {} + void WaitForResult() { run_loop_.Run(); } + + const UsbDeviceHandle::TransferCallback& callback() const { + return callback_; + } + UsbTransferStatus status() const { return status_; } + size_t transferred() const { return transferred_; } + + private: void SetResult(UsbTransferStatus status, scoped_refptr<net::IOBuffer> buffer, size_t transferred) { @@ -63,34 +101,37 @@ class TestCompletionCallback { run_loop_.Quit(); } - void WaitForResult() { run_loop_.Run(); } - - const UsbTransferCallback& callback() const { return callback_; } - UsbTransferStatus status() const { return status_; } - size_t transferred() const { return transferred_; } - - private: - const UsbTransferCallback callback_; + const UsbDeviceHandle::TransferCallback callback_; base::RunLoop run_loop_; UsbTransferStatus status_; size_t transferred_; }; TEST_F(UsbDeviceHandleTest, InterruptTransfer) { - if (!handle_.get()) { + if (!UsbTestGadget::IsTestEnabled()) { return; } - ASSERT_TRUE(handle_->ClaimInterface(0)); + scoped_ptr<UsbTestGadget> gadget = + UsbTestGadget::Claim(io_thread_->task_runner()); + ASSERT_TRUE(gadget.get()); + ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO)); + + TestOpenCallback open_device; + gadget->GetDevice()->Open(open_device.callback()); + scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult(); + ASSERT_TRUE(handle.get()); + + TestResultCallback claim_interface; + handle->ClaimInterface(0, claim_interface.callback()); + ASSERT_TRUE(claim_interface.WaitForResult()); scoped_refptr<net::IOBufferWithSize> in_buffer(new net::IOBufferWithSize(64)); TestCompletionCallback in_completion; - handle_->InterruptTransfer(USB_DIRECTION_INBOUND, - 0x81, - in_buffer.get(), - in_buffer->size(), - 5000, // 5 second timeout - in_completion.callback()); + handle->InterruptTransfer(USB_DIRECTION_INBOUND, 0x81, in_buffer.get(), + in_buffer->size(), + 5000, // 5 second timeout + in_completion.callback()); scoped_refptr<net::IOBufferWithSize> out_buffer( new net::IOBufferWithSize(in_buffer->size())); @@ -99,12 +140,10 @@ TEST_F(UsbDeviceHandleTest, InterruptTransfer) { out_buffer->data()[i] = i; } - handle_->InterruptTransfer(USB_DIRECTION_OUTBOUND, - 0x01, - out_buffer.get(), - out_buffer->size(), - 5000, // 5 second timeout - out_completion.callback()); + handle->InterruptTransfer(USB_DIRECTION_OUTBOUND, 0x01, out_buffer.get(), + out_buffer->size(), + 5000, // 5 second timeout + out_completion.callback()); out_completion.WaitForResult(); ASSERT_EQ(USB_TRANSFER_COMPLETED, out_completion.status()); EXPECT_EQ(static_cast<size_t>(out_buffer->size()), @@ -117,22 +156,36 @@ TEST_F(UsbDeviceHandleTest, InterruptTransfer) { for (size_t i = 0; i < in_completion.transferred(); ++i) { EXPECT_EQ(out_buffer->data()[i], in_buffer->data()[i]); } + + handle->Close(); } TEST_F(UsbDeviceHandleTest, BulkTransfer) { - if (!handle_.get()) { + if (!UsbTestGadget::IsTestEnabled()) { return; } - ASSERT_TRUE(handle_->ClaimInterface(1)); + scoped_ptr<UsbTestGadget> gadget = + UsbTestGadget::Claim(io_thread_->task_runner()); + ASSERT_TRUE(gadget.get()); + ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO)); + + TestOpenCallback open_device; + gadget->GetDevice()->Open(open_device.callback()); + scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult(); + ASSERT_TRUE(handle.get()); + + TestResultCallback claim_interface; + handle->ClaimInterface(1, claim_interface.callback()); + ASSERT_TRUE(claim_interface.WaitForResult()); scoped_refptr<net::IOBufferWithSize> in_buffer( new net::IOBufferWithSize(512)); TestCompletionCallback in_completion; - handle_->BulkTransfer(USB_DIRECTION_INBOUND, 0x82, in_buffer.get(), - in_buffer->size(), - 5000, // 5 second timeout - in_completion.callback()); + handle->BulkTransfer(USB_DIRECTION_INBOUND, 0x82, in_buffer.get(), + in_buffer->size(), + 5000, // 5 second timeout + in_completion.callback()); scoped_refptr<net::IOBufferWithSize> out_buffer( new net::IOBufferWithSize(in_buffer->size())); @@ -141,10 +194,10 @@ TEST_F(UsbDeviceHandleTest, BulkTransfer) { out_buffer->data()[i] = i; } - handle_->BulkTransfer(USB_DIRECTION_OUTBOUND, 0x02, out_buffer.get(), - out_buffer->size(), - 5000, // 5 second timeout - out_completion.callback()); + handle->BulkTransfer(USB_DIRECTION_OUTBOUND, 0x02, out_buffer.get(), + out_buffer->size(), + 5000, // 5 second timeout + out_completion.callback()); out_completion.WaitForResult(); ASSERT_EQ(USB_TRANSFER_COMPLETED, out_completion.status()); EXPECT_EQ(static_cast<size_t>(out_buffer->size()), @@ -157,6 +210,8 @@ TEST_F(UsbDeviceHandleTest, BulkTransfer) { for (size_t i = 0; i < in_completion.transferred(); ++i) { EXPECT_EQ(out_buffer->data()[i], in_buffer->data()[i]); } + + handle->Close(); } } // namespace diff --git a/device/usb/usb_device_impl.cc b/device/usb/usb_device_impl.cc index 02ceb6f..f04092f 100644 --- a/device/usb/usb_device_impl.cc +++ b/device/usb/usb_device_impl.cc @@ -8,10 +8,9 @@ #include "base/bind.h" #include "base/location.h" +#include "base/sequenced_task_runner.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 "components/device_event_log/device_event_log.h" #include "device/usb/usb_context.h" @@ -25,25 +24,10 @@ #include "chromeos/dbus/permission_broker_client.h" #endif // defined(OS_CHROMEOS) -#if defined(USE_UDEV) -#include "device/udev_linux/scoped_udev.h" -#endif // defined(USE_UDEV) - namespace device { namespace { -#if defined(OS_CHROMEOS) - -void PostResultOnTaskRunner( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const base::Callback<void(bool success)>& callback, - bool success) { - task_runner->PostTask(FROM_HERE, base::Bind(callback, success)); -} - -#endif // defined(OS_CHROMEOS) - UsbEndpointDirection GetDirection( const libusb_endpoint_descriptor* descriptor) { switch (descriptor->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) { @@ -108,69 +92,27 @@ UsbUsageType GetUsageType(const libusb_endpoint_descriptor* descriptor) { UsbDeviceImpl::UsbDeviceImpl( scoped_refptr<UsbContext> context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, PlatformUsbDevice platform_device, uint16 vendor_id, uint16 product_id, - uint32 unique_id) - : UsbDevice(vendor_id, product_id, unique_id), + uint32 unique_id, + const base::string16& manufacturer_string, + const base::string16& product_string, + const base::string16& serial_number, + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) + : UsbDevice(vendor_id, + product_id, + unique_id, + manufacturer_string, + product_string, + serial_number), platform_device_(platform_device), context_(context), - ui_task_runner_(ui_task_runner) { + task_runner_(base::ThreadTaskRunnerHandle::Get()), + blocking_task_runner_(blocking_task_runner) { CHECK(platform_device) << "platform_device cannot be NULL"; libusb_ref_device(platform_device); RefreshConfiguration(); -#if defined(USE_UDEV) - 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; - } - -#if defined(OS_CHROMEOS) - value = udev_device_get_devnode(device.get()); - if (value) { - devnode_ = value; - } -#endif - value = udev_device_get_sysattr_value(device.get(), "manufacturer"); - if (value) { - manufacturer_ = base::UTF8ToUTF16(value); - } - value = udev_device_get_sysattr_value(device.get(), "product"); - if (value) { - product_ = base::UTF8ToUTF16(value); - } - value = udev_device_get_sysattr_value(device.get(), "serial"); - if (value) { - serial_number_ = base::UTF8ToUTF16(value); - } - break; - } - } -#else - strings_cached_ = false; -#endif } UsbDeviceImpl::~UsbDeviceImpl() { @@ -182,51 +124,28 @@ UsbDeviceImpl::~UsbDeviceImpl() { void UsbDeviceImpl::CheckUsbAccess(const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - chromeos::PermissionBrokerClient* client = chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); DCHECK(client) << "Could not get permission broker client."; - - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&chromeos::PermissionBrokerClient::CheckPathAccess, - base::Unretained(client), devnode_, - base::Bind(&PostResultOnTaskRunner, - base::ThreadTaskRunnerHandle::Get(), callback))); + client->CheckPathAccess(devnode_, callback); } void UsbDeviceImpl::RequestUsbAccess(int interface_id, const ResultCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - chromeos::PermissionBrokerClient* client = chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); DCHECK(client) << "Could not get permission broker client."; - - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&chromeos::PermissionBrokerClient::RequestPathAccess, - base::Unretained(client), devnode_, interface_id, - base::Bind(&PostResultOnTaskRunner, - base::ThreadTaskRunnerHandle::Get(), callback))); + client->RequestPathAccess(devnode_, interface_id, callback); } #endif -scoped_refptr<UsbDeviceHandle> UsbDeviceImpl::Open() { +void UsbDeviceImpl::Open(const OpenCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - PlatformUsbDeviceHandle handle; - const int rv = libusb_open(platform_device_, &handle); - if (LIBUSB_SUCCESS == rv) { - scoped_refptr<UsbDeviceHandleImpl> device_handle = - new UsbDeviceHandleImpl(context_, this, handle); - handles_.push_back(device_handle); - return device_handle; - } else { - USB_LOG(EVENT) << "Failed to open device: " - << ConvertPlatformUsbErrorToString(rv); - return NULL; - } + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbDeviceImpl::OpenOnBlockingThread, this, callback)); } bool UsbDeviceImpl::Close(scoped_refptr<UsbDeviceHandle> handle) { @@ -248,45 +167,6 @@ const UsbConfigDescriptor* UsbDeviceImpl::GetConfiguration() { return configuration_.get(); } -bool UsbDeviceImpl::GetManufacturer(base::string16* manufacturer) { - DCHECK(thread_checker_.CalledOnValidThread()); - -#if !defined(USE_UDEV) - if (!strings_cached_) { - CacheStrings(); - } -#endif - - *manufacturer = manufacturer_; - return !manufacturer_.empty(); -} - -bool UsbDeviceImpl::GetProduct(base::string16* product) { - DCHECK(thread_checker_.CalledOnValidThread()); - -#if !defined(USE_UDEV) - if (!strings_cached_) { - CacheStrings(); - } -#endif - - *product = product_; - return !product_.empty(); -} - -bool UsbDeviceImpl::GetSerialNumber(base::string16* serial_number) { - DCHECK(thread_checker_.CalledOnValidThread()); - -#if !defined(USE_UDEV) - if (!strings_cached_) { - CacheStrings(); - } -#endif - - *serial_number = serial_number_; - return !serial_number_.empty(); -} - void UsbDeviceImpl::OnDisconnect() { DCHECK(thread_checker_.CalledOnValidThread()); @@ -365,35 +245,26 @@ void UsbDeviceImpl::RefreshConfiguration() { libusb_free_config_descriptor(platform_config); } -#if !defined(USE_UDEV) -void UsbDeviceImpl::CacheStrings() { - DCHECK(thread_checker_.CalledOnValidThread()); - // 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) { - scoped_refptr<UsbDeviceHandle> device_handle = Open(); - if (device_handle.get()) { - if (desc.iManufacturer != 0) { - device_handle->GetStringDescriptor(desc.iManufacturer, &manufacturer_); - } - if (desc.iProduct != 0) { - device_handle->GetStringDescriptor(desc.iProduct, &product_); - } - if (desc.iSerialNumber != 0) { - device_handle->GetStringDescriptor(desc.iSerialNumber, &serial_number_); - } - device_handle->Close(); - } else { - USB_LOG(EVENT) << "Failed to open device to cache string descriptors."; - } +void UsbDeviceImpl::OpenOnBlockingThread(const OpenCallback& callback) { + PlatformUsbDeviceHandle handle; + const int rv = libusb_open(platform_device_, &handle); + if (LIBUSB_SUCCESS == rv) { + task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbDeviceImpl::Opened, this, handle, callback)); } else { - USB_LOG(EVENT) - << "Failed to read device descriptor to cache string descriptors: " - << ConvertPlatformUsbErrorToString(rv); + USB_LOG(EVENT) << "Failed to open device: " + << ConvertPlatformUsbErrorToString(rv); + task_runner_->PostTask(FROM_HERE, base::Bind(callback, nullptr)); } - strings_cached_ = true; } -#endif // !defined(USE_UDEV) + +void UsbDeviceImpl::Opened(PlatformUsbDeviceHandle platform_handle, + const OpenCallback& callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + scoped_refptr<UsbDeviceHandleImpl> device_handle = new UsbDeviceHandleImpl( + context_, this, platform_handle, blocking_task_runner_); + handles_.push_back(device_handle); + callback.Run(device_handle); +} } // namespace device diff --git a/device/usb/usb_device_impl.h b/device/usb/usb_device_impl.h index 897b4910..a865ea2 100644 --- a/device/usb/usb_device_impl.h +++ b/device/usb/usb_device_impl.h @@ -15,9 +15,10 @@ struct libusb_device; struct libusb_config_descriptor; +struct libusb_device_handle; namespace base { -class SingleThreadTaskRunner; +class SequencedTaskRunner; } namespace device { @@ -25,8 +26,9 @@ namespace device { class UsbDeviceHandleImpl; class UsbContext; -typedef libusb_device* PlatformUsbDevice; -typedef libusb_config_descriptor* PlatformUsbConfigDescriptor; +typedef struct libusb_device* PlatformUsbDevice; +typedef struct libusb_config_descriptor* PlatformUsbConfigDescriptor; +typedef struct libusb_device_handle* PlatformUsbDeviceHandle; class UsbDeviceImpl : public UsbDevice { public: @@ -37,12 +39,9 @@ class UsbDeviceImpl : public UsbDevice { void RequestUsbAccess(int interface_id, const ResultCallback& callback) override; #endif // OS_CHROMEOS - scoped_refptr<UsbDeviceHandle> Open() override; + void Open(const OpenCallback& callback) override; bool Close(scoped_refptr<UsbDeviceHandle> handle) override; const UsbConfigDescriptor* GetConfiguration() override; - bool GetManufacturer(base::string16* manufacturer) override; - bool GetProduct(base::string16* product) override; - bool GetSerialNumber(base::string16* serial_number) override; protected: friend class UsbServiceImpl; @@ -50,37 +49,35 @@ class UsbDeviceImpl : public UsbDevice { // Called by UsbServiceImpl only; UsbDeviceImpl(scoped_refptr<UsbContext> context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner, PlatformUsbDevice platform_device, uint16 vendor_id, uint16 product_id, - uint32 unique_id); + uint32 unique_id, + const base::string16& manufacturer_string, + const base::string16& product_string, + const base::string16& serial_number, + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); ~UsbDeviceImpl() override; // Called only by UsbServiceImpl. + PlatformUsbDevice platform_device() const { return platform_device_; } + void set_visited(bool visited) { visited_ = visited; } + bool was_visited() const { return visited_; } void OnDisconnect(); // Called by UsbDeviceHandleImpl. void RefreshConfiguration(); private: + void OpenOnBlockingThread(const OpenCallback& callback); + void Opened(PlatformUsbDeviceHandle platform_handle, + const OpenCallback& callback); + base::ThreadChecker thread_checker_; PlatformUsbDevice platform_device_; + bool visited_ = false; - // 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. - base::string16 manufacturer_; - base::string16 product_; - base::string16 serial_number_; -#if !defined(USE_UDEV) - // On other platforms the device must be opened in order to cache them. This - // should be delayed until the strings are needed to avoid poor interactions - // with other applications. - void CacheStrings(); - bool strings_cached_; -#endif #if defined(OS_CHROMEOS) // On Chrome OS save the devnode string for requesting path access from // permission broker. @@ -98,8 +95,8 @@ class UsbDeviceImpl : public UsbDevice { typedef std::vector<scoped_refptr<UsbDeviceHandleImpl> > HandlesVector; HandlesVector handles_; - // Reference to the UI thread for permission-broker calls. - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; + scoped_refptr<base::SequencedTaskRunner> task_runner_; + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; DISALLOW_COPY_AND_ASSIGN(UsbDeviceImpl); }; diff --git a/device/usb/usb_service.cc b/device/usb/usb_service.cc index 10338fc..18732b5 100644 --- a/device/usb/usb_service.cc +++ b/device/usb/usb_service.cc @@ -17,28 +17,6 @@ UsbService* g_service; } // namespace -// This class manages the lifetime of the global UsbService instance so that -// it is destroyed when the current message loop is destroyed. A lazy instance -// cannot be used because this object does not live on the main thread. -class UsbService::Destroyer : private base::MessageLoop::DestructionObserver { - public: - explicit Destroyer(UsbService* usb_service) : usb_service_(usb_service) { - base::MessageLoop::current()->AddDestructionObserver(this); - } - ~Destroyer() override {} - - private: - // base::MessageLoop::DestructionObserver implementation. - void WillDestroyCurrentMessageLoop() override { - base::MessageLoop::current()->RemoveDestructionObserver(this); - delete usb_service_; - delete this; - g_service = nullptr; - } - - UsbService* usb_service_; -}; - void UsbService::Observer::OnDeviceAdded(scoped_refptr<UsbDevice> device) { } @@ -51,25 +29,23 @@ void UsbService::Observer::OnDeviceRemovedCleanup( // static UsbService* UsbService::GetInstance( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { if (!g_service) { - g_service = UsbServiceImpl::Create(ui_task_runner); - // This object will clean itself up when the message loop is destroyed. - new Destroyer(g_service); + // UsbService constructor saves the pointer this returns and UsbServiceImpl + // will destroy itself when the current message loop exits. + UsbServiceImpl::Create(blocking_task_runner); } return g_service; } -// static -void UsbService::SetInstanceForTest(UsbService* instance) { - g_service = instance; - new Destroyer(instance); -} - UsbService::UsbService() { + DCHECK(!g_service); + g_service = this; } UsbService::~UsbService() { + DCHECK(g_service); + g_service = nullptr; } void UsbService::AddObserver(Observer* observer) { @@ -85,18 +61,12 @@ void UsbService::RemoveObserver(Observer* observer) { void UsbService::NotifyDeviceAdded(scoped_refptr<UsbDevice> device) { DCHECK(CalledOnValidThread()); - USB_LOG(USER) << "USB device added: vendorId = " << device->vendor_id() - << ", productId = " << device->product_id() - << ", uniqueId = " << device->unique_id(); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceAdded(device)); } void UsbService::NotifyDeviceRemoved(scoped_refptr<UsbDevice> device) { DCHECK(CalledOnValidThread()); - USB_LOG(USER) << "USB device removed: uniqueId = " << device->unique_id(); - FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemoved(device)); FOR_EACH_OBSERVER(Observer, observer_list_, OnDeviceRemovedCleanup(device)); } diff --git a/device/usb/usb_service.h b/device/usb/usb_service.h index a2dfa9e..5dfe942 100644 --- a/device/usb/usb_service.h +++ b/device/usb/usb_service.h @@ -7,13 +7,14 @@ #include <vector> +#include "base/bind_helpers.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" #include "base/threading/non_thread_safe.h" namespace base { -class SingleThreadTaskRunner; +class SequencedTaskRunner; } namespace device { @@ -24,11 +25,11 @@ class UsbDevice; // used to manage and dispatch USB events. It is also responsible for device // discovery on the system, which allows it to re-use device handles to prevent // competition for the same USB device. -// -// All functions on this object must be called from a thread with a -// MessageLoopForIO (for example, BrowserThread::FILE). class UsbService : public base::NonThreadSafe { public: + using GetDevicesCallback = + base::Callback<void(const std::vector<scoped_refptr<UsbDevice>>&)>; + class Observer { public: // These events are delivered from the thread on which the UsbService object @@ -40,19 +41,15 @@ class UsbService : public base::NonThreadSafe { virtual void OnDeviceRemovedCleanup(scoped_refptr<UsbDevice> device); }; - // The UI task runner reference is used to talk to the PermissionBrokerClient - // on ChromeOS (UI thread). Returns NULL when initialization fails. + // The file task runner reference is used for blocking I/O operations. + // Returns NULL when initialization fails. static UsbService* GetInstance( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); - - static void SetInstanceForTest(UsbService* instance); + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); virtual scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) = 0; - // Get all of the devices attached to the system, inserting them into - // |devices|. Clears |devices| before use. The result will be sorted by id - // in increasing order. - virtual void GetDevices(std::vector<scoped_refptr<UsbDevice> >* devices) = 0; + // Enumerates available devices. + virtual void GetDevices(const GetDevicesCallback& callback) = 0; void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); @@ -67,7 +64,7 @@ class UsbService : public base::NonThreadSafe { ObserverList<Observer, true> observer_list_; private: - class Destroyer; + friend void base::DeletePointer<UsbService>(UsbService* service); DISALLOW_COPY_AND_ASSIGN(UsbService); }; diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc index 6bdac2b..ce5b51a 100644 --- a/device/usb/usb_service_impl.cc +++ b/device/usb/usb_service_impl.cc @@ -4,6 +4,7 @@ #include "device/usb/usb_service_impl.h" +#include <algorithm> #include <set> #include "base/bind.h" @@ -11,25 +12,160 @@ #include "base/memory/weak_ptr.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 "components/device_event_log/device_event_log.h" #include "device/usb/usb_error.h" +#include "third_party/libusb/src/libusb/libusb.h" #if defined(OS_WIN) #include <setupapi.h> #include <usbiodef.h> -#include "base/scoped_observer.h" #include "base/strings/string_util.h" -#include "device/core/device_monitor_win.h" #endif // OS_WIN -namespace device { +#if defined(USE_UDEV) +#include "device/udev_linux/scoped_udev.h" +#endif // USE_UDEV -#if defined(OS_WIN) +namespace device { namespace { +#if defined(USE_UDEV) + +void ReadDeviceStrings(PlatformUsbDevice platform_device, + libusb_device_descriptor* descriptor, + base::string16* manufacturer_string, + base::string16* product_string, + base::string16* serial_number, + std::string* device_node) { + 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_devnode(device.get()); + if (value) { + *device_node = value; + } + value = udev_device_get_sysattr_value(device.get(), "manufacturer"); + if (value) { + *manufacturer_string = base::UTF8ToUTF16(value); + } + value = udev_device_get_sysattr_value(device.get(), "product"); + if (value) { + *product_string = base::UTF8ToUTF16(value); + } + value = udev_device_get_sysattr_value(device.get(), "serial"); + if (value) { + *serial_number = base::UTF8ToUTF16(value); + } + break; + } + } +} + +#else + +uint16 ReadDeviceLanguage(PlatformUsbDeviceHandle handle) { + uint16 language_id = 0x0409; + uint8 buffer[256]; + int size = + libusb_get_string_descriptor(handle, 0, 0, &buffer[0], sizeof(buffer)); + if (size < 0) { + USB_LOG(EVENT) << "Failed to get supported string languages: " + << ConvertPlatformUsbErrorToString(size); + } else if (size >= 4) { + // Just pick the first supported language. + language_id = buffer[2] | (buffer[3] << 8); + } else { + USB_LOG(EVENT) << "List of available string languages invalid."; + } + + return language_id; +} + +void ReadDeviceString(PlatformUsbDeviceHandle handle, + uint8 string_id, + uint16 language_id, + base::string16* string) { + if (string_id == 0) { + return; + } + + uint8 buffer[256]; + int size = libusb_get_string_descriptor(handle, string_id, language_id, + &buffer[0], sizeof(buffer)); + if (size < 0) { + USB_LOG(EVENT) << "Failed to read string " << (int)string_id + << " from the device: " + << ConvertPlatformUsbErrorToString(size); + } else if (size > 2) { + *string = base::string16(reinterpret_cast<base::char16*>(&buffer[2]), + size / 2 - 1); + } else { + USB_LOG(EVENT) << "String descriptor " << string_id << " is invalid."; + } +} + +void ReadDeviceStrings(PlatformUsbDevice platform_device, + libusb_device_descriptor* descriptor, + base::string16* manufacturer_string, + base::string16* product_string, + base::string16* serial_number, + std::string* device_node) { + if (descriptor->iManufacturer == 0 && descriptor->iProduct == 0 && + descriptor->iSerialNumber == 0) { + // Don't bother distrubing the device if it doesn't have any string + // descriptors we care about. + return; + } + + PlatformUsbDeviceHandle handle; + int rv = libusb_open(platform_device, &handle); + if (rv != LIBUSB_SUCCESS) { + USB_LOG(EVENT) << "Failed to open device to read string descriptors: " + << ConvertPlatformUsbErrorToString(rv); + return; + } + + uint16 language_id = ReadDeviceLanguage(handle); + ReadDeviceString(handle, descriptor->iManufacturer, language_id, + manufacturer_string); + ReadDeviceString(handle, descriptor->iProduct, language_id, product_string); + ReadDeviceString(handle, descriptor->iSerialNumber, language_id, + serial_number); + libusb_close(handle); +} + +#endif // USE_UDEV + +#if defined(OS_WIN) + // Wrapper around a HDEVINFO that automatically destroys it. class ScopedDeviceInfoList { public: @@ -81,62 +217,57 @@ class ScopedDeviceInfo { SP_DEVINFO_DATA dev_info_data_; }; -} // namespace - -// This class lives on the application main thread so that it can listen for -// device change notification window messages. It registers for notifications -// that may indicate new devices that the UsbService will enumerate. -class UsbServiceImpl::UIThreadHelper final - : private DeviceMonitorWin::Observer { - public: - UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service) - : task_runner_(base::ThreadTaskRunnerHandle::Get()), - usb_service_(usb_service), - device_observer_(this) {} - - ~UIThreadHelper() {} +bool IsWinUsbInterface(const std::string& device_path) { + ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); + if (!dev_info_list.valid()) { + USB_PLOG(ERROR) << "Failed to create a device information set"; + return false; + } - void Start() { - DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); - if (device_monitor) { - device_observer_.Add(device_monitor); - } + // This will add the device to |dev_info_list| so we can query driver info. + if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, + NULL)) { + USB_PLOG(ERROR) << "Failed to get device interface data for " + << device_path; + return false; } - private: - void OnDeviceAdded(const GUID& class_guid, - const std::string& device_path) override { - // Only the root node of a composite USB device has the class GUID - // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. - // This first pass filter will catch anything that's sitting on the USB bus - // (including devices on 3rd party USB controllers) to avoid the more - // expensive driver check that needs to be done on the FILE thread. - if (device_path.find("usb") != std::string::npos) { - task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesIfWinUsbDevice, - usb_service_, device_path)); - } + ScopedDeviceInfo dev_info; + if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { + USB_PLOG(ERROR) << "Failed to get device info for " << device_path; + return false; } + dev_info.set_valid(dev_info_list.get()); - void OnDeviceRemoved(const GUID& class_guid, - const std::string& device_path) override { - // The root USB device node is removed last - if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { - task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevices, usb_service_)); - } + DWORD reg_data_type; + BYTE buffer[256]; + if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(), + SPDRP_SERVICE, ®_data_type, + &buffer[0], sizeof buffer, NULL)) { + USB_PLOG(ERROR) << "Failed to get device service property"; + return false; + } + if (reg_data_type != REG_SZ) { + USB_LOG(ERROR) << "Unexpected data type for driver service: " + << reg_data_type; + return false; } - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - base::WeakPtr<UsbServiceImpl> usb_service_; - ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; -}; + USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << "."; + if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") == + 0) { + return true; + } + return false; +} #endif // OS_WIN +} // namespace + // static UsbService* UsbServiceImpl::Create( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { PlatformUsbContext context = NULL; const int rv = libusb_init(&context); if (rv != LIBUSB_SUCCESS) { @@ -148,42 +279,21 @@ UsbService* UsbServiceImpl::Create( return nullptr; } - return new UsbServiceImpl(context, ui_task_runner); -} - -scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { - DCHECK(CalledOnValidThread()); - RefreshDevices(); - DeviceMap::iterator it = devices_.find(unique_id); - if (it != devices_.end()) { - return it->second; - } - return NULL; -} - -void UsbServiceImpl::GetDevices( - std::vector<scoped_refptr<UsbDevice> >* devices) { - DCHECK(CalledOnValidThread()); - STLClearObject(devices); - - if (!hotplug_enabled_) { - RefreshDevices(); - } - - for (const auto& map_entry : devices_) { - devices->push_back(map_entry.second); - } + return new UsbServiceImpl(context, blocking_task_runner); } UsbServiceImpl::UsbServiceImpl( PlatformUsbContext context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) : context_(new UsbContext(context)), - ui_task_runner_(ui_task_runner), - next_unique_id_(0), - hotplug_enabled_(false), + task_runner_(base::ThreadTaskRunnerHandle::Get()), + blocking_task_runner_(blocking_task_runner), +#if defined(OS_WIN) + device_observer_(this), +#endif weak_factory_(this) { - task_runner_ = base::ThreadTaskRunnerHandle::Get(); + base::MessageLoop::current()->AddDestructionObserver(this); + int rv = libusb_hotplug_register_callback( context_->context(), static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | @@ -193,149 +303,269 @@ UsbServiceImpl::UsbServiceImpl( &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); if (rv == LIBUSB_SUCCESS) { hotplug_enabled_ = true; + + // libusb will call the hotplug callback for each device currently + // enumerated. Once this is complete enumeration_ready_ can be set to true + // but we must first wait for any tasks posted to blocking_task_runner_ to + // complete. + blocking_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), + base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + weak_factory_.GetWeakPtr(), nullptr, 0)); } else { + RefreshDevices(""); #if defined(OS_WIN) - ui_thread_helper_ = new UIThreadHelper(weak_factory_.GetWeakPtr()); - ui_task_runner_->PostTask(FROM_HERE, - base::Bind(&UIThreadHelper::Start, - base::Unretained(ui_thread_helper_))); + DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); + if (device_monitor) { + device_observer_.Add(device_monitor); + } #endif // OS_WIN } } UsbServiceImpl::~UsbServiceImpl() { + base::MessageLoop::current()->RemoveDestructionObserver(this); + if (hotplug_enabled_) { libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); } -#if defined(OS_WIN) - if (ui_thread_helper_) { - ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_); - } -#endif // OS_WIN for (const auto& map_entry : devices_) { map_entry.second->OnDisconnect(); } } -void UsbServiceImpl::RefreshDevices() { +scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { DCHECK(CalledOnValidThread()); - - libusb_device** platform_devices = NULL; - const ssize_t device_count = - libusb_get_device_list(context_->context(), &platform_devices); - if (device_count < 0) { - USB_LOG(ERROR) << "Failed to get device list: " - << ConvertPlatformUsbErrorToString(device_count); + DeviceMap::iterator it = devices_.find(unique_id); + if (it != devices_.end()) { + return it->second; } + return NULL; +} - std::set<UsbDevice*> connected_devices; - std::vector<PlatformUsbDevice> disconnected_devices; +void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) { + DCHECK(CalledOnValidThread()); - // Populates new devices. - for (ssize_t i = 0; i < device_count; ++i) { - if (!ContainsKey(platform_devices_, platform_devices[i])) { - scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]); - if (new_device) { - connected_devices.insert(new_device.get()); - } - } else { - connected_devices.insert(platform_devices_[platform_devices[i]].get()); + if (!enumeration_ready_) { + // On startup wait for the first enumeration, + pending_enumerations_.push_back(callback); + } else if (hotplug_enabled_) { + // The device list is updated live when hotplug events are supported. + std::vector<scoped_refptr<UsbDevice>> devices; + for (const auto& map_entry : devices_) { + devices.push_back(map_entry.second); + } + callback.Run(devices); + } else { + // Only post one re-enumeration task at a time. + if (pending_enumerations_.empty()) { + RefreshDevices(""); } + pending_enumerations_.push_back(callback); } +} - // Find disconnected devices. - for (const auto& map_entry : platform_devices_) { - PlatformUsbDevice platform_device = map_entry.first; - scoped_refptr<UsbDeviceImpl> device = map_entry.second; - if (!ContainsKey(connected_devices, device.get())) { - disconnected_devices.push_back(platform_device); - devices_.erase(device->unique_id()); - - NotifyDeviceRemoved(device); - device->OnDisconnect(); - } +#if defined(OS_WIN) + +void UsbServiceImpl::OnDeviceAdded(const GUID& class_guid, + const std::string& device_path) { + // Only the root node of a composite USB device has the class GUID + // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. + // This first pass filter will catch anything that's sitting on the USB bus + // (including devices on 3rd party USB controllers) to avoid the more + // expensive driver check that needs to be done on the FILE thread. + if (device_path.find("usb") != std::string::npos) { + RefreshDevices(device_path); } +} - // Remove disconnected devices from platform_devices_. - for (const PlatformUsbDevice& platform_device : disconnected_devices) { - // UsbDevice will be destroyed after this. The corresponding - // PlatformUsbDevice will be unref'ed during this process. - platform_devices_.erase(platform_device); +void UsbServiceImpl::OnDeviceRemoved(const GUID& class_guid, + const std::string& device_path) { + // The root USB device node is removed last + if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { + RefreshDevices(""); } +} - libusb_free_device_list(platform_devices, true); +#endif // OS_WIN + +void UsbServiceImpl::WillDestroyCurrentMessageLoop() { + DCHECK(CalledOnValidThread()); + delete this; } -#if defined(OS_WIN) -void UsbServiceImpl::RefreshDevicesIfWinUsbDevice( - const std::string& device_path) { - ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); - if (!dev_info_list.valid()) { - USB_PLOG(ERROR) << "Failed to create a device information set"; - return; - } +void UsbServiceImpl::RefreshDevices(const std::string& new_device_path) { + DCHECK(CalledOnValidThread()); - // This will add the device to |dev_info_list| so we can query driver info. - if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, - NULL)) { - USB_PLOG(ERROR) << "Failed to get device interface data for " - << device_path; - return; + std::set<PlatformUsbDevice> current_devices; + for (const auto& map_entry : platform_devices_) { + current_devices.insert(map_entry.first); } + blocking_task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesOnBlockingThread, + weak_factory_.GetWeakPtr(), new_device_path, + task_runner_, context_, current_devices)); +} - ScopedDeviceInfo dev_info; - if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { - USB_PLOG(ERROR) << "Failed to get device info for " << device_path; - return; +// static +void UsbServiceImpl::RefreshDevicesOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + const std::string& new_device_path, + scoped_refptr<base::SequencedTaskRunner> task_runner, + scoped_refptr<UsbContext> usb_context, + const std::set<PlatformUsbDevice>& previous_devices) { + if (!new_device_path.empty()) { +#if defined(OS_WIN) + if (!IsWinUsbInterface(new_device_path)) { + // Wait to call libusb_get_device_list until libusb will be able to find + // a WinUSB interface for the device. + return; + } +#endif // defined(OS_WIN) } - dev_info.set_valid(dev_info_list.get()); - DWORD reg_data_type; - BYTE buffer[256]; - if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(), - SPDRP_SERVICE, ®_data_type, - &buffer[0], sizeof buffer, NULL)) { - USB_PLOG(ERROR) << "Failed to get device service property"; - return; - } - if (reg_data_type != REG_SZ) { - USB_LOG(ERROR) << "Unexpected data type for driver service: " - << reg_data_type; + libusb_device** platform_devices = NULL; + const ssize_t device_count = + libusb_get_device_list(usb_context->context(), &platform_devices); + if (device_count < 0) { + USB_LOG(ERROR) << "Failed to get device list: " + << ConvertPlatformUsbErrorToString(device_count); + task_runner->PostTask(FROM_HERE, + base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + usb_service, nullptr, 0)); return; } - USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << "."; - if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") == - 0) { - RefreshDevices(); + // Find new devices. + for (ssize_t i = 0; i < device_count; ++i) { + PlatformUsbDevice platform_device = platform_devices[i]; + if (previous_devices.find(platform_device) == previous_devices.end()) { + libusb_ref_device(platform_device); + AddDeviceOnBlockingThread(usb_service, task_runner, platform_device); + } } + + // |platform_devices| will be freed in this callback. + task_runner->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + usb_service, platform_devices, device_count)); } -#endif // OS_WIN -scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice( +// static +void UsbServiceImpl::AddDeviceOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + scoped_refptr<base::SequencedTaskRunner> task_runner, PlatformUsbDevice platform_device) { libusb_device_descriptor descriptor; int rv = libusb_get_device_descriptor(platform_device, &descriptor); if (rv == LIBUSB_SUCCESS) { - uint32 unique_id; - do { - unique_id = ++next_unique_id_; - } while (devices_.find(unique_id) != devices_.end()); - - scoped_refptr<UsbDeviceImpl> new_device(new UsbDeviceImpl( - context_, ui_task_runner_, platform_device, descriptor.idVendor, - descriptor.idProduct, unique_id)); - platform_devices_[platform_device] = new_device; - devices_[unique_id] = new_device; - NotifyDeviceAdded(new_device); - return new_device; + base::string16 manufacturer_string; + base::string16 product_string; + base::string16 serial_number; + std::string device_node; + ReadDeviceStrings(platform_device, &descriptor, &manufacturer_string, + &product_string, &serial_number, &device_node); + + task_runner->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::AddDevice, usb_service, + platform_device, descriptor.idVendor, + descriptor.idProduct, manufacturer_string, + product_string, serial_number, device_node)); } else { USB_LOG(EVENT) << "Failed to get device descriptor: " << ConvertPlatformUsbErrorToString(rv); - return nullptr; + libusb_unref_device(platform_device); } } +void UsbServiceImpl::RefreshDevicesComplete(libusb_device** platform_devices, + ssize_t device_count) { + if (platform_devices) { + // Mark devices seen in this enumeration. + for (ssize_t i = 0; i < device_count; ++i) { + const PlatformDeviceMap::iterator it = + platform_devices_.find(platform_devices[i]); + if (it != platform_devices_.end()) { + it->second->set_visited(true); + } + } + + // Remove devices not seen in this enumeration. + for (PlatformDeviceMap::iterator it = platform_devices_.begin(); + it != platform_devices_.end(); + /* incremented internally */) { + PlatformDeviceMap::iterator current = it++; + const scoped_refptr<UsbDeviceImpl>& device = current->second; + if (device->was_visited()) { + device->set_visited(false); + } else { + RemoveDevice(device); + } + } + + libusb_free_device_list(platform_devices, true); + } + + enumeration_ready_ = true; + + if (!pending_enumerations_.empty()) { + std::vector<scoped_refptr<UsbDevice>> devices; + for (const auto& map_entry : devices_) { + devices.push_back(map_entry.second); + } + + std::vector<GetDevicesCallback> pending_enumerations; + pending_enumerations.swap(pending_enumerations_); + for (const GetDevicesCallback& callback : pending_enumerations) { + callback.Run(devices); + } + } +} + +void UsbServiceImpl::AddDevice(PlatformUsbDevice platform_device, + uint16 vendor_id, + uint16 product_id, + base::string16 manufacturer_string, + base::string16 product_string, + base::string16 serial_number, + std::string device_node) { + uint32 unique_id; + do { + unique_id = ++next_unique_id_; + } while (devices_.find(unique_id) != devices_.end()); + + scoped_refptr<UsbDeviceImpl> device( + new UsbDeviceImpl(context_, platform_device, vendor_id, product_id, + unique_id, manufacturer_string, product_string, + serial_number, blocking_task_runner_)); + + platform_devices_[platform_device] = device; + devices_[unique_id] = device; + + USB_LOG(USER) << "USB device added: vendor=" << device->vendor_id() << " \"" + << device->manufacturer_string() + << "\", product=" << device->product_id() << " \"" + << device->product_string() << "\", serial=\"" + << device->serial_number() + << "\", uniqueId=" << device->unique_id(); + + if (enumeration_ready_) { + NotifyDeviceAdded(device); + } + + libusb_unref_device(platform_device); +} + +void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) { + platform_devices_.erase(device->platform_device()); + devices_.erase(device->unique_id()); + + USB_LOG(USER) << "USB device removed: uniqueId=" << device->unique_id(); + + NotifyDeviceRemoved(device); + device->OnDisconnect(); +} + // static int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, PlatformUsbDevice device, @@ -348,22 +578,22 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); switch (event) { case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: - libusb_ref_device(device); // Released in OnDeviceAdded. + libusb_ref_device(device); // Released in OnPlatformDeviceAdded. if (self->task_runner_->BelongsToCurrentThread()) { - self->OnDeviceAdded(device); + self->OnPlatformDeviceAdded(device); } else { self->task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, + FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceAdded, base::Unretained(self), device)); } break; case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: - libusb_ref_device(device); // Released in OnDeviceRemoved. + libusb_ref_device(device); // Released in OnPlatformDeviceRemoved. if (self->task_runner_->BelongsToCurrentThread()) { - self->OnDeviceRemoved(device); + self->OnPlatformDeviceRemoved(device); } else { self->task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, + FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceRemoved, base::Unretained(self), device)); } break; @@ -374,30 +604,28 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, return 0; } -void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { +void UsbServiceImpl::OnPlatformDeviceAdded(PlatformUsbDevice platform_device) { DCHECK(CalledOnValidThread()); DCHECK(!ContainsKey(platform_devices_, platform_device)); + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbServiceImpl::AddDeviceOnBlockingThread, + weak_factory_.GetWeakPtr(), task_runner_, platform_device)); - AddDevice(platform_device); - libusb_unref_device(platform_device); + // libusb_unref_device(platform_device) is called by the task above. } -void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { +void UsbServiceImpl::OnPlatformDeviceRemoved( + PlatformUsbDevice platform_device) { DCHECK(CalledOnValidThread()); - PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); if (it != platform_devices_.end()) { scoped_refptr<UsbDeviceImpl> device = it->second; - DeviceMap::iterator dev_it = devices_.find(device->unique_id()); - if (dev_it != devices_.end()) { - devices_.erase(dev_it); - } else { - NOTREACHED(); - } - platform_devices_.erase(it); - - NotifyDeviceRemoved(device); - device->OnDisconnect(); + // Serialize with calls to AddDeviceOnBlockingThread. + blocking_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), + base::Bind(&UsbServiceImpl::RemoveDevice, weak_factory_.GetWeakPtr(), + device)); } else { NOTREACHED(); } diff --git a/device/usb/usb_service_impl.h b/device/usb/usb_service_impl.h index 44fff41..e4c4443 100644 --- a/device/usb/usb_service_impl.h +++ b/device/usb/usb_service_impl.h @@ -5,42 +5,92 @@ #include "device/usb/usb_service.h" #include <map> +#include <set> #include "base/memory/weak_ptr.h" -#include "base/single_thread_task_runner.h" +#include "base/message_loop/message_loop.h" #include "device/usb/usb_context.h" #include "device/usb/usb_device_impl.h" #include "third_party/libusb/src/libusb/libusb.h" +#if defined(OS_WIN) +#include "base/scoped_observer.h" +#include "device/core/device_monitor_win.h" +#endif // OS_WIN + +struct libusb_device; +struct libusb_context; + +namespace base { +class SequencedTaskRunner; +class SingleThreadTaskRunner; +} + namespace device { typedef struct libusb_device* PlatformUsbDevice; typedef struct libusb_context* PlatformUsbContext; -class UsbServiceImpl : public UsbService { +class UsbServiceImpl : public UsbService, +#if defined(OS_WIN) + public DeviceMonitorWin::Observer, +#endif // OS_WIN + public base::MessageLoop::DestructionObserver { public: static UsbService* Create( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); private: explicit UsbServiceImpl( PlatformUsbContext context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner); + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner); ~UsbServiceImpl() override; // device::UsbService implementation scoped_refptr<UsbDevice> GetDeviceById(uint32 unique_id) override; - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override; - - // Enumerate USB devices from OS and update devices_ map. - void RefreshDevices(); + void GetDevices(const GetDevicesCallback& callback) override; #if defined(OS_WIN) - void RefreshDevicesIfWinUsbDevice(const std::string& device_path); + // device::DeviceMonitorWin::Observer implementation + void OnDeviceAdded(const GUID& class_guid, + const std::string& device_path) override; + void OnDeviceRemoved(const GUID& class_guid, + const std::string& device_path) override; #endif // OS_WIN + // base::MessageLoop::DestructionObserver implementation + void WillDestroyCurrentMessageLoop() override; + + // Enumerate USB devices from OS and update devices_ map. |new_device_path| is + // an optional hint used on Windows to prevent enumerations before drivers for + // a new device have been completely loaded. + void RefreshDevices(const std::string& new_device_path); + + static void RefreshDevicesOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + const std::string& new_device_path, + scoped_refptr<base::SequencedTaskRunner> task_runner, + scoped_refptr<UsbContext> usb_context, + const std::set<PlatformUsbDevice>& previous_devices); + + static void AddDeviceOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + scoped_refptr<base::SequencedTaskRunner> task_runner, + PlatformUsbDevice platform_device); + + void RefreshDevicesComplete(libusb_device** platform_devices, + ssize_t device_count); + // Adds a new UsbDevice to the devices_ map based on the given libusb device. - scoped_refptr<UsbDeviceImpl> AddDevice(PlatformUsbDevice platform_device); + void AddDevice(PlatformUsbDevice platform_device, + uint16 vendor_id, + uint16 product_id, + base::string16 manufacturer_string, + base::string16 product_string, + base::string16 serial_number, + std::string device_node); + + void RemoveDevice(scoped_refptr<UsbDeviceImpl> device); // Handle hotplug events from libusb. static int LIBUSB_CALL HotplugCallback(libusb_context* context, @@ -48,27 +98,26 @@ class UsbServiceImpl : public UsbService { libusb_hotplug_event event, void* user_data); // These functions release a reference to the provided platform device. - void OnDeviceAdded(PlatformUsbDevice platform_device); - void OnDeviceRemoved(PlatformUsbDevice platform_device); + void OnPlatformDeviceAdded(PlatformUsbDevice platform_device); + void OnPlatformDeviceRemoved(PlatformUsbDevice platform_device); scoped_refptr<UsbContext> context_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; - -#if defined(OS_WIN) - class UIThreadHelper; - UIThreadHelper* ui_thread_helper_; -#endif // OS_WIN + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; // TODO(reillyg): Figure out a better solution for device IDs. - uint32 next_unique_id_; + uint32 next_unique_id_ = 0; // When available the device list will be updated when new devices are // connected instead of only when a full enumeration is requested. // TODO(reillyg): Support this on all platforms. crbug.com/411715 - bool hotplug_enabled_; + bool hotplug_enabled_ = false; libusb_hotplug_callback_handle hotplug_handle_; + // Enumeration callbacks are queued until an enumeration completes. + bool enumeration_ready_ = false; + std::vector<GetDevicesCallback> pending_enumerations_; + // The map from unique IDs to UsbDevices. typedef std::map<uint32, scoped_refptr<UsbDeviceImpl>> DeviceMap; DeviceMap devices_; @@ -78,6 +127,10 @@ class UsbServiceImpl : public UsbService { PlatformDeviceMap; PlatformDeviceMap platform_devices_; +#if defined(OS_WIN) + ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; +#endif // OS_WIN + base::WeakPtrFactory<UsbServiceImpl> weak_factory_; DISALLOW_COPY_AND_ASSIGN(UsbServiceImpl); diff --git a/device/usb/usb_service_unittest.cc b/device/usb/usb_service_unittest.cc index edd0712..f0e5ae2 100644 --- a/device/usb/usb_service_unittest.cc +++ b/device/usb/usb_service_unittest.cc @@ -4,6 +4,7 @@ #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/test_io_thread.h" #include "device/test/usb_test_gadget.h" #include "device/usb/usb_device.h" #include "device/usb/usb_device_handle.h" @@ -15,34 +16,34 @@ namespace { class UsbServiceTest : public ::testing::Test { public: - void SetUp() override { message_loop_.reset(new base::MessageLoopForIO); } + void SetUp() override { + message_loop_.reset(new base::MessageLoopForUI); + io_thread_.reset(new base::TestIOThread(base::TestIOThread::kAutoStart)); + } - private: + protected: scoped_ptr<base::MessageLoop> message_loop_; + scoped_ptr<base::TestIOThread> io_thread_; }; TEST_F(UsbServiceTest, ClaimGadget) { if (!UsbTestGadget::IsTestEnabled()) return; - scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim(); + scoped_ptr<UsbTestGadget> gadget = + UsbTestGadget::Claim(io_thread_->task_runner()); ASSERT_TRUE(gadget.get()); scoped_refptr<UsbDevice> device = gadget->GetDevice(); - base::string16 utf16; - ASSERT_TRUE(device->GetManufacturer(&utf16)); - ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16)); - - ASSERT_TRUE(device->GetProduct(&utf16)); - ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16)); - - ASSERT_TRUE(device->GetSerialNumber(&utf16)); - ASSERT_EQ(gadget->GetSerialNumber(), base::UTF16ToUTF8(utf16)); + ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(device->manufacturer_string())); + ASSERT_EQ("Test Gadget (default state)", + base::UTF16ToUTF8(device->product_string())); } TEST_F(UsbServiceTest, DisconnectAndReconnect) { if (!UsbTestGadget::IsTestEnabled()) return; - scoped_ptr<UsbTestGadget> gadget = UsbTestGadget::Claim(); + scoped_ptr<UsbTestGadget> gadget = + UsbTestGadget::Claim(io_thread_->task_runner()); ASSERT_TRUE(gadget.get()); ASSERT_TRUE(gadget->Disconnect()); ASSERT_TRUE(gadget->Reconnect()); diff --git a/extensions/browser/api/device_permissions_manager.cc b/extensions/browser/api/device_permissions_manager.cc index 17e3eab..d64c027 100644 --- a/extensions/browser/api/device_permissions_manager.cc +++ b/extensions/browser/api/device_permissions_manager.cc @@ -233,16 +233,13 @@ std::set<scoped_refptr<DevicePermissionEntry>> GetDevicePermissionEntries( } // namespace DevicePermissionEntry::DevicePermissionEntry( - scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number, - const base::string16& manufacturer_string, - const base::string16& product_string) + scoped_refptr<device::UsbDevice> device) : device_(device), vendor_id_(device->vendor_id()), product_id_(device->product_id()), - serial_number_(serial_number), - manufacturer_string_(manufacturer_string), - product_string_(product_string) { + serial_number_(device->serial_number()), + manufacturer_string_(device->manufacturer_string()), + product_string_(device->product_string()) { } DevicePermissionEntry::DevicePermissionEntry( @@ -341,14 +338,13 @@ DevicePermissions::~DevicePermissions() { } scoped_refptr<DevicePermissionEntry> DevicePermissions::FindEntry( - scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number) const { + scoped_refptr<device::UsbDevice> device) const { const auto& ephemeral_device_entry = ephemeral_devices_.find(device); if (ephemeral_device_entry != ephemeral_devices_.end()) { return ephemeral_device_entry->second; } - if (serial_number.empty()) { + if (device->serial_number().empty()) { return nullptr; } @@ -362,7 +358,7 @@ scoped_refptr<DevicePermissionEntry> DevicePermissions::FindEntry( if (entry->product_id() != device->product_id()) { continue; } - if (entry->serial_number() != serial_number) { + if (entry->serial_number() != device->serial_number()) { continue; } return entry; @@ -376,50 +372,22 @@ DevicePermissions::DevicePermissions(BrowserContext* context, entries_ = GetDevicePermissionEntries(prefs, extension_id); } -DevicePermissions::DevicePermissions(const DevicePermissions* original) - : entries_(original->entries_), - ephemeral_devices_(original->ephemeral_devices_) { -} - -class DevicePermissionsManager::FileThreadHelper : public UsbService::Observer { - public: - FileThreadHelper( - base::WeakPtr<DevicePermissionsManager> device_permissions_manager) - : device_permissions_manager_(device_permissions_manager), - observer_(this) {} - virtual ~FileThreadHelper() {} - - void Start() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - UsbService* service = device::DeviceClient::Get()->GetUsbService(); - if (service) { - observer_.Add(service); - } - } - - private: - void OnDeviceRemovedCleanup(scoped_refptr<UsbDevice> device) override { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DevicePermissionsManager::OnDeviceRemoved, - device_permissions_manager_, device)); - } - - base::WeakPtr<DevicePermissionsManager> device_permissions_manager_; - ScopedObserver<UsbService, UsbService::Observer> observer_; -}; - // static DevicePermissionsManager* DevicePermissionsManager::Get( BrowserContext* context) { return DevicePermissionsManagerFactory::GetForBrowserContext(context); } -scoped_ptr<DevicePermissions> DevicePermissionsManager::GetForExtension( +DevicePermissions* DevicePermissionsManager::GetForExtension( const std::string& extension_id) { DCHECK(CalledOnValidThread()); - return make_scoped_ptr(new DevicePermissions(GetOrInsert(extension_id))); + DevicePermissions* device_permissions = GetInternal(extension_id); + if (!device_permissions) { + device_permissions = new DevicePermissions(context_, extension_id); + extension_id_to_device_permissions_[extension_id] = device_permissions; + } + + return device_permissions; } std::vector<base::string16> @@ -427,7 +395,7 @@ DevicePermissionsManager::GetPermissionMessageStrings( const std::string& extension_id) const { DCHECK(CalledOnValidThread()); std::vector<base::string16> messages; - const DevicePermissions* device_permissions = Get(extension_id); + const DevicePermissions* device_permissions = GetInternal(extension_id); if (device_permissions) { for (const scoped_refptr<DevicePermissionEntry>& entry : device_permissions->entries()) { @@ -439,15 +407,12 @@ DevicePermissionsManager::GetPermissionMessageStrings( void DevicePermissionsManager::AllowUsbDevice( const std::string& extension_id, - scoped_refptr<device::UsbDevice> device, - const base::string16& product_string, - const base::string16& manufacturer_string, - const base::string16& serial_number) { + scoped_refptr<device::UsbDevice> device) { DCHECK(CalledOnValidThread()); - DevicePermissions* device_permissions = GetOrInsert(extension_id); + DevicePermissions* device_permissions = GetForExtension(extension_id); - scoped_refptr<DevicePermissionEntry> device_entry(new DevicePermissionEntry( - device, serial_number, manufacturer_string, product_string)); + scoped_refptr<DevicePermissionEntry> device_entry( + new DevicePermissionEntry(device)); if (device_entry->IsPersistent()) { for (const auto& entry : device_permissions->entries()) { @@ -474,13 +439,9 @@ void DevicePermissionsManager::AllowUsbDevice( // Only start observing when an ephemeral device has been added so that // UsbService is not automatically initialized on profile creation (which it // would be if this call were in the constructor). - if (!helper_) { - helper_ = new FileThreadHelper(weak_factory_.GetWeakPtr()); - // base::Unretained is safe because any task to delete helper_ will be - // executed after this call. - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&FileThreadHelper::Start, base::Unretained(helper_))); + UsbService* usb_service = device::DeviceClient::Get()->GetUsbService(); + if (!usb_service_observer_.IsObserving(usb_service)) { + usb_service_observer_.Add(usb_service); } } } @@ -499,7 +460,7 @@ void DevicePermissionsManager::RemoveEntry( const std::string& extension_id, scoped_refptr<DevicePermissionEntry> entry) { DCHECK(CalledOnValidThread()); - DevicePermissions* device_permissions = Get(extension_id); + DevicePermissions* device_permissions = GetInternal(extension_id); DCHECK(device_permissions); DCHECK(ContainsKey(device_permissions->entries_, entry)); device_permissions->entries_.erase(entry); @@ -514,7 +475,7 @@ void DevicePermissionsManager::Clear(const std::string& extension_id) { DCHECK(CalledOnValidThread()); ClearDevicePermissionEntries(ExtensionPrefs::Get(context_), extension_id); - DevicePermissions* device_permissions = Get(extension_id); + DevicePermissions* device_permissions = GetInternal(extension_id); if (device_permissions) { extension_id_to_device_permissions_.erase(extension_id); delete device_permissions; @@ -525,8 +486,7 @@ DevicePermissionsManager::DevicePermissionsManager( content::BrowserContext* context) : context_(context), process_manager_observer_(this), - helper_(nullptr), - weak_factory_(this) { + usb_service_observer_(this) { process_manager_observer_.Add(ProcessManager::Get(context)); } @@ -535,13 +495,9 @@ DevicePermissionsManager::~DevicePermissionsManager() { DevicePermissions* device_permissions = map_entry.second; delete device_permissions; } - if (helper_) { - BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, helper_); - helper_ = nullptr; - } } -DevicePermissions* DevicePermissionsManager::Get( +DevicePermissions* DevicePermissionsManager::GetInternal( const std::string& extension_id) const { std::map<std::string, DevicePermissions*>::const_iterator it = extension_id_to_device_permissions_.find(extension_id); @@ -552,22 +508,11 @@ DevicePermissions* DevicePermissionsManager::Get( return NULL; } -DevicePermissions* DevicePermissionsManager::GetOrInsert( - const std::string& extension_id) { - DevicePermissions* device_permissions = Get(extension_id); - if (!device_permissions) { - device_permissions = new DevicePermissions(context_, extension_id); - extension_id_to_device_permissions_[extension_id] = device_permissions; - } - - return device_permissions; -} - void DevicePermissionsManager::OnBackgroundHostClose( const std::string& extension_id) { DCHECK(CalledOnValidThread()); - DevicePermissions* device_permissions = Get(extension_id); + DevicePermissions* device_permissions = GetInternal(extension_id); if (device_permissions) { // When all of the app's windows are closed and the background page is // suspended all ephemeral device permissions are cleared. @@ -578,7 +523,7 @@ void DevicePermissionsManager::OnBackgroundHostClose( } } -void DevicePermissionsManager::OnDeviceRemoved( +void DevicePermissionsManager::OnDeviceRemovedCleanup( scoped_refptr<UsbDevice> device) { DCHECK(CalledOnValidThread()); for (const auto& map_entry : extension_id_to_device_permissions_) { diff --git a/extensions/browser/api/device_permissions_manager.h b/extensions/browser/api/device_permissions_manager.h index 159cd7b..7d3a500 100644 --- a/extensions/browser/api/device_permissions_manager.h +++ b/extensions/browser/api/device_permissions_manager.h @@ -40,13 +40,7 @@ namespace extensions { class DevicePermissionEntry : public base::RefCountedThreadSafe<DevicePermissionEntry> { public: - // TODO(reillyg): This function should be able to take only the - // device::UsbDevice and read the strings from there. This is not yet possible - // as the device can not be accessed from the UI thread. crbug.com/427985 - DevicePermissionEntry(scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number, - const base::string16& manufacturer_string, - const base::string16& product_string); + DevicePermissionEntry(scoped_refptr<device::UsbDevice> device); DevicePermissionEntry(uint16_t vendor_id, uint16_t product_id, const base::string16& serial_number, @@ -98,7 +92,7 @@ class DevicePermissionEntry base::Time last_used_; }; -// Stores a copy of device permissions associated with a particular extension. +// Stores device permissions associated with a particular extension. class DevicePermissions { public: virtual ~DevicePermissions(); @@ -108,8 +102,7 @@ class DevicePermissions { // to call device->GetSerialNumber() which may not be possible on the // current thread. scoped_refptr<DevicePermissionEntry> FindEntry( - scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number) const; + scoped_refptr<device::UsbDevice> device) const; const std::set<scoped_refptr<DevicePermissionEntry>>& entries() const { return entries_; @@ -121,9 +114,6 @@ class DevicePermissions { // Reads permissions out of ExtensionPrefs. DevicePermissions(content::BrowserContext* context, const std::string& extension_id); - // Does a shallow copy, duplicating the device lists so that the resulting - // object can be used from a different thread. - DevicePermissions(const DevicePermissions* original); std::set<scoped_refptr<DevicePermissionEntry>> entries_; std::map<scoped_refptr<device::UsbDevice>, @@ -135,28 +125,21 @@ class DevicePermissions { // Manages saved device permissions for all extensions. class DevicePermissionsManager : public KeyedService, public base::NonThreadSafe, - public ProcessManagerObserver { + public ProcessManagerObserver, + public device::UsbService::Observer { public: static DevicePermissionsManager* Get(content::BrowserContext* context); - // Returns a copy of the DevicePermissions object for a given extension that - // can be used by any thread. - scoped_ptr<DevicePermissions> GetForExtension( - const std::string& extension_id); + // The DevicePermissions object for a given extension. + DevicePermissions* GetForExtension(const std::string& extension_id); // Equivalent to calling GetForExtension and extracting the permission string // for each entry. std::vector<base::string16> GetPermissionMessageStrings( const std::string& extension_id) const; - // TODO(reillyg): AllowUsbDevice should only take the extension ID and - // device, with the strings read from the device. This isn't possible now as - // the device can not be accessed from the UI thread yet. crbug.com/427985 void AllowUsbDevice(const std::string& extension_id, - scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number, - const base::string16& manufacturer_string, - const base::string16& product_string); + scoped_refptr<device::UsbDevice> device); // Updates the "last used" timestamp on the given device entry and writes it // out to ExtensionPrefs. @@ -171,7 +154,6 @@ class DevicePermissionsManager : public KeyedService, void Clear(const std::string& extension_id); private: - class FileThreadHelper; friend class DevicePermissionsManagerFactory; FRIEND_TEST_ALL_PREFIXES(DevicePermissionsManagerTest, SuspendExtension); @@ -179,20 +161,20 @@ class DevicePermissionsManager : public KeyedService, DevicePermissionsManager(content::BrowserContext* context); ~DevicePermissionsManager() override; - DevicePermissions* Get(const std::string& extension_id) const; - DevicePermissions* GetOrInsert(const std::string& extension_id); - void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device); + DevicePermissions* GetInternal(const std::string& extension_id) const; // ProcessManagerObserver implementation void OnBackgroundHostClose(const std::string& extension_id) override; + // UsbService::Observer implementation + void OnDeviceRemovedCleanup(scoped_refptr<device::UsbDevice> device) override; + content::BrowserContext* context_; std::map<std::string, DevicePermissions*> extension_id_to_device_permissions_; ScopedObserver<ProcessManager, ProcessManagerObserver> process_manager_observer_; - FileThreadHelper* helper_; - - base::WeakPtrFactory<DevicePermissionsManager> weak_factory_; + ScopedObserver<device::UsbService, device::UsbService::Observer> + usb_service_observer_; DISALLOW_COPY_AND_ASSIGN(DevicePermissionsManager); }; diff --git a/extensions/browser/api/device_permissions_prompt.cc b/extensions/browser/api/device_permissions_prompt.cc index f45ae56..fe6d535 100644 --- a/extensions/browser/api/device_permissions_prompt.cc +++ b/extensions/browser/api/device_permissions_prompt.cc @@ -28,10 +28,8 @@ namespace extensions { DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo( scoped_refptr<UsbDevice> device) : device(device) { - base::string16 manufacturer_string; - if (device->GetManufacturer(&manufacturer_string)) { - original_manufacturer_string = manufacturer_string; - } else { + base::string16 manufacturer_string = device->manufacturer_string(); + if (manufacturer_string.empty()) { const char* vendor_name = device::UsbIds::GetVendorName(device->vendor_id()); if (vendor_name) { @@ -44,10 +42,8 @@ DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo( } } - base::string16 product_string; - if (device->GetProduct(&product_string)) { - original_product_string = product_string; - } else { + base::string16 product_string = device->product_string(); + if (product_string.empty()) { const char* product_name = device::UsbIds::GetProductName( device->vendor_id(), device->product_id()); if (product_name) { @@ -60,10 +56,6 @@ DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo( } } - if (!device->GetSerialNumber(&serial_number)) { - serial_number.clear(); - } - name = l10n_util::GetStringFUTF16(IDS_DEVICE_PERMISSIONS_DEVICE_NAME, product_string, manufacturer_string); } @@ -71,21 +63,19 @@ DevicePermissionsPrompt::Prompt::DeviceInfo::DeviceInfo( DevicePermissionsPrompt::Prompt::DeviceInfo::~DeviceInfo() { } -DevicePermissionsPrompt::Prompt::Prompt() - : extension_(nullptr), - browser_context_(nullptr), - multiple_(false), - observer_(nullptr), - usb_service_observer_(this) { +DevicePermissionsPrompt::Prompt::Prompt() : usb_service_observer_(this) { } void DevicePermissionsPrompt::Prompt::SetObserver(Observer* observer) { observer_ = observer; if (observer_) { - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&DevicePermissionsPrompt::Prompt::DoDeviceQuery, this)); + UsbService* service = device::DeviceClient::Get()->GetUsbService(); + if (service && !usb_service_observer_.IsObserving(service)) { + service->GetDevices(base::Bind( + &DevicePermissionsPrompt::Prompt::OnDevicesEnumerated, this)); + usb_service_observer_.Add(service); + } } } @@ -115,9 +105,7 @@ void DevicePermissionsPrompt::Prompt::GrantDevicePermission( DevicePermissionsManager::Get(browser_context_); if (permissions_manager) { const DeviceInfo& device = devices_[index]; - permissions_manager->AllowUsbDevice( - extension_->id(), device.device, device.original_product_string, - device.original_manufacturer_string, device.serial_number); + permissions_manager->AllowUsbDevice(extension_->id(), device.device); } } @@ -129,82 +117,17 @@ void DevicePermissionsPrompt::Prompt::set_filters( DevicePermissionsPrompt::Prompt::~Prompt() { } -void DevicePermissionsPrompt::Prompt::DoDeviceQuery() { - UsbService* service = device::DeviceClient::Get()->GetUsbService(); - if (!service) { +void DevicePermissionsPrompt::Prompt::OnDeviceAdded( + scoped_refptr<UsbDevice> device) { + if (!(filters_.empty() || UsbDeviceFilter::MatchesAny(device, filters_))) { return; } - std::vector<scoped_refptr<UsbDevice>> devices; - service->GetDevices(&devices); - - if (!usb_service_observer_.IsObserving(service)) { - usb_service_observer_.Add(service); - } - - std::vector<DeviceInfo>* device_info = new std::vector<DeviceInfo>(); - base::Closure barrier = base::BarrierClosure( - devices.size(), - base::Bind(&DevicePermissionsPrompt::Prompt::DeviceQueryComplete, this, - base::Owned(device_info))); - - for (const auto& device : devices) { - if (filters_.empty() || UsbDeviceFilter::MatchesAny(device, filters_)) { - device->CheckUsbAccess( - base::Bind(&DevicePermissionsPrompt::Prompt::AppendCheckedUsbDevice, - this, device_info, device, barrier)); - } else { - barrier.Run(); - } - } -} - -void DevicePermissionsPrompt::Prompt::AppendCheckedUsbDevice( - std::vector<DeviceInfo>* device_info, - scoped_refptr<UsbDevice> device, - const base::Closure& callback, - bool allowed) { - if (allowed) { - device_info->push_back(DeviceInfo(device)); - } - callback.Run(); -} - -void DevicePermissionsPrompt::Prompt::AddCheckedUsbDevice( - scoped_refptr<UsbDevice> device, - bool allowed) { - if (allowed) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DevicePermissionsPrompt::Prompt::AddDevice, this, - DeviceInfo(device))); - } -} - -void DevicePermissionsPrompt::Prompt::DeviceQueryComplete( - std::vector<DeviceInfo>* device_info) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DevicePermissionsPrompt::Prompt::SetDevices, this, - *device_info)); -} - -void DevicePermissionsPrompt::Prompt::SetDevices( - const std::vector<DeviceInfo>& devices) { - devices_ = devices; - if (observer_) { - observer_->OnDevicesChanged(); - } -} - -void DevicePermissionsPrompt::Prompt::AddDevice(const DeviceInfo& device) { - devices_.push_back(device); - if (observer_) { - observer_->OnDevicesChanged(); - } + device->CheckUsbAccess(base::Bind( + &DevicePermissionsPrompt::Prompt::AddCheckedUsbDevice, this, device)); } -void DevicePermissionsPrompt::Prompt::RemoveDevice( +void DevicePermissionsPrompt::Prompt::OnDeviceRemoved( scoped_refptr<UsbDevice> device) { bool removed_entry = false; for (std::vector<DeviceInfo>::iterator it = devices_.begin(); @@ -220,21 +143,25 @@ void DevicePermissionsPrompt::Prompt::RemoveDevice( } } -void DevicePermissionsPrompt::Prompt::OnDeviceAdded( - scoped_refptr<UsbDevice> device) { - if (!(filters_.empty() || UsbDeviceFilter::MatchesAny(device, filters_))) { - return; +void DevicePermissionsPrompt::Prompt::OnDevicesEnumerated( + const std::vector<scoped_refptr<UsbDevice>>& devices) { + for (const auto& device : devices) { + if (filters_.empty() || UsbDeviceFilter::MatchesAny(device, filters_)) { + device->CheckUsbAccess(base::Bind( + &DevicePermissionsPrompt::Prompt::AddCheckedUsbDevice, this, device)); + } } - - device->CheckUsbAccess(base::Bind( - &DevicePermissionsPrompt::Prompt::AddCheckedUsbDevice, this, device)); } -void DevicePermissionsPrompt::Prompt::OnDeviceRemoved( - scoped_refptr<UsbDevice> device) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&DevicePermissionsPrompt::Prompt::RemoveDevice, this, device)); +void DevicePermissionsPrompt::Prompt::AddCheckedUsbDevice( + scoped_refptr<UsbDevice> device, + bool allowed) { + if (allowed) { + devices_.push_back(DeviceInfo(device)); + if (observer_) { + observer_->OnDevicesChanged(); + } + } } DevicePermissionsPrompt::DevicePermissionsPrompt( diff --git a/extensions/browser/api/device_permissions_prompt.h b/extensions/browser/api/device_permissions_prompt.h index e7d11ca..26775bf 100644 --- a/extensions/browser/api/device_permissions_prompt.h +++ b/extensions/browser/api/device_permissions_prompt.h @@ -13,6 +13,7 @@ #include "base/scoped_observer.h" #include "base/strings/string16.h" #include "content/public/browser/browser_thread.h" +#include "device/usb/usb_device.h" #include "device/usb/usb_service.h" namespace content { @@ -21,7 +22,6 @@ class WebContents; } namespace device { -class UsbDevice; class UsbDeviceFilter; } @@ -34,9 +34,7 @@ class Extension; class DevicePermissionsPrompt { public: // Context information available to the UI implementation. - class Prompt : public base::RefCountedThreadSafe< - Prompt, - content::BrowserThread::DeleteOnFileThread>, + class Prompt : public base::RefCounted<Prompt>, public device::UsbService::Observer { public: // Displayed properties of a device. @@ -46,9 +44,6 @@ class DevicePermissionsPrompt { scoped_refptr<device::UsbDevice> device; base::string16 name; - base::string16 original_manufacturer_string; - base::string16 original_product_string; - base::string16 serial_number; }; // Since the set of devices can change while the UI is visible an @@ -73,7 +68,7 @@ class DevicePermissionsPrompt { } base::string16 GetDeviceSerialNumber(size_t index) const { DCHECK_LT(index, devices_.size()); - return devices_[index].serial_number; + return devices_[index].device->serial_number(); } // Notifies the DevicePermissionsManager for the current extension that @@ -98,35 +93,25 @@ class DevicePermissionsPrompt { void set_filters(const std::vector<device::UsbDeviceFilter>& filters); private: - friend struct content::BrowserThread::DeleteOnThread< - content::BrowserThread::FILE>; - friend class base::DeleteHelper<Prompt>; + friend class base::RefCounted<Prompt>; virtual ~Prompt(); - // Querying for devices must be done asynchronously on the FILE thread. - void DoDeviceQuery(); - void AppendCheckedUsbDevice(std::vector<DeviceInfo>* device_info, - scoped_refptr<device::UsbDevice> device, - const base::Closure& callback, - bool allowed); - void AddCheckedUsbDevice(scoped_refptr<device::UsbDevice> device, - bool allowed); - void DeviceQueryComplete(std::vector<DeviceInfo>* device_info); - void SetDevices(const std::vector<DeviceInfo>& devices); - void AddDevice(const DeviceInfo& device); - void RemoveDevice(scoped_refptr<device::UsbDevice> device); - // device::UsbService::Observer implementation: void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override; void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override; - const extensions::Extension* extension_; - content::BrowserContext* browser_context_; - bool multiple_; + void OnDevicesEnumerated( + const std::vector<scoped_refptr<device::UsbDevice>>& devices); + void AddCheckedUsbDevice(scoped_refptr<device::UsbDevice> device, + bool allowed); + + const extensions::Extension* extension_ = nullptr; + content::BrowserContext* browser_context_ = nullptr; + bool multiple_ = false; std::vector<device::UsbDeviceFilter> filters_; std::vector<DeviceInfo> devices_; - Observer* observer_; + Observer* observer_ = nullptr; ScopedObserver<device::UsbService, device::UsbService::Observer> usb_service_observer_; }; diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc index f1fcd23..d023813 100644 --- a/extensions/browser/api/usb/usb_api.cc +++ b/extensions/browser/api/usb/usb_api.cc @@ -7,8 +7,8 @@ #include <string> #include <vector> +#include "base/barrier_closure.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop_proxy.h" #include "device/core/device_client.h" #include "device/usb/usb_device_handle.h" #include "device/usb/usb_service.h" @@ -70,9 +70,6 @@ using usb::SynchronizationType; using usb::TransferType; using usb::UsageType; -typedef std::vector<scoped_refptr<UsbDevice> > DeviceVector; -typedef scoped_ptr<DeviceVector> ScopedDeviceVector; - namespace { const char kDataKey[] = "data"; @@ -100,6 +97,7 @@ const char kErrorConvertDirection[] = "Invalid transfer direction."; const char kErrorConvertRecipient[] = "Invalid transfer recipient."; const char kErrorConvertRequestType[] = "Invalid request type."; const char kErrorMalformedParameters[] = "Error parsing parameters."; +const char kErrorNoConnection[] = "No such connection."; const char kErrorNoDevice[] = "No such device."; const char kErrorPermissionDenied[] = "Permission to access device was denied"; const char kErrorInvalidTransferLength[] = @@ -242,56 +240,6 @@ const char* ConvertTransferStatusToApi(const UsbTransferStatus status) { } } -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)->RequestUsbAccess(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)->RequestUsbAccess(interface_id, - base::Bind(RequestUsbDevicesAccessHelper, - base::Passed(devices.Pass()), - i, - interface_id, - callback)); -} - -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) { @@ -434,31 +382,25 @@ void ConvertDeviceFilter(const usb::DeviceFilter& input, namespace extensions { -UsbAsyncApiFunction::UsbAsyncApiFunction() : manager_(NULL) { -} - -UsbAsyncApiFunction::~UsbAsyncApiFunction() { +UsbPermissionCheckingFunction::UsbPermissionCheckingFunction() + : device_permissions_manager_(nullptr) { } -bool UsbAsyncApiFunction::PrePrepare() { - manager_ = ApiResourceManager<UsbDeviceResource>::Get(browser_context()); - set_work_thread_id(BrowserThread::FILE); - return manager_ != NULL; +UsbPermissionCheckingFunction::~UsbPermissionCheckingFunction() { } -bool UsbAsyncApiFunction::Respond() { - return error_.empty(); -} - -bool UsbAsyncApiFunction::HasDevicePermission(scoped_refptr<UsbDevice> device) { - DCHECK(device_permissions_); +bool UsbPermissionCheckingFunction::HasDevicePermission( + scoped_refptr<UsbDevice> device) { + if (!device_permissions_manager_) { + device_permissions_manager_ = + DevicePermissionsManager::Get(browser_context()); + } - // Check the DevicePermissionsManager first so that if an entry is found - // it can be stored for later. This requires the serial number. - base::string16 serial_number; - device->GetSerialNumber(&serial_number); + DevicePermissions* device_permissions = + device_permissions_manager_->GetForExtension(extension_id()); + DCHECK(device_permissions); - permission_entry_ = device_permissions_->FindEntry(device, serial_number); + permission_entry_ = device_permissions->FindEntry(device); if (permission_entry_.get()) { return true; } @@ -475,83 +417,65 @@ bool UsbAsyncApiFunction::HasDevicePermission(scoped_refptr<UsbDevice> device) { return false; } -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().get() || !resource->device()->GetDevice().get()) { - CompleteWithError(kErrorDisconnect); - manager_->Remove(extension_->id(), input_device_handle.handle); - return NULL; - } - - if (resource->device()->GetDevice()->vendor_id() != - input_device_handle.vendor_id || - resource->device()->GetDevice()->product_id() != - input_device_handle.product_id) { - CompleteWithError(kErrorNoDevice); - return NULL; +void UsbPermissionCheckingFunction::RecordDeviceLastUsed() { + if (permission_entry_.get()) { + device_permissions_manager_->UpdateLastUsed(extension_id(), + permission_entry_); } - - return resource->device(); } -void UsbAsyncApiFunction::RemoveUsbDeviceResource(int api_resource_id) { - manager_->Remove(extension_->id(), api_resource_id); +UsbConnectionFunction::UsbConnectionFunction() { } -void UsbAsyncApiFunction::CompleteWithError(const std::string& error) { - SetError(error); - AsyncWorkCompleted(); +UsbConnectionFunction::~UsbConnectionFunction() { } -UsbAsyncApiTransferFunction::UsbAsyncApiTransferFunction() { -} +scoped_refptr<device::UsbDeviceHandle> UsbConnectionFunction::GetDeviceHandle( + const extensions::core_api::usb::ConnectionHandle& handle) { + ApiResourceManager<UsbDeviceResource>* manager = + ApiResourceManager<UsbDeviceResource>::Get(browser_context()); + if (!manager) { + return nullptr; + } -UsbAsyncApiTransferFunction::~UsbAsyncApiTransferFunction() { -} + UsbDeviceResource* resource = manager->Get(extension_id(), handle.handle); + if (!resource) { + return nullptr; + } -void UsbAsyncApiTransferFunction::OnCompleted(UsbTransferStatus status, - scoped_refptr<net::IOBuffer> data, - size_t length) { - if (status != device::USB_TRANSFER_COMPLETED) - SetError(ConvertTransferStatusToApi(status)); + return resource->device(); +} - SetResult(CreateTransferInfo(status, data, length)); - AsyncWorkCompleted(); +void UsbConnectionFunction::ReleaseDeviceHandle( + const extensions::core_api::usb::ConnectionHandle& handle) { + ApiResourceManager<UsbDeviceResource>* manager = + ApiResourceManager<UsbDeviceResource>::Get(browser_context()); + manager->Remove(extension_id(), handle.handle); } -bool UsbAsyncApiTransferFunction::ConvertDirectionSafely( - const Direction& input, - UsbEndpointDirection* output) { - const bool converted = ConvertDirectionFromApi(input, output); - if (!converted) - SetError(kErrorConvertDirection); - return converted; +UsbTransferFunction::UsbTransferFunction() { } -bool UsbAsyncApiTransferFunction::ConvertRequestTypeSafely( - const RequestType& input, - UsbDeviceHandle::TransferRequestType* output) { - const bool converted = ConvertRequestTypeFromApi(input, output); - if (!converted) - SetError(kErrorConvertRequestType); - return converted; +UsbTransferFunction::~UsbTransferFunction() { } -bool UsbAsyncApiTransferFunction::ConvertRecipientSafely( - const Recipient& input, - UsbDeviceHandle::TransferRecipient* output) { - const bool converted = ConvertRecipientFromApi(input, output); - if (!converted) - SetError(kErrorConvertRecipient); - return converted; +void UsbTransferFunction::OnCompleted(UsbTransferStatus status, + scoped_refptr<net::IOBuffer> data, + size_t length) { + scoped_ptr<base::DictionaryValue> transfer_info(new base::DictionaryValue()); + transfer_info->SetInteger(kResultCodeKey, status); + transfer_info->Set(kDataKey, base::BinaryValue::CreateWithCopiedBuffer( + data->data(), length)); + + if (status == device::USB_TRANSFER_COMPLETED) { + Respond(OneArgument(transfer_info.release())); + } else { + scoped_ptr<base::ListValue> error_args(new base::ListValue()); + error_args->Append(transfer_info.release()); + // Returning arguments with an error is wrong but we're stuck with it. + Respond(ErrorWithArguments(error_args.Pass(), + ConvertTransferStatusToApi(status))); + } } UsbFindDevicesFunction::UsbFindDevicesFunction() { @@ -560,71 +484,77 @@ UsbFindDevicesFunction::UsbFindDevicesFunction() { UsbFindDevicesFunction::~UsbFindDevicesFunction() { } -bool UsbFindDevicesFunction::Prepare() { - parameters_ = FindDevices::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbFindDevicesFunction::Run() { + scoped_ptr<extensions::core_api::usb::FindDevices::Params> parameters = + FindDevices::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -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); + vendor_id_ = parameters->options.vendor_id; + product_id_ = parameters->options.product_id; + 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 (!extension()->permissions_data()->CheckAPIPermissionWithParam( APIPermission::kUsbDevice, ¶m)) { - LOG(WARNING) << "Insufficient permissions to access device."; - CompleteWithError(kErrorPermissionDenied); - return; + return RespondNow(Error(kErrorPermissionDenied)); } UsbService* service = device::DeviceClient::Get()->GetUsbService(); if (!service) { - CompleteWithError(kErrorInitService); - return; + return RespondNow(Error(kErrorInitService)); } - ScopedDeviceVector devices(new DeviceVector()); - service->GetDevices(devices.get()); + service->GetDevices( + base::Bind(&UsbFindDevicesFunction::OnGetDevicesComplete, this)); + return RespondLater(); +} - for (DeviceVector::iterator it = devices->begin(); it != devices->end();) { - if ((*it)->vendor_id() != vendor_id || (*it)->product_id() != product_id) { - it = devices->erase(it); +void UsbFindDevicesFunction::OnGetDevicesComplete( + const std::vector<scoped_refptr<UsbDevice>>& devices) { + result_.reset(new base::ListValue()); + barrier_ = base::BarrierClosure( + devices.size(), base::Bind(&UsbFindDevicesFunction::OpenComplete, this)); + + for (const scoped_refptr<UsbDevice>& device : devices) { + if (device->vendor_id() != vendor_id_ || + device->product_id() != product_id_) { + barrier_.Run(); } else { - ++it; + device->RequestUsbAccess( + interface_id_, + base::Bind(&UsbFindDevicesFunction::OnRequestAccessComplete, this, + device)); } } - - RequestUsbDevicesAccess( - devices.Pass(), - interface_id, - base::Bind(&UsbFindDevicesFunction::OpenDevices, this)); } -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.get()) - device_handles_.push_back(device_handle); +void UsbFindDevicesFunction::OnRequestAccessComplete( + scoped_refptr<UsbDevice> device, + bool success) { + if (success) { + device->Open(base::Bind(&UsbFindDevicesFunction::OnDeviceOpened, this)); + } else { + barrier_.Run(); } +} - 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)); +void UsbFindDevicesFunction::OnDeviceOpened( + scoped_refptr<UsbDeviceHandle> device_handle) { + if (device_handle.get()) { + ApiResourceManager<UsbDeviceResource>* manager = + ApiResourceManager<UsbDeviceResource>::Get(browser_context()); + UsbDeviceResource* resource = + new UsbDeviceResource(extension_id(), device_handle); + scoped_refptr<UsbDevice> device = device_handle->GetDevice(); + result_->Append(PopulateConnectionHandle( + manager->Add(resource), device->vendor_id(), device->product_id())); } + barrier_.Run(); +} - SetResult(result); - AsyncWorkCompleted(); +void UsbFindDevicesFunction::OpenComplete() { + Respond(OneArgument(result_.release())); } UsbGetDevicesFunction::UsbGetDevicesFunction() { @@ -633,51 +563,47 @@ UsbGetDevicesFunction::UsbGetDevicesFunction() { UsbGetDevicesFunction::~UsbGetDevicesFunction() { } -bool UsbGetDevicesFunction::Prepare() { - parameters_ = GetDevices::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - device_permissions_ = DevicePermissionsManager::Get(browser_context()) - ->GetForExtension(extension()->id()); - return true; -} +ExtensionFunction::ResponseAction UsbGetDevicesFunction::Run() { + scoped_ptr<extensions::core_api::usb::GetDevices::Params> parameters = + GetDevices::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbGetDevicesFunction::AsyncWorkStart() { - std::vector<UsbDeviceFilter> filters; - if (parameters_->options.filters) { - filters.resize(parameters_->options.filters->size()); - for (size_t i = 0; i < parameters_->options.filters->size(); ++i) { - ConvertDeviceFilter(*parameters_->options.filters->at(i).get(), - &filters[i]); + if (parameters->options.filters) { + filters_.resize(parameters->options.filters->size()); + for (size_t i = 0; i < parameters->options.filters->size(); ++i) { + ConvertDeviceFilter(*parameters->options.filters->at(i).get(), + &filters_[i]); } } - if (parameters_->options.vendor_id) { - filters.resize(filters.size() + 1); - filters.back().SetVendorId(*parameters_->options.vendor_id); - if (parameters_->options.product_id) { - filters.back().SetProductId(*parameters_->options.product_id); + if (parameters->options.vendor_id) { + filters_.resize(filters_.size() + 1); + 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(); if (!service) { - CompleteWithError(kErrorInitService); - return; + return RespondNow(Error(kErrorInitService)); } - DeviceVector devices; - service->GetDevices(&devices); + service->GetDevices( + base::Bind(&UsbGetDevicesFunction::OnGetDevicesComplete, this)); + return RespondLater(); +} +void UsbGetDevicesFunction::OnGetDevicesComplete( + const std::vector<scoped_refptr<UsbDevice>>& devices) { scoped_ptr<base::ListValue> result(new base::ListValue()); - for (DeviceVector::iterator it = devices.begin(); it != devices.end(); ++it) { - scoped_refptr<UsbDevice> device = *it; - if ((filters.empty() || UsbDeviceFilter::MatchesAny(device, filters)) && + for (const scoped_refptr<UsbDevice>& device : devices) { + if ((filters_.empty() || UsbDeviceFilter::MatchesAny(device, filters_)) && HasDevicePermission(device)) { - result->Append(PopulateDevice(it->get())); + result->Append(PopulateDevice(device.get())); } } - SetResult(result.release()); - AsyncWorkCompleted(); + Respond(OneArgument(result.release())); } UsbGetUserSelectedDevicesFunction::UsbGetUserSelectedDevicesFunction() { @@ -686,7 +612,7 @@ UsbGetUserSelectedDevicesFunction::UsbGetUserSelectedDevicesFunction() { UsbGetUserSelectedDevicesFunction::~UsbGetUserSelectedDevicesFunction() { } -AsyncApiFunction::ResponseAction UsbGetUserSelectedDevicesFunction::Run() { +ExtensionFunction::ResponseAction UsbGetUserSelectedDevicesFunction::Run() { scoped_ptr<extensions::core_api::usb::GetUserSelectedDevices::Params> parameters = GetUserSelectedDevices::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); @@ -728,8 +654,7 @@ void UsbGetUserSelectedDevicesFunction::OnUsbDevicesChosen( result->Append(PopulateDevice(device.get())); } - SetResult(result.release()); - SendResponse(true); + Respond(OneArgument(result.release())); Release(); } @@ -739,15 +664,11 @@ UsbRequestAccessFunction::UsbRequestAccessFunction() { UsbRequestAccessFunction::~UsbRequestAccessFunction() { } -bool UsbRequestAccessFunction::Prepare() { - parameters_ = RequestAccess::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} - -void UsbRequestAccessFunction::AsyncWorkStart() { - SetResult(new base::FundamentalValue(true)); - AsyncWorkCompleted(); +ExtensionFunction::ResponseAction UsbRequestAccessFunction::Run() { + scoped_ptr<extensions::core_api::usb::RequestAccess::Params> parameters = + RequestAccess::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); + return RespondNow(OneArgument(new base::FundamentalValue(true))); } UsbOpenDeviceFunction::UsbOpenDeviceFunction() { @@ -756,67 +677,60 @@ UsbOpenDeviceFunction::UsbOpenDeviceFunction() { UsbOpenDeviceFunction::~UsbOpenDeviceFunction() { } -bool UsbOpenDeviceFunction::Prepare() { - parameters_ = OpenDevice::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - device_permissions_manager_ = - DevicePermissionsManager::Get(browser_context()); - device_permissions_ = - device_permissions_manager_->GetForExtension(extension()->id()); - return true; -} +ExtensionFunction::ResponseAction UsbOpenDeviceFunction::Run() { + scoped_ptr<extensions::core_api::usb::OpenDevice::Params> parameters = + OpenDevice::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbOpenDeviceFunction::AsyncWorkStart() { UsbService* service = device::DeviceClient::Get()->GetUsbService(); if (!service) { - CompleteWithError(kErrorInitService); - return; + return RespondNow(Error(kErrorInitService)); } - device_ = service->GetDeviceById(parameters_->device.device); - if (!device_.get()) { - CompleteWithError(kErrorNoDevice); - return; + scoped_refptr<UsbDevice> device = + service->GetDeviceById(parameters->device.device); + if (!device.get()) { + return RespondNow(Error(kErrorNoDevice)); } - if (!HasDevicePermission(device_)) { + if (!HasDevicePermission(device)) { // This function must act as if there is no such device. Otherwise it can be // used to fingerprint unauthorized devices. - CompleteWithError(kErrorNoDevice); - return; + return RespondNow(Error(kErrorNoDevice)); } - device_->RequestUsbAccess( + device->RequestUsbAccess( -1, /* any interface, unused by the permission broker */ - base::Bind(&UsbOpenDeviceFunction::OnRequestAccessComplete, this)); + base::Bind(&UsbOpenDeviceFunction::OnRequestAccessComplete, this, + device)); + return RespondLater(); } -void UsbOpenDeviceFunction::OnRequestAccessComplete(bool success) { - if (!success) { - SetError(kErrorPermissionDenied); - AsyncWorkCompleted(); - return; +void UsbOpenDeviceFunction::OnRequestAccessComplete( + scoped_refptr<UsbDevice> device, + bool success) { + if (success) { + device->Open(base::Bind(&UsbOpenDeviceFunction::OnDeviceOpened, this)); + } else { + Respond(Error(kErrorPermissionDenied)); } +} - scoped_refptr<UsbDeviceHandle> handle = device_->Open(); - if (!handle.get()) { - SetError(kErrorOpen); - AsyncWorkCompleted(); +void UsbOpenDeviceFunction::OnDeviceOpened( + scoped_refptr<UsbDeviceHandle> device_handle) { + if (!device_handle.get()) { + Respond(Error(kErrorOpen)); return; } - SetResult(PopulateConnectionHandle( - manager_->Add(new UsbDeviceResource(extension_->id(), handle)), - device_->vendor_id(), device_->product_id())); - AsyncWorkCompleted(); -} + RecordDeviceLastUsed(); -bool UsbOpenDeviceFunction::Respond() { - if (permission_entry_.get()) { - device_permissions_manager_->UpdateLastUsed(extension_->id(), - permission_entry_); - } - return UsbAsyncApiFunction::Respond(); + ApiResourceManager<UsbDeviceResource>* manager = + ApiResourceManager<UsbDeviceResource>::Get(browser_context()); + scoped_refptr<UsbDevice> device = device_handle->GetDevice(); + Respond(OneArgument(PopulateConnectionHandle( + manager->Add(new UsbDeviceResource(extension_id(), device_handle)), + device->vendor_id(), device->product_id()))); } UsbSetConfigurationFunction::UsbSetConfigurationFunction() { @@ -825,23 +739,29 @@ UsbSetConfigurationFunction::UsbSetConfigurationFunction() { UsbSetConfigurationFunction::~UsbSetConfigurationFunction() { } -bool UsbSetConfigurationFunction::Prepare() { - parameters_ = SetConfiguration::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbSetConfigurationFunction::Run() { + scoped_ptr<extensions::core_api::usb::SetConfiguration::Params> parameters = + SetConfiguration::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbSetConfigurationFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); + GetDeviceHandle(parameters->handle); if (!device_handle.get()) { - return; + return RespondNow(Error(kErrorNoConnection)); } - if (!device_handle->SetConfiguration(parameters_->configuration_value)) { - SetError(kErrorCannotSetConfiguration); + device_handle->SetConfiguration( + parameters->configuration_value, + base::Bind(&UsbSetConfigurationFunction::OnComplete, this)); + return RespondLater(); +} + +void UsbSetConfigurationFunction::OnComplete(bool success) { + if (success) { + Respond(NoArguments()); + } else { + Respond(Error(kErrorCannotSetConfiguration)); } - AsyncWorkCompleted(); } UsbGetConfigurationFunction::UsbGetConfigurationFunction() { @@ -850,17 +770,15 @@ UsbGetConfigurationFunction::UsbGetConfigurationFunction() { UsbGetConfigurationFunction::~UsbGetConfigurationFunction() { } -bool UsbGetConfigurationFunction::Prepare() { - parameters_ = GetConfiguration::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbGetConfigurationFunction::Run() { + scoped_ptr<extensions::core_api::usb::GetConfiguration::Params> parameters = + GetConfiguration::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbGetConfigurationFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); + GetDeviceHandle(parameters->handle); if (!device_handle.get()) { - return; + return RespondNow(Error(kErrorNoConnection)); } const UsbConfigDescriptor* config_descriptor = @@ -868,12 +786,10 @@ void UsbGetConfigurationFunction::AsyncWorkStart() { if (config_descriptor) { ConfigDescriptor config; ConvertConfigDescriptor(*config_descriptor, &config); - SetResult(config.ToValue().release()); + return RespondNow(OneArgument(config.ToValue().release())); } else { - SetError(kErrorNotConfigured); + return RespondNow(Error(kErrorNotConfigured)); } - - AsyncWorkCompleted(); } UsbListInterfacesFunction::UsbListInterfacesFunction() { @@ -882,17 +798,15 @@ UsbListInterfacesFunction::UsbListInterfacesFunction() { UsbListInterfacesFunction::~UsbListInterfacesFunction() { } -bool UsbListInterfacesFunction::Prepare() { - parameters_ = ListInterfaces::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbListInterfacesFunction::Run() { + scoped_ptr<extensions::core_api::usb::ListInterfaces::Params> parameters = + ListInterfaces::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbListInterfacesFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); + GetDeviceHandle(parameters->handle); if (!device_handle.get()) { - return; + return RespondNow(Error(kErrorNoConnection)); } const UsbConfigDescriptor* config_descriptor = @@ -906,12 +820,10 @@ void UsbListInterfacesFunction::AsyncWorkStart() { result->Append(config.interfaces[i]->ToValue().release()); } - SetResult(result.release()); + return RespondNow(OneArgument(result.release())); } else { - SetError(kErrorNotConfigured); + return RespondNow(Error(kErrorNotConfigured)); } - - AsyncWorkCompleted(); } UsbCloseDeviceFunction::UsbCloseDeviceFunction() { @@ -920,21 +832,20 @@ UsbCloseDeviceFunction::UsbCloseDeviceFunction() { UsbCloseDeviceFunction::~UsbCloseDeviceFunction() { } -bool UsbCloseDeviceFunction::Prepare() { - parameters_ = CloseDevice::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbCloseDeviceFunction::Run() { + scoped_ptr<extensions::core_api::usb::CloseDevice::Params> parameters = + CloseDevice::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbCloseDeviceFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } // The device handle is closed when the resource is destroyed. - RemoveUsbDeviceResource(parameters_->handle.handle); - AsyncWorkCompleted(); + ReleaseDeviceHandle(parameters->handle); + return RespondNow(NoArguments()); } UsbClaimInterfaceFunction::UsbClaimInterfaceFunction() { @@ -943,23 +854,29 @@ UsbClaimInterfaceFunction::UsbClaimInterfaceFunction() { UsbClaimInterfaceFunction::~UsbClaimInterfaceFunction() { } -bool UsbClaimInterfaceFunction::Prepare() { - parameters_ = ClaimInterface::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbClaimInterfaceFunction::Run() { + scoped_ptr<extensions::core_api::usb::ClaimInterface::Params> parameters = + ClaimInterface::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbClaimInterfaceFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } - bool success = device_handle->ClaimInterface(parameters_->interface_number); + device_handle->ClaimInterface( + parameters->interface_number, + base::Bind(&UsbClaimInterfaceFunction::OnComplete, this)); + return RespondLater(); +} - if (!success) - SetError(kErrorCannotClaimInterface); - AsyncWorkCompleted(); +void UsbClaimInterfaceFunction::OnComplete(bool success) { + if (success) { + Respond(NoArguments()); + } else { + Respond(Error(kErrorCannotClaimInterface)); + } } UsbReleaseInterfaceFunction::UsbReleaseInterfaceFunction() { @@ -968,22 +885,22 @@ UsbReleaseInterfaceFunction::UsbReleaseInterfaceFunction() { UsbReleaseInterfaceFunction::~UsbReleaseInterfaceFunction() { } -bool UsbReleaseInterfaceFunction::Prepare() { - parameters_ = ReleaseInterface::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbReleaseInterfaceFunction::Run() { + scoped_ptr<extensions::core_api::usb::ReleaseInterface::Params> parameters = + ReleaseInterface::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbReleaseInterfaceFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } - bool success = device_handle->ReleaseInterface(parameters_->interface_number); - if (!success) - SetError(kErrorCannotReleaseInterface); - AsyncWorkCompleted(); + if (device_handle->ReleaseInterface(parameters->interface_number)) { + return RespondNow(NoArguments()); + } else { + return RespondNow(Error(kErrorCannotReleaseInterface)); + } } UsbSetInterfaceAlternateSettingFunction:: @@ -994,24 +911,30 @@ UsbSetInterfaceAlternateSettingFunction:: ~UsbSetInterfaceAlternateSettingFunction() { } -bool UsbSetInterfaceAlternateSettingFunction::Prepare() { - parameters_ = SetInterfaceAlternateSetting::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction +UsbSetInterfaceAlternateSettingFunction::Run() { + scoped_ptr<extensions::core_api::usb::SetInterfaceAlternateSetting::Params> + parameters = SetInterfaceAlternateSetting::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbSetInterfaceAlternateSettingFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } - bool success = device_handle->SetInterfaceAlternateSetting( - parameters_->interface_number, parameters_->alternate_setting); - if (!success) - SetError(kErrorCannotSetInterfaceAlternateSetting); + device_handle->SetInterfaceAlternateSetting( + parameters->interface_number, parameters->alternate_setting, + base::Bind(&UsbSetInterfaceAlternateSettingFunction::OnComplete, this)); + return RespondLater(); +} - AsyncWorkCompleted(); +void UsbSetInterfaceAlternateSettingFunction::OnComplete(bool success) { + if (success) { + Respond(NoArguments()); + } else { + Respond(Error(kErrorCannotSetInterfaceAlternateSetting)); + } } UsbControlTransferFunction::UsbControlTransferFunction() { @@ -1020,54 +943,55 @@ UsbControlTransferFunction::UsbControlTransferFunction() { UsbControlTransferFunction::~UsbControlTransferFunction() { } -bool UsbControlTransferFunction::Prepare() { - parameters_ = ControlTransfer::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbControlTransferFunction::Run() { + scoped_ptr<extensions::core_api::usb::ControlTransfer::Params> parameters = + ControlTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbControlTransferFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; - - const ControlTransferInfo& transfer = parameters_->transfer_info; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } + 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 (!ConvertDirectionFromApi(transfer.direction, &direction)) { + return RespondNow(Error(kErrorConvertDirection)); + } + + if (!ConvertRequestTypeFromApi(transfer.request_type, &request_type)) { + return RespondNow(Error(kErrorConvertRequestType)); + } + + if (!ConvertRecipientFromApi(transfer.recipient, &recipient)) { + return RespondNow(Error(kErrorConvertRecipient)); } if (!GetTransferSize(transfer, &size)) { - CompleteWithError(kErrorInvalidTransferLength); - return; + return RespondNow(Error(kErrorInvalidTransferLength)); } scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer, direction, size); if (!buffer.get()) { - CompleteWithError(kErrorMalformedParameters); - return; + return RespondNow(Error(kErrorMalformedParameters)); } int timeout = transfer.timeout ? *transfer.timeout : 0; if (timeout < 0) { - CompleteWithError(kErrorInvalidTimeout); - return; + return RespondNow(Error(kErrorInvalidTimeout)); } device_handle->ControlTransfer( direction, request_type, recipient, transfer.request, transfer.value, transfer.index, buffer.get(), size, timeout, base::Bind(&UsbControlTransferFunction::OnCompleted, this)); + return RespondLater(); } UsbBulkTransferFunction::UsbBulkTransferFunction() { @@ -1076,49 +1000,44 @@ UsbBulkTransferFunction::UsbBulkTransferFunction() { UsbBulkTransferFunction::~UsbBulkTransferFunction() { } -bool UsbBulkTransferFunction::Prepare() { - parameters_ = BulkTransfer::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbBulkTransferFunction::Run() { + scoped_ptr<extensions::core_api::usb::BulkTransfer::Params> parameters = + BulkTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbBulkTransferFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; - - const GenericTransferInfo& transfer = parameters_->transfer_info; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } + const GenericTransferInfo& transfer = parameters->transfer_info; UsbEndpointDirection direction; size_t size = 0; - if (!ConvertDirectionSafely(transfer.direction, &direction)) { - AsyncWorkCompleted(); - return; + if (!ConvertDirectionFromApi(transfer.direction, &direction)) { + return RespondNow(Error(kErrorConvertDirection)); } if (!GetTransferSize(transfer, &size)) { - CompleteWithError(kErrorInvalidTransferLength); - return; + return RespondNow(Error(kErrorInvalidTransferLength)); } scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer, direction, size); if (!buffer.get()) { - CompleteWithError(kErrorMalformedParameters); - return; + return RespondNow(Error(kErrorMalformedParameters)); } int timeout = transfer.timeout ? *transfer.timeout : 0; if (timeout < 0) { - CompleteWithError(kErrorInvalidTimeout); - return; + return RespondNow(Error(kErrorInvalidTimeout)); } device_handle->BulkTransfer( direction, transfer.endpoint, buffer.get(), size, timeout, base::Bind(&UsbBulkTransferFunction::OnCompleted, this)); + return RespondLater(); } UsbInterruptTransferFunction::UsbInterruptTransferFunction() { @@ -1127,49 +1046,44 @@ UsbInterruptTransferFunction::UsbInterruptTransferFunction() { UsbInterruptTransferFunction::~UsbInterruptTransferFunction() { } -bool UsbInterruptTransferFunction::Prepare() { - parameters_ = InterruptTransfer::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbInterruptTransferFunction::Run() { + scoped_ptr<extensions::core_api::usb::InterruptTransfer::Params> parameters = + InterruptTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbInterruptTransferFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; - - const GenericTransferInfo& transfer = parameters_->transfer_info; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } + const GenericTransferInfo& transfer = parameters->transfer_info; UsbEndpointDirection direction; size_t size = 0; - if (!ConvertDirectionSafely(transfer.direction, &direction)) { - AsyncWorkCompleted(); - return; + if (!ConvertDirectionFromApi(transfer.direction, &direction)) { + return RespondNow(Error(kErrorConvertDirection)); } if (!GetTransferSize(transfer, &size)) { - CompleteWithError(kErrorInvalidTransferLength); - return; + return RespondNow(Error(kErrorInvalidTransferLength)); } scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(transfer, direction, size); if (!buffer.get()) { - CompleteWithError(kErrorMalformedParameters); - return; + return RespondNow(Error(kErrorMalformedParameters)); } int timeout = transfer.timeout ? *transfer.timeout : 0; if (timeout < 0) { - CompleteWithError(kErrorInvalidTimeout); - return; + return RespondNow(Error(kErrorInvalidTimeout)); } device_handle->InterruptTransfer( direction, transfer.endpoint, buffer.get(), size, timeout, base::Bind(&UsbInterruptTransferFunction::OnCompleted, this)); + return RespondLater(); } UsbIsochronousTransferFunction::UsbIsochronousTransferFunction() { @@ -1178,66 +1092,62 @@ UsbIsochronousTransferFunction::UsbIsochronousTransferFunction() { UsbIsochronousTransferFunction::~UsbIsochronousTransferFunction() { } -bool UsbIsochronousTransferFunction::Prepare() { - parameters_ = IsochronousTransfer::Params::Create(*args_); - EXTENSION_FUNCTION_VALIDATE(parameters_.get()); - return true; -} +ExtensionFunction::ResponseAction UsbIsochronousTransferFunction::Run() { + scoped_ptr<extensions::core_api::usb::IsochronousTransfer::Params> + parameters = IsochronousTransfer::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters.get()); -void UsbIsochronousTransferFunction::AsyncWorkStart() { scoped_refptr<UsbDeviceHandle> device_handle = - GetDeviceHandleOrCompleteWithError(parameters_->handle); - if (!device_handle.get()) - return; + GetDeviceHandle(parameters->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); + } - const IsochronousTransferInfo& transfer = parameters_->transfer_info; + 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 (!ConvertDirectionFromApi(generic_transfer.direction, &direction)) { + return RespondNow(Error(kErrorConvertDirection)); } + if (!GetTransferSize(generic_transfer, &size)) { - CompleteWithError(kErrorInvalidTransferLength); - return; + return RespondNow(Error(kErrorInvalidTransferLength)); } + if (transfer.packets < 0 || transfer.packets >= kMaxPackets) { - CompleteWithError(kErrorInvalidNumberOfPackets); - return; + return RespondNow(Error(kErrorInvalidNumberOfPackets)); } + unsigned int packets = transfer.packets; if (transfer.packet_length < 0 || transfer.packet_length >= kMaxPacketLength) { - CompleteWithError(kErrorInvalidPacketLength); - return; + return RespondNow(Error(kErrorInvalidPacketLength)); } + unsigned int packet_length = transfer.packet_length; const uint64 total_length = packets * packet_length; if (packets > size || total_length > size) { - CompleteWithError(kErrorTransferLength); - return; + return RespondNow(Error(kErrorTransferLength)); } scoped_refptr<net::IOBuffer> buffer = CreateBufferForTransfer(generic_transfer, direction, size); if (!buffer.get()) { - CompleteWithError(kErrorMalformedParameters); - return; + return RespondNow(Error(kErrorMalformedParameters)); } int timeout = generic_transfer.timeout ? *generic_transfer.timeout : 0; if (timeout < 0) { - CompleteWithError(kErrorInvalidTimeout); - return; + return RespondNow(Error(kErrorInvalidTimeout)); } device_handle->IsochronousTransfer( direction, generic_transfer.endpoint, buffer.get(), size, packets, packet_length, timeout, base::Bind(&UsbIsochronousTransferFunction::OnCompleted, this)); + return RespondLater(); } UsbResetDeviceFunction::UsbResetDeviceFunction() { @@ -1246,29 +1156,37 @@ UsbResetDeviceFunction::UsbResetDeviceFunction() { UsbResetDeviceFunction::~UsbResetDeviceFunction() { } -bool UsbResetDeviceFunction::Prepare() { +ExtensionFunction::ResponseAction UsbResetDeviceFunction::Run() { 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.get()) - return; - - bool success = device_handle->ResetDevice(); - if (!success) { - device_handle->Close(); - RemoveUsbDeviceResource(parameters_->handle.handle); - SetResult(new base::FundamentalValue(false)); - CompleteWithError(kErrorResetDevice); - return; + GetDeviceHandle(parameters_->handle); + if (!device_handle.get()) { + return RespondNow(Error(kErrorNoConnection)); } - SetResult(new base::FundamentalValue(true)); - AsyncWorkCompleted(); + device_handle->ResetDevice( + base::Bind(&UsbResetDeviceFunction::OnComplete, this)); + return RespondLater(); +} + +void UsbResetDeviceFunction::OnComplete(bool success) { + if (success) { + Respond(OneArgument(new base::FundamentalValue(true))); + } else { + scoped_refptr<UsbDeviceHandle> device_handle = + GetDeviceHandle(parameters_->handle); + if (device_handle.get()) { + device_handle->Close(); + } + ReleaseDeviceHandle(parameters_->handle); + + scoped_ptr<base::ListValue> error_args(new base::ListValue()); + error_args->AppendBoolean(false); + // Returning arguments with an error is wrong but we're stuck with it. + Respond(ErrorWithArguments(error_args.Pass(), kErrorResetDevice)); + } } } // namespace extensions diff --git a/extensions/browser/api/usb/usb_api.h b/extensions/browser/api/usb/usb_api.h index 8903348..c7ab6c3 100644 --- a/extensions/browser/api/usb/usb_api.h +++ b/extensions/browser/api/usb/usb_api.h @@ -14,8 +14,8 @@ #include "device/usb/usb_device_filter.h" #include "device/usb/usb_device_handle.h" #include "extensions/browser/api/api_resource_manager.h" -#include "extensions/browser/api/async_api_function.h" #include "extensions/browser/api/device_permissions_prompt.h" +#include "extensions/browser/extension_function.h" #include "extensions/common/api/usb.h" #include "net/base/io_buffer.h" @@ -26,83 +26,86 @@ class DevicePermissions; class DevicePermissionsManager; class UsbDeviceResource; -class UsbAsyncApiFunction : public AsyncApiFunction { - public: - UsbAsyncApiFunction(); - +class UsbPermissionCheckingFunction : public UIThreadExtensionFunction { protected: - ~UsbAsyncApiFunction() override; - - // AsyncApiFunction: - bool PrePrepare() override; - bool Respond() override; + UsbPermissionCheckingFunction(); + ~UsbPermissionCheckingFunction() override; bool HasDevicePermission(scoped_refptr<device::UsbDevice> device); - scoped_refptr<device::UsbDeviceHandle> GetDeviceHandleOrCompleteWithError( - const extensions::core_api::usb::ConnectionHandle& input_device_handle); - void RemoveUsbDeviceResource(int api_resource_id); - void CompleteWithError(const std::string& error); + void RecordDeviceLastUsed(); - ApiResourceManager<UsbDeviceResource>* manager_; - scoped_ptr<DevicePermissions> device_permissions_; + private: + DevicePermissionsManager* device_permissions_manager_; scoped_refptr<DevicePermissionEntry> permission_entry_; }; -class UsbAsyncApiTransferFunction : public UsbAsyncApiFunction { +class UsbConnectionFunction : public UIThreadExtensionFunction { protected: - UsbAsyncApiTransferFunction(); - ~UsbAsyncApiTransferFunction() override; - - bool ConvertDirectionSafely(const extensions::core_api::usb::Direction& input, - device::UsbEndpointDirection* output); - bool ConvertRequestTypeSafely( - const extensions::core_api::usb::RequestType& input, - device::UsbDeviceHandle::TransferRequestType* output); - bool ConvertRecipientSafely( - const extensions::core_api::usb::Recipient& input, - device::UsbDeviceHandle::TransferRecipient* output); + UsbConnectionFunction(); + ~UsbConnectionFunction() override; + + scoped_refptr<device::UsbDeviceHandle> GetDeviceHandle( + const extensions::core_api::usb::ConnectionHandle& handle); + void ReleaseDeviceHandle( + const extensions::core_api::usb::ConnectionHandle& handle); +}; + +class UsbTransferFunction : public UsbConnectionFunction { + protected: + UsbTransferFunction(); + ~UsbTransferFunction() override; void OnCompleted(device::UsbTransferStatus status, scoped_refptr<net::IOBuffer> data, size_t length); }; -class UsbFindDevicesFunction : public UsbAsyncApiFunction { +class UsbFindDevicesFunction : public UIThreadExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.findDevices", USB_FINDDEVICES) UsbFindDevicesFunction(); - protected: + private: ~UsbFindDevicesFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; + + void OnGetDevicesComplete( + const std::vector<scoped_refptr<device::UsbDevice>>& devices); + void OnRequestAccessComplete(scoped_refptr<device::UsbDevice> device, + bool success); + void OnDeviceOpened(scoped_refptr<device::UsbDeviceHandle> device_handle); + void OpenComplete(); - private: - void OpenDevices( - scoped_ptr<std::vector<scoped_refptr<device::UsbDevice> > > devices); + uint16_t vendor_id_; + uint16_t product_id_; + int interface_id_; + scoped_ptr<base::ListValue> result_; + base::Closure barrier_; - std::vector<scoped_refptr<device::UsbDeviceHandle> > device_handles_; - scoped_ptr<extensions::core_api::usb::FindDevices::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbFindDevicesFunction); }; -class UsbGetDevicesFunction : public UsbAsyncApiFunction { +class UsbGetDevicesFunction : public UsbPermissionCheckingFunction { public: DECLARE_EXTENSION_FUNCTION("usb.getDevices", USB_GETDEVICES) UsbGetDevicesFunction(); - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; - - protected: + private: ~UsbGetDevicesFunction() override; - private: - scoped_ptr<extensions::core_api::usb::GetDevices::Params> parameters_; + // ExtensionFunction: + ResponseAction Run() override; + + void OnGetDevicesComplete( + const std::vector<scoped_refptr<device::UsbDevice>>& devices); + + std::vector<device::UsbDeviceFilter> filters_; + + DISALLOW_COPY_AND_ASSIGN(UsbGetDevicesFunction); }; class UsbGetUserSelectedDevicesFunction @@ -114,130 +117,117 @@ class UsbGetUserSelectedDevicesFunction UsbGetUserSelectedDevicesFunction(); - protected: + private: ~UsbGetUserSelectedDevicesFunction() override; // ExtensionFunction: ResponseAction Run() override; - private: void OnUsbDevicesChosen( const std::vector<scoped_refptr<device::UsbDevice>>& devices) override; scoped_ptr<DevicePermissionsPrompt> prompt_; - std::vector<uint32> device_ids_; - std::vector<scoped_refptr<device::UsbDevice>> devices_; - std::vector<base::string16> serial_numbers_; + + DISALLOW_COPY_AND_ASSIGN(UsbGetUserSelectedDevicesFunction); }; -class UsbRequestAccessFunction : public UsbAsyncApiFunction { +class UsbRequestAccessFunction : public UIThreadExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.requestAccess", USB_REQUESTACCESS) UsbRequestAccessFunction(); - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; - - protected: + private: ~UsbRequestAccessFunction() override; - private: - scoped_ptr<extensions::core_api::usb::RequestAccess::Params> parameters_; + // ExtensionFunction: + ResponseAction Run() override; + + DISALLOW_COPY_AND_ASSIGN(UsbRequestAccessFunction); }; -class UsbOpenDeviceFunction : public UsbAsyncApiFunction { +class UsbOpenDeviceFunction : public UsbPermissionCheckingFunction { public: DECLARE_EXTENSION_FUNCTION("usb.openDevice", USB_OPENDEVICE) UsbOpenDeviceFunction(); - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; - bool Respond() override; - - protected: + private: ~UsbOpenDeviceFunction() override; - private: - void OnRequestAccessComplete(bool success); + // ExtensionFunction: + ResponseAction Run() override; - DevicePermissionsManager* device_permissions_manager_; - scoped_refptr<device::UsbDevice> device_; - scoped_ptr<extensions::core_api::usb::OpenDevice::Params> parameters_; + void OnRequestAccessComplete(scoped_refptr<device::UsbDevice> device, + bool success); + void OnDeviceOpened(scoped_refptr<device::UsbDeviceHandle> device_handle); + + DISALLOW_COPY_AND_ASSIGN(UsbOpenDeviceFunction); }; -class UsbSetConfigurationFunction : public UsbAsyncApiFunction { +class UsbSetConfigurationFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.setConfiguration", USB_SETCONFIGURATION) UsbSetConfigurationFunction(); - protected: + private: ~UsbSetConfigurationFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::SetConfiguration::Params> parameters_; + void OnComplete(bool success); + + DISALLOW_COPY_AND_ASSIGN(UsbSetConfigurationFunction); }; -class UsbGetConfigurationFunction : public UsbAsyncApiFunction { +class UsbGetConfigurationFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.getConfiguration", USB_GETCONFIGURATION) UsbGetConfigurationFunction(); - protected: + private: ~UsbGetConfigurationFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::GetConfiguration::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbGetConfigurationFunction); }; -class UsbListInterfacesFunction : public UsbAsyncApiFunction { +class UsbListInterfacesFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.listInterfaces", USB_LISTINTERFACES) UsbListInterfacesFunction(); - protected: + private: ~UsbListInterfacesFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::ListInterfaces::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbListInterfacesFunction); }; -class UsbCloseDeviceFunction : public UsbAsyncApiFunction { +class UsbCloseDeviceFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.closeDevice", USB_CLOSEDEVICE) UsbCloseDeviceFunction(); - protected: + private: ~UsbCloseDeviceFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::CloseDevice::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbCloseDeviceFunction); }; -class UsbClaimInterfaceFunction : public UsbAsyncApiFunction { +class UsbClaimInterfaceFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.claimInterface", USB_CLAIMINTERFACE) @@ -246,32 +236,30 @@ class UsbClaimInterfaceFunction : public UsbAsyncApiFunction { protected: ~UsbClaimInterfaceFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; + + void OnComplete(bool success); - private: - scoped_ptr<extensions::core_api::usb::ClaimInterface::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbClaimInterfaceFunction); }; -class UsbReleaseInterfaceFunction : public UsbAsyncApiFunction { +class UsbReleaseInterfaceFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.releaseInterface", USB_RELEASEINTERFACE) UsbReleaseInterfaceFunction(); - protected: + private: ~UsbReleaseInterfaceFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::ReleaseInterface::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbReleaseInterfaceFunction); }; -class UsbSetInterfaceAlternateSettingFunction : public UsbAsyncApiFunction { +class UsbSetInterfaceAlternateSettingFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.setInterfaceAlternateSetting", USB_SETINTERFACEALTERNATESETTING) @@ -281,98 +269,91 @@ class UsbSetInterfaceAlternateSettingFunction : public UsbAsyncApiFunction { private: ~UsbSetInterfaceAlternateSettingFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; + + void OnComplete(bool success); - scoped_ptr<extensions::core_api::usb::SetInterfaceAlternateSetting::Params> - parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbSetInterfaceAlternateSettingFunction); }; -class UsbControlTransferFunction : public UsbAsyncApiTransferFunction { +class UsbControlTransferFunction : public UsbTransferFunction { public: DECLARE_EXTENSION_FUNCTION("usb.controlTransfer", USB_CONTROLTRANSFER) UsbControlTransferFunction(); - protected: + private: ~UsbControlTransferFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::ControlTransfer::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbControlTransferFunction); }; -class UsbBulkTransferFunction : public UsbAsyncApiTransferFunction { +class UsbBulkTransferFunction : public UsbTransferFunction { public: DECLARE_EXTENSION_FUNCTION("usb.bulkTransfer", USB_BULKTRANSFER) UsbBulkTransferFunction(); - protected: + private: ~UsbBulkTransferFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::BulkTransfer::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbBulkTransferFunction); }; -class UsbInterruptTransferFunction : public UsbAsyncApiTransferFunction { +class UsbInterruptTransferFunction : public UsbTransferFunction { public: DECLARE_EXTENSION_FUNCTION("usb.interruptTransfer", USB_INTERRUPTTRANSFER) UsbInterruptTransferFunction(); - protected: + private: ~UsbInterruptTransferFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::InterruptTransfer::Params> parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbInterruptTransferFunction); }; -class UsbIsochronousTransferFunction : public UsbAsyncApiTransferFunction { +class UsbIsochronousTransferFunction : public UsbTransferFunction { public: DECLARE_EXTENSION_FUNCTION("usb.isochronousTransfer", USB_ISOCHRONOUSTRANSFER) UsbIsochronousTransferFunction(); - protected: + private: ~UsbIsochronousTransferFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; - private: - scoped_ptr<extensions::core_api::usb::IsochronousTransfer::Params> - parameters_; + DISALLOW_COPY_AND_ASSIGN(UsbIsochronousTransferFunction); }; -class UsbResetDeviceFunction : public UsbAsyncApiFunction { +class UsbResetDeviceFunction : public UsbConnectionFunction { public: DECLARE_EXTENSION_FUNCTION("usb.resetDevice", USB_RESETDEVICE) UsbResetDeviceFunction(); - protected: + private: ~UsbResetDeviceFunction() override; - // AsyncApiFunction: - bool Prepare() override; - void AsyncWorkStart() override; + // ExtensionFunction: + ResponseAction Run() override; + + void OnComplete(bool success); - private: scoped_ptr<extensions::core_api::usb::ResetDevice::Params> parameters_; + + DISALLOW_COPY_AND_ASSIGN(UsbResetDeviceFunction); }; } // namespace extensions diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc index ad36408..d2be33c 100644 --- a/extensions/browser/api/usb/usb_apitest.cc +++ b/extensions/browser/api/usb/usb_apitest.cc @@ -25,12 +25,17 @@ using device::UsbDeviceHandle; using device::UsbEndpointDirection; using device::UsbInterfaceDescriptor; using device::UsbService; -using device::UsbTransferCallback; namespace extensions { namespace { +ACTION_TEMPLATE(InvokeCallback, + HAS_1_TEMPLATE_PARAMS(int, k), + AND_1_VALUE_PARAMS(p1)) { + ::std::tr1::get<k>(args).Run(p1); +} + ACTION_TEMPLATE(InvokeUsbTransferCallback, HAS_1_TEMPLATE_PARAMS(int, k), AND_1_VALUE_PARAMS(p1)) { @@ -84,44 +89,48 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { uint8 request, uint16 value, uint16 index, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback)); + const TransferCallback& callback)); MOCK_METHOD6(BulkTransfer, void(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback)); + const TransferCallback& callback)); MOCK_METHOD6(InterruptTransfer, void(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int timeout, - const UsbTransferCallback& callback)); + const TransferCallback& callback)); MOCK_METHOD8(IsochronousTransfer, void(UsbEndpointDirection direction, uint8 endpoint, - net::IOBuffer* buffer, + scoped_refptr<net::IOBuffer> buffer, size_t length, unsigned int packets, unsigned int packet_length, unsigned int timeout, - const UsbTransferCallback& callback)); + const TransferCallback& callback)); - MOCK_METHOD0(ResetDevice, bool()); + MOCK_METHOD1(ResetDevice, void(const ResultCallback& callback)); MOCK_METHOD2(GetStringDescriptor, bool(uint8_t, base::string16*)); - MOCK_METHOD1(SetConfiguration, bool(int)); - MOCK_METHOD1(ClaimInterface, bool(int interface_number)); + MOCK_METHOD2(SetConfiguration, + void(int configuration_value, const ResultCallback& callback)); + MOCK_METHOD2(ClaimInterface, + void(int interface_number, const ResultCallback& callback)); MOCK_METHOD1(ReleaseInterface, bool(int interface_number)); - MOCK_METHOD2(SetInterfaceAlternateSetting, - bool(int interface_number, int alternate_setting)); + MOCK_METHOD3(SetInterfaceAlternateSetting, + void(int interface_number, + int alternate_setting, + const ResultCallback& callback)); virtual scoped_refptr<UsbDevice> GetDevice() const override { return device_; @@ -138,14 +147,16 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { class MockUsbDevice : public UsbDevice { public: MockUsbDevice(uint16 vendor_id, uint16 product_id, uint32 unique_id) - : UsbDevice(vendor_id, product_id, unique_id) {} - - MOCK_METHOD0(Open, scoped_refptr<UsbDeviceHandle>()); + : UsbDevice(vendor_id, + product_id, + unique_id, + base::string16(), + base::string16(), + base::string16()) {} + + MOCK_METHOD1(Open, void(const OpenCallback&)); MOCK_METHOD1(Close, bool(scoped_refptr<UsbDeviceHandle>)); MOCK_METHOD0(GetConfiguration, const device::UsbConfigDescriptor*()); - MOCK_METHOD1(GetManufacturer, bool(base::string16*)); - MOCK_METHOD1(GetProduct, bool(base::string16*)); - MOCK_METHOD1(GetSerialNumber, bool(base::string16*)); private: virtual ~MockUsbDevice() {} @@ -171,9 +182,10 @@ class MockUsbService : public UsbService { return device_; } - void GetDevices(std::vector<scoped_refptr<UsbDevice>>* devices) override { - STLClearObject(devices); - devices->push_back(device_); + void GetDevices(const GetDevicesCallback& callback) override { + std::vector<scoped_refptr<UsbDevice>> devices; + devices.push_back(device_); + callback.Run(devices); } scoped_refptr<UsbDevice> device_; @@ -185,46 +197,17 @@ class UsbApiTest : public ShellApiTest { ShellApiTest::SetUpOnMainThread(); mock_device_ = new MockUsbDevice(0, 0, 0); - EXPECT_CALL(*mock_device_.get(), GetManufacturer(_)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_device_.get(), GetProduct(_)) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*mock_device_.get(), GetSerialNumber(_)) - .WillRepeatedly(Return(false)); - mock_device_handle_ = new MockUsbDeviceHandle(); mock_device_handle_->set_device(mock_device_.get()); - EXPECT_CALL(*mock_device_.get(), Open()) - .WillRepeatedly(Return(mock_device_handle_)); - - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply(BrowserThread::FILE, FROM_HERE, - base::Bind(&UsbApiTest::SetUpService, this), - run_loop.QuitClosure()); - run_loop.Run(); - } - - void SetUpService() { - mock_service_ = new MockUsbService(mock_device_); - UsbService::SetInstanceForTest(mock_service_); - } - - void AddTestDevices() { - scoped_refptr<MockUsbDevice> device(new MockUsbDevice(0x18D1, 0x58F0, 1)); - EXPECT_CALL(*device.get(), GetSerialNumber(_)) - .WillRepeatedly(Return(false)); - mock_service_->NotifyDeviceAdded(device); - - device = new MockUsbDevice(0x18D1, 0x58F1, 2); - EXPECT_CALL(*device.get(), GetSerialNumber(_)) - .WillRepeatedly(Return(false)); - mock_service_->NotifyDeviceAdded(device); + EXPECT_CALL(*mock_device_.get(), Open(_)) + .WillRepeatedly(InvokeCallback<0>(mock_device_handle_)); + mock_service_.reset(new MockUsbService(mock_device_)); } protected: scoped_refptr<MockUsbDeviceHandle> mock_device_handle_; scoped_refptr<MockUsbDevice> mock_device_; - MockUsbService* mock_service_; + scoped_ptr<MockUsbService> mock_service_; }; } // namespace @@ -236,9 +219,9 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, DeviceHandling) { 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(), ResetDevice(_)) + .WillOnce(InvokeCallback<0>(true)) + .WillOnce(InvokeCallback<0>(false)); EXPECT_CALL(*mock_device_handle_.get(), InterruptTransfer(device::USB_DIRECTION_OUTBOUND, 2, _, 1, _, _)) .WillOnce(InvokeUsbTransferCallback<5>(device::USB_TRANSFER_COMPLETED)); @@ -247,8 +230,8 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, ResetDevice) { IN_PROC_BROWSER_TEST_F(UsbApiTest, SetConfiguration) { UsbConfigDescriptor config_descriptor; - EXPECT_CALL(*mock_device_handle_.get(), SetConfiguration(1)) - .WillOnce(Return(true)); + EXPECT_CALL(*mock_device_handle_.get(), SetConfiguration(1, _)) + .WillOnce(InvokeCallback<1>(true)); EXPECT_CALL(*mock_device_handle_.get(), Close()).Times(1); EXPECT_CALL(*mock_device_.get(), GetConfiguration()) .WillOnce(Return(nullptr)) @@ -325,12 +308,11 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceAdded) { ASSERT_TRUE(LoadApp("api_test/usb/add_event")); ASSERT_TRUE(load_listener.WaitUntilSatisfied()); - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, FROM_HERE, - base::Bind(&UsbApiTest::AddTestDevices, base::Unretained(this)), - run_loop.QuitClosure()); - run_loop.Run(); + scoped_refptr<MockUsbDevice> device(new MockUsbDevice(0x18D1, 0x58F0, 1)); + mock_service_->NotifyDeviceAdded(device); + + device = new MockUsbDevice(0x18D1, 0x58F1, 2); + mock_service_->NotifyDeviceAdded(device); ASSERT_TRUE(result_listener.WaitUntilSatisfied()); } @@ -343,14 +325,7 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, OnDeviceRemoved) { ASSERT_TRUE(LoadApp("api_test/usb/remove_event")); ASSERT_TRUE(load_listener.WaitUntilSatisfied()); - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, FROM_HERE, - base::Bind(&MockUsbService::NotifyDeviceRemoved, - base::Unretained(mock_service_), mock_device_), - run_loop.QuitClosure()); - run_loop.Run(); - + mock_service_->NotifyDeviceRemoved(mock_device_); ASSERT_TRUE(result_listener.WaitUntilSatisfied()); } @@ -365,14 +340,7 @@ IN_PROC_BROWSER_TEST_F(UsbApiTest, GetUserSelectedDevices) { ASSERT_TRUE(LoadApp("api_test/usb/get_user_selected_devices")); ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); - base::RunLoop run_loop; - BrowserThread::PostTaskAndReply( - BrowserThread::FILE, FROM_HERE, - base::Bind(&MockUsbService::NotifyDeviceRemoved, - base::Unretained(mock_service_), mock_device_), - run_loop.QuitClosure()); - run_loop.Run(); - + mock_service_->NotifyDeviceRemoved(mock_device_); ASSERT_TRUE(result_listener.WaitUntilSatisfied()); } diff --git a/extensions/browser/api/usb/usb_device_resource.h b/extensions/browser/api/usb/usb_device_resource.h index a0de7f3..d44675f 100644 --- a/extensions/browser/api/usb/usb_device_resource.h +++ b/extensions/browser/api/usb/usb_device_resource.h @@ -25,7 +25,7 @@ namespace extensions { class UsbDeviceResource : public ApiResource { public: static const content::BrowserThread::ID kThreadId = - content::BrowserThread::FILE; + content::BrowserThread::UI; UsbDeviceResource(const std::string& owner_extension_id, scoped_refptr<device::UsbDeviceHandle> device); diff --git a/extensions/browser/api/usb/usb_event_router.cc b/extensions/browser/api/usb/usb_event_router.cc index 93fff3f..41ae777 100644 --- a/extensions/browser/api/usb/usb_event_router.cc +++ b/extensions/browser/api/usb/usb_event_router.cc @@ -6,7 +6,6 @@ #include "device/core/device_client.h" #include "device/usb/usb_device.h" -#include "device/usb/usb_service.h" #include "extensions/browser/api/device_permissions_manager.h" #include "extensions/common/api/usb.h" #include "extensions/common/permissions/permissions_data.h" @@ -25,7 +24,6 @@ namespace { // Returns true iff the given extension has permission to receive events // regarding this device. bool WillDispatchDeviceEvent(scoped_refptr<UsbDevice> device, - const base::string16& serial_number, content::BrowserContext* browser_context, const Extension* extension, base::ListValue* event_args) { @@ -39,10 +37,10 @@ bool WillDispatchDeviceEvent(scoped_refptr<UsbDevice> device, } // Check permissions granted through chrome.usb.getUserSelectedDevices. - scoped_ptr<DevicePermissions> device_permissions = + DevicePermissions* device_permissions = DevicePermissionsManager::Get(browser_context) ->GetForExtension(extension->id()); - if (device_permissions->FindEntry(device, serial_number).get()) { + if (device_permissions->FindEntry(device).get()) { return true; } @@ -54,50 +52,6 @@ base::LazyInstance<BrowserContextKeyedAPIFactory<UsbEventRouter>>::Leaky } // namespace -class UsbEventRouter::FileThreadHelper : public UsbService::Observer { - public: - FileThreadHelper(base::WeakPtr<UsbEventRouter> usb_event_router) - : usb_event_router_(usb_event_router), observer_(this) {} - virtual ~FileThreadHelper() {} - - void Start() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - UsbService* service = device::DeviceClient::Get()->GetUsbService(); - if (service) { - observer_.Add(service); - } - } - - private: - // UsbService::Observer implementation. - void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - - base::string16 serial_number; - device->GetSerialNumber(&serial_number); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&UsbEventRouter::DispatchEvent, usb_event_router_, - usb::OnDeviceAdded::kEventName, device, serial_number)); - } - - void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); - - base::string16 serial_number; - device->GetSerialNumber(&serial_number); - - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - base::Bind(&UsbEventRouter::DispatchEvent, usb_event_router_, - usb::OnDeviceRemoved::kEventName, device, serial_number)); - } - - base::WeakPtr<UsbEventRouter> usb_event_router_; - ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; -}; - // static BrowserContextKeyedAPIFactory<UsbEventRouter>* UsbEventRouter::GetFactoryInstance() { @@ -105,7 +59,7 @@ UsbEventRouter::GetFactoryInstance() { } UsbEventRouter::UsbEventRouter(content::BrowserContext* browser_context) - : browser_context_(browser_context), weak_factory_(this) { + : browser_context_(browser_context), observer_(this) { EventRouter* event_router = EventRouter::Get(browser_context_); if (event_router) { event_router->RegisterObserver(this, usb::OnDeviceAdded::kEventName); @@ -121,21 +75,26 @@ void UsbEventRouter::Shutdown() { if (event_router) { event_router->UnregisterObserver(this); } - helper_.reset(nullptr); } void UsbEventRouter::OnListenerAdded(const EventListenerInfo& details) { - if (!helper_) { - helper_.reset(new FileThreadHelper(weak_factory_.GetWeakPtr())); - BrowserThread::PostTask( - BrowserThread::FILE, FROM_HERE, - base::Bind(&FileThreadHelper::Start, base::Unretained(helper_.get()))); + UsbService* service = device::DeviceClient::Get()->GetUsbService(); + if (!observer_.IsObserving(service)) { + observer_.Add(service); } } +void UsbEventRouter::OnDeviceAdded(scoped_refptr<device::UsbDevice> device) { + DispatchEvent(usb::OnDeviceAdded::kEventName, device); +} + +void UsbEventRouter::OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) { + DispatchEvent(usb::OnDeviceRemoved::kEventName, device); +} + void UsbEventRouter::DispatchEvent(const std::string& event_name, - scoped_refptr<UsbDevice> device, - const base::string16& serial_number) { + scoped_refptr<UsbDevice> device) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); EventRouter* event_router = EventRouter::Get(browser_context_); if (event_router) { usb::Device device_obj; @@ -154,7 +113,7 @@ void UsbEventRouter::DispatchEvent(const std::string& event_name, } event->will_dispatch_callback = - base::Bind(&WillDispatchDeviceEvent, device, serial_number); + base::Bind(&WillDispatchDeviceEvent, device); event_router->BroadcastEvent(event.Pass()); } } diff --git a/extensions/browser/api/usb/usb_event_router.h b/extensions/browser/api/usb/usb_event_router.h index 5ff750e..6f081a2 100644 --- a/extensions/browser/api/usb/usb_event_router.h +++ b/extensions/browser/api/usb/usb_event_router.h @@ -6,6 +6,7 @@ #define EXTENSIONS_BROWSER_API_USB_USB_EVENT_ROUTER_H_ #include "content/public/browser/browser_thread.h" +#include "device/usb/usb_service.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/event_router.h" @@ -18,7 +19,8 @@ namespace extensions { // A BrowserContext-scoped object which is registered as an observer of the // EventRouter and UsbService in order to generate device add/remove events. class UsbEventRouter : public BrowserContextKeyedAPI, - public EventRouter::Observer { + public EventRouter::Observer, + public device::UsbService::Observer { public: // BrowserContextKeyedAPI implementation. static BrowserContextKeyedAPIFactory<UsbEventRouter>* GetFactoryInstance(); @@ -26,8 +28,6 @@ class UsbEventRouter : public BrowserContextKeyedAPI, private: friend class BrowserContextKeyedAPIFactory<UsbEventRouter>; - class FileThreadHelper; - explicit UsbEventRouter(content::BrowserContext* context); ~UsbEventRouter() override; @@ -41,16 +41,17 @@ class UsbEventRouter : public BrowserContextKeyedAPI, // EventRouter::Observer implementation. void OnListenerAdded(const EventListenerInfo& details) override; + // UsbService::Observer implementation. + void OnDeviceAdded(scoped_refptr<device::UsbDevice> device) override; + void OnDeviceRemoved(scoped_refptr<device::UsbDevice> device) override; + // Broadcasts a device add or remove event for the given device. void DispatchEvent(const std::string& event_name, - scoped_refptr<device::UsbDevice> device, - const base::string16& serial_number); + scoped_refptr<device::UsbDevice> device); content::BrowserContext* const browser_context_; - scoped_ptr<FileThreadHelper, content::BrowserThread::DeleteOnFileThread> - helper_; - base::WeakPtrFactory<UsbEventRouter> weak_factory_; + ScopedObserver<device::UsbService, device::UsbService::Observer> observer_; DISALLOW_COPY_AND_ASSIGN(UsbEventRouter); }; diff --git a/extensions/shell/browser/shell_device_client.cc b/extensions/shell/browser/shell_device_client.cc index 90e974b..5dd1c6b 100644 --- a/extensions/shell/browser/shell_device_client.cc +++ b/extensions/shell/browser/shell_device_client.cc @@ -9,6 +9,8 @@ #include "device/hid/hid_service.h" #include "device/usb/usb_service.h" +using content::BrowserThread; + namespace extensions { ShellDeviceClient::ShellDeviceClient() {} @@ -16,15 +18,15 @@ ShellDeviceClient::ShellDeviceClient() {} ShellDeviceClient::~ShellDeviceClient() {} device::UsbService* ShellDeviceClient::GetUsbService() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); return device::UsbService::GetInstance( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::UI)); + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); } device::HidService* ShellDeviceClient::GetHidService() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); return device::HidService::GetInstance( - content::BrowserThread::GetMessageLoopProxyForThread( - content::BrowserThread::FILE)); + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)); } } diff --git a/extensions/test/data/api_test/usb/reset_device/test.js b/extensions/test/data/api_test/usb/reset_device/test.js index 2a39ada..e709fff 100644 --- a/extensions/test/data/api_test/usb/reset_device/test.js +++ b/extensions/test/data/api_test/usb/reset_device/test.js @@ -19,10 +19,10 @@ function resetDevice() { usb.resetDevice(devices[0], function(result) { chrome.test.assertLastError( 'Error resetting the device. The device has been closed.'); - chrome.test.assertEq(result, false); + chrome.test.assertEq(false, result); usb.interruptTransfer(devices[0], transfer, function(result) { - chrome.test.assertEq(result, undefined); - chrome.test.assertLastError('No such device.'); + chrome.test.assertEq(undefined, result); + chrome.test.assertLastError('No such connection.'); chrome.test.succeed(); }); }); |