diff options
author | ikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 00:56:15 +0000 |
---|---|---|
committer | ikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-02 00:56:15 +0000 |
commit | b6eb1ac86ad7b25035984d6ab9f78ba6898835be (patch) | |
tree | 5bea453844c7d6c386c83746e38a10b97bf89def /chrome/browser/usb | |
parent | e6c97f6de21efd3e693a1afda435cc3b9a9eb2c7 (diff) | |
download | chromium_src-b6eb1ac86ad7b25035984d6ab9f78ba6898835be.zip chromium_src-b6eb1ac86ad7b25035984d6ab9f78ba6898835be.tar.gz chromium_src-b6eb1ac86ad7b25035984d6ab9f78ba6898835be.tar.bz2 |
Create a refcounted usb context wrapper.
This will be also used when we introduce UsbDevice.
BUG=223817
Review URL: https://chromiumcodereview.appspot.com/20012002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@215174 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/usb')
-rw-r--r-- | chrome/browser/usb/usb_context.cc | 73 | ||||
-rw-r--r-- | chrome/browser/usb/usb_context.h | 46 | ||||
-rw-r--r-- | chrome/browser/usb/usb_context_unittest.cc (renamed from chrome/browser/usb/usb_service_unittest.cc) | 17 | ||||
-rw-r--r-- | chrome/browser/usb/usb_device_handle.cc | 14 | ||||
-rw-r--r-- | chrome/browser/usb/usb_device_handle.h | 2 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service.cc | 162 | ||||
-rw-r--r-- | chrome/browser/usb/usb_service.h | 55 |
7 files changed, 264 insertions, 105 deletions
diff --git a/chrome/browser/usb/usb_context.cc b/chrome/browser/usb/usb_context.cc new file mode 100644 index 0000000..3b0811a --- /dev/null +++ b/chrome/browser/usb/usb_context.cc @@ -0,0 +1,73 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/usb/usb_context.h" + +#include "base/logging.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/platform_thread.h" +#include "content/public/browser/browser_thread.h" +#include "third_party/libusb/src/libusb/interrupt.h" +#include "third_party/libusb/src/libusb/libusb.h" + +// The UsbEventHandler works around a design flaw in the libusb interface. There +// is currently no way to signal to libusb that any caller into one of the event +// handler calls should return without handling any events. +class UsbContext::UsbEventHandler : public base::PlatformThread::Delegate { + public: + explicit UsbEventHandler(libusb_context* context); + virtual ~UsbEventHandler(); + + // base::PlatformThread::Delegate + virtual void ThreadMain() OVERRIDE; + + private: + volatile bool running_; + libusb_context* context_; + base::PlatformThreadHandle thread_handle_; + base::WaitableEvent start_polling_; + DISALLOW_COPY_AND_ASSIGN(UsbEventHandler); +}; + +UsbContext::UsbEventHandler::UsbEventHandler(libusb_context* context) + : running_(true), + context_(context), + thread_handle_(0), + start_polling_(false, false) { + DCHECK(base::PlatformThread::Create(0, this, &thread_handle_)) << + "Failed to create USB IO handling thread."; + start_polling_.Wait(); +} + +UsbContext::UsbEventHandler::~UsbEventHandler() { + running_ = false; + // Spreading running_ to the UsbEventHandler thread. + base::subtle::MemoryBarrier(); + libusb_interrupt_handle_event(context_); + base::PlatformThread::Join(thread_handle_); +} + +void UsbContext::UsbEventHandler::ThreadMain() { + base::PlatformThread::SetName("UsbEventHandler"); + VLOG(1) << "UsbEventHandler started."; + if (running_) { + start_polling_.Signal(); + libusb_handle_events(context_); + } + while (running_) + libusb_handle_events(context_); + VLOG(1) << "UsbEventHandler shutting down."; +} + +UsbContext::UsbContext() : context_(NULL) { + CHECK_EQ(0, libusb_init(&context_)) << "Cannot initialize libusb"; + event_handler_.reset(new UsbEventHandler(context_)); +} + +UsbContext::~UsbContext() { + // destruction of UsbEventHandler is a blocking operation. + CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE)); + event_handler_.reset(NULL); + libusb_exit(context_); +} diff --git a/chrome/browser/usb/usb_context.h b/chrome/browser/usb/usb_context.h new file mode 100644 index 0000000..07894c5 --- /dev/null +++ b/chrome/browser/usb/usb_context.h @@ -0,0 +1,46 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_USB_USB_CONTEXT_H_ +#define CHROME_BROWSER_USB_USB_CONTEXT_H_ + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_thread.h" + +struct libusb_context; + +typedef libusb_context* PlatformUsbContext; + +// Ref-counted wrapper for libusb_context*. +// It also manages the life-cycle of UsbEventHandler. +// It is a blocking operation to delete UsbContext. +// Destructor must be called on FILE thread. +class UsbContext + : public base::RefCountedThreadSafe< + UsbContext, content::BrowserThread::DeleteOnFileThread> { + public: + PlatformUsbContext context() const { return context_; } + + protected: + friend class UsbService; + friend struct content::BrowserThread::DeleteOnThread< + content::BrowserThread::FILE>; + friend class base::DeleteHelper<UsbContext>; + + // Set wait_for_polling_starts to true if the constructor should wait until + // the polling starts. It will block the current thread. Only use it in tests. + explicit UsbContext(); + virtual ~UsbContext(); + + private: + class UsbEventHandler; + PlatformUsbContext context_; + scoped_ptr<UsbEventHandler> event_handler_; + + DISALLOW_COPY_AND_ASSIGN(UsbContext); +}; + +#endif // CHROME_BROWSER_USB_USB_CONTEXT_H_ diff --git a/chrome/browser/usb/usb_service_unittest.cc b/chrome/browser/usb/usb_context_unittest.cc index 44864d8..4944c1c 100644 --- a/chrome/browser/usb/usb_service_unittest.cc +++ b/chrome/browser/usb/usb_context_unittest.cc @@ -2,16 +2,23 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/usb/usb_service.h" +#include "chrome/browser/usb/usb_context.h" +#include "base/threading/platform_thread.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" namespace { -class UsbServiceTest : public testing::Test { +class UsbContextTest : public testing::Test { protected: - class UsbServiceForTest : public UsbService {}; + class UsbContextForTest : public UsbContext { + public: + UsbContextForTest() : UsbContext() {} + private: + virtual ~UsbContextForTest() {} + DISALLOW_COPY_AND_ASSIGN(UsbContextForTest); + }; }; #if defined(OS_LINUX) @@ -24,10 +31,10 @@ class UsbServiceTest : public testing::Test { #define MAYBE_GracefulShutdown GracefulShutdown #endif -TEST_F(UsbServiceTest, MAYBE_GracefulShutdown) { +TEST_F(UsbContextTest, MAYBE_GracefulShutdown) { base::TimeTicks start = base::TimeTicks::Now(); { - scoped_ptr<UsbServiceForTest> service(new UsbServiceForTest()); + scoped_refptr<UsbContextForTest> context(new UsbContextForTest()); } base::TimeDelta elapse = base::TimeTicks::Now() - start; if (elapse > base::TimeDelta::FromSeconds(2)) { diff --git a/chrome/browser/usb/usb_device_handle.cc b/chrome/browser/usb/usb_device_handle.cc index b6e54f4..56c3dc7 100644 --- a/chrome/browser/usb/usb_device_handle.cc +++ b/chrome/browser/usb/usb_device_handle.cc @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -127,7 +127,7 @@ void UsbDeviceHandle::TransferComplete(PlatformUsbTransferHandle handle) { DCHECK(ContainsKey(transfers_, handle)) << "Missing transfer completed"; Transfer* const transfer = &transfers_[handle]; - DCHECK(handle->actual_length >= 0) << "Negative actual length received"; + DCHECK_GE(handle->actual_length, 0) << "Negative actual length received"; size_t actual_length = static_cast<size_t>(std::max(handle->actual_length, 0)); @@ -272,9 +272,15 @@ bool UsbDeviceHandle::GetSerial(base::string16* serial) { size = libusb_get_string_descriptor( handle_, desc.iSerialNumber, langid[i], reinterpret_cast<unsigned char*>(&text[0]), sizeof(text)); - if (size < 0) + if (size <= 2) continue; - *serial = base::string16(text, size / 2); + if ((text[0] >> 8) != LIBUSB_DT_STRING) + continue; + if ((text[0] & 255) > size) + continue; + + size = size / 2 - 1; + *serial = base::string16(text + 1, size); return true; } return false; diff --git a/chrome/browser/usb/usb_device_handle.h b/chrome/browser/usb/usb_device_handle.h index ef059d7..0659955 100644 --- a/chrome/browser/usb/usb_device_handle.h +++ b/chrome/browser/usb/usb_device_handle.h @@ -1,4 +1,4 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. +// Copyright (c) 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. diff --git a/chrome/browser/usb/usb_service.cc b/chrome/browser/usb/usb_service.cc index 9cba20cc..d850488 100644 --- a/chrome/browser/usb/usb_service.cc +++ b/chrome/browser/usb/usb_service.cc @@ -10,9 +10,13 @@ #include "base/bind_helpers.h" #include "base/logging.h" #include "base/stl_util.h" -#include "base/synchronization/waitable_event.h" +#include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/usb/usb_context.h" #include "chrome/browser/usb/usb_device_handle.h" -#include "third_party/libusb/src/libusb/interrupt.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" #include "third_party/libusb/src/libusb/libusb.h" #if defined(OS_CHROMEOS) @@ -21,70 +25,64 @@ #include "chromeos/dbus/permission_broker_client.h" #endif // defined(OS_CHROMEOS) -using std::vector; +namespace content { -// The UsbEventHandler works around a design flaw in the libusb interface. There -// is currently no way to signal to libusb that any caller into one of the event -// handler calls should return without handling any events. -class UsbEventHandler : public base::PlatformThread::Delegate { - public: - explicit UsbEventHandler(PlatformUsbContext context) - : running_(true), - context_(context), - thread_handle_(0), - started_event_(false, false) { - base::PlatformThread::Create(0, this, &thread_handle_); - started_event_.Wait(); - } +class NotificationDetails; +class NotificationSource; - virtual ~UsbEventHandler() {} +} // namespace content - virtual void ThreadMain() OVERRIDE { - base::PlatformThread::SetName("UsbEventHandler"); - VLOG(1) << "UsbEventHandler started."; - started_event_.Signal(); - while (running_) - libusb_handle_events(context_); - VLOG(1) << "UsbEventHandler shutting down."; - } +using content::BrowserThread; +using std::vector; + +namespace { - void Stop() { - running_ = false; - base::subtle::MemoryBarrier(); - libusb_interrupt_handle_event(context_); - base::PlatformThread::Join(thread_handle_); +class ExitObserver : public content::NotificationObserver { + public: + explicit ExitObserver(UsbService* service) : service_(service) { + registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING, + content::NotificationService::AllSources()); } private: - volatile bool running_; - PlatformUsbContext context_; - base::PlatformThreadHandle thread_handle_; - base::WaitableEvent started_event_; - DISALLOW_COPY_AND_ASSIGN(UsbEventHandler); + // content::NotificationObserver + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE { + if (type == chrome::NOTIFICATION_APP_TERMINATING) { + registrar_.RemoveAll(); + BrowserThread::DeleteSoon(BrowserThread::FILE, FROM_HERE, service_); + } + } + UsbService* service_; + content::NotificationRegistrar registrar_; }; -UsbService::UsbService() { - libusb_init(&context_); - event_handler_ = new UsbEventHandler(context_); +} // namespace + +UsbService::UsbService() : context_(new UsbContext()) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); } UsbService::~UsbService() { - event_handler_->Stop(); - delete event_handler_; - libusb_exit(context_); - context_ = NULL; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // UsbDeviceHandle::Close removes itself from devices_. + while (devices_.size()) + devices_.begin()->second->Close(); } UsbService* UsbService::GetInstance() { - return Singleton<UsbService>::get(); + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // UsbService deletes itself upon APP_TERMINATING. + return Singleton<UsbService, LeakySingletonTraits<UsbService> >::get(); } -void UsbService::FindDevices(const uint16 vendor_id, - const uint16 product_id, - int interface_id, - vector<scoped_refptr<UsbDeviceHandle> >* devices, - const base::Callback<void()>& callback) { - DCHECK(event_handler_) << "FindDevices called after event handler stopped."; +void UsbService::FindDevices( + const uint16 vendor_id, + const uint16 product_id, + int interface_id, + const base::Callback<void(ScopedDeviceVector vector)>& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); #if defined(OS_CHROMEOS) // ChromeOS builds on non-ChromeOS machines (dev) should not attempt to // use permission broker. @@ -93,29 +91,33 @@ void UsbService::FindDevices(const uint16 vendor_id, chromeos::DBusThreadManager::Get()->GetPermissionBrokerClient(); DCHECK(client) << "Could not get permission broker client."; if (!client) { - callback.Run(); + callback.Run(ScopedDeviceVector()); return; } - client->RequestUsbAccess(vendor_id, - product_id, - interface_id, - base::Bind(&UsbService::FindDevicesImpl, - base::Unretained(this), - vendor_id, - product_id, - devices, - callback)); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&chromeos::PermissionBrokerClient::RequestUsbAccess, + base::Unretained(client), + vendor_id, + product_id, + interface_id, + base::Bind(&UsbService::OnRequestUsbAccessReplied, + base::Unretained(this), + vendor_id, + product_id, + callback))); } else { - FindDevicesImpl(vendor_id, product_id, devices, callback, true); + FindDevicesImpl(vendor_id, product_id, callback, true); } #else - FindDevicesImpl(vendor_id, product_id, devices, callback, true); + FindDevicesImpl(vendor_id, product_id, callback, true); #endif // defined(OS_CHROMEOS) } void UsbService::EnumerateDevices( - std::vector<scoped_refptr<UsbDeviceHandle> >* devices) { + vector<scoped_refptr<UsbDeviceHandle> >* devices) { devices->clear(); DeviceVector enumerated_devices; @@ -130,21 +132,36 @@ void UsbService::EnumerateDevices( } } -void UsbService::FindDevicesImpl( +void UsbService::OnRequestUsbAccessReplied( const uint16 vendor_id, const uint16 product_id, - vector<scoped_refptr<UsbDeviceHandle> >* devices, - const base::Callback<void()>& callback, + const base::Callback<void(ScopedDeviceVector vectors)>& callback, bool success) { - base::ScopedClosureRunner run_callback(callback); + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&UsbService::FindDevicesImpl, + base::Unretained(this), + vendor_id, + product_id, + callback, + success)); +} - devices->clear(); +void UsbService::FindDevicesImpl( + const uint16 vendor_id, + const uint16 product_id, + const base::Callback<void(ScopedDeviceVector vectors)>& callback, + bool success) { + ScopedDeviceVector devices(new vector<scoped_refptr<UsbDeviceHandle> >()); // If the permission broker was unable to obtain permission for the specified // devices then there is no point in attempting to enumerate the devices. On // platforms without a permission broker, we assume permission is granted. - if (!success) + if (!success) { + callback.Run(devices.Pass()); return; + } DeviceVector enumerated_devices; EnumerateDevicesImpl(&enumerated_devices); @@ -155,14 +172,13 @@ void UsbService::FindDevicesImpl( if (DeviceMatches(device, vendor_id, product_id)) { UsbDeviceHandle* const wrapper = LookupOrCreateDevice(device); if (wrapper) - devices->push_back(wrapper); + devices->push_back(make_scoped_refptr(wrapper)); } } + callback.Run(devices.Pass()); } void UsbService::CloseDevice(scoped_refptr<UsbDeviceHandle> device) { - DCHECK(event_handler_) << "CloseDevice called after event handler stopped."; - PlatformUsbDevice platform_device = libusb_get_device(device->handle()); if (!ContainsKey(devices_, platform_device)) { LOG(WARNING) << "CloseDevice called for device we're not tracking!"; @@ -195,7 +211,9 @@ void UsbService::EnumerateDevicesImpl(DeviceVector* output) { STLClearObject(output); libusb_device** devices = NULL; - const ssize_t device_count = libusb_get_device_list(context_, &devices); + const ssize_t device_count = libusb_get_device_list( + context_->context(), + &devices); if (device_count < 0) return; diff --git a/chrome/browser/usb/usb_service.h b/chrome/browser/usb/usb_service.h index 5588364..dcd6e28 100644 --- a/chrome/browser/usb/usb_service.h +++ b/chrome/browser/usb/usb_service.h @@ -11,15 +11,16 @@ #include "base/basictypes.h" #include "base/memory/singleton.h" -#include "base/threading/platform_thread.h" #include "chrome/browser/usb/usb_device_handle.h" -#include "third_party/libusb/src/libusb/libusb.h" -class UsbEventHandler; -template <typename T> struct DefaultSingletonTraits; -struct libusb_context; +namespace base { + +template <class T> class DeleteHelper; -typedef struct libusb_context* PlatformUsbContext; +} // namespace base + +template <typename T> struct DefaultSingletonTraits; +class UsbContext; // The USB service handles creating and managing an event handler thread that is // used to manage and dispatch USB events. It is also responsbile for device @@ -27,16 +28,19 @@ typedef struct libusb_context* PlatformUsbContext; // competition for the same USB device. class UsbService { public: + typedef scoped_ptr<std::vector<scoped_refptr<UsbDeviceHandle> > > + ScopedDeviceVector; + // Must be called on FILE thread. static UsbService* GetInstance(); // Find all of the devices attached to the system that are identified by // |vendor_id| and |product_id|, inserting them into |devices|. Clears // |devices| before use. Calls |callback| once |devices| is populated. - void FindDevices(const uint16 vendor_id, - const uint16 product_id, - int interface_id, - std::vector<scoped_refptr<UsbDeviceHandle> >* devices, - const base::Callback<void()>& callback); + void FindDevices( + const uint16 vendor_id, + const uint16 product_id, + int interface_id, + const base::Callback<void(ScopedDeviceVector vector)>& callback); // Find all of the devices attached to the system, inserting them into // |devices|. Clears |devices| before use. @@ -46,13 +50,11 @@ class UsbService { // UsbDevice's Close function and disposes of the associated platform handle. void CloseDevice(scoped_refptr<UsbDeviceHandle> device); - protected: + private: UsbService(); virtual ~UsbService(); - - private: friend struct DefaultSingletonTraits<UsbService>; - + friend class base::DeleteHelper<UsbService>; // RefCountedPlatformUsbDevice takes care of managing the underlying reference // count of a single PlatformUsbDevice. This allows us to construct things @@ -77,15 +79,23 @@ class UsbService { const uint16 vendor_id, const uint16 product_id); + // This method is called when permission broker replied our request. + // We will simply relay it to FILE thread. + void OnRequestUsbAccessReplied( + const uint16 vendor_id, + const uint16 product_id, + const base::Callback<void(ScopedDeviceVector vector)>& callback, + bool success); + // FindDevicesImpl is called by FindDevices on ChromeOS after the permission - // broker has signalled that permission has been granted to access the + // broker has signaled that permission has been granted to access the // underlying device nodes. On other platforms, it is called directly by // FindDevices. - void FindDevicesImpl(const uint16 vendor_id, - const uint16 product_id, - std::vector<scoped_refptr<UsbDeviceHandle> >* devices, - const base::Callback<void()>& callback, - bool success); + void FindDevicesImpl( + const uint16 vendor_id, + const uint16 product_id, + const base::Callback<void(ScopedDeviceVector vector)>& callback, + bool success); // Populates |output| with the result of enumerating all attached USB devices. void EnumerateDevicesImpl(DeviceVector* output); @@ -95,8 +105,7 @@ class UsbService { // the wrapper with the device internally. UsbDeviceHandle* LookupOrCreateDevice(PlatformUsbDevice device); - PlatformUsbContext context_; - UsbEventHandler* event_handler_; + scoped_refptr<UsbContext> context_; // The devices_ map contains scoped_refptrs to all open devices, indicated by // their vendor and product id. This allows for reusing an open device without |