summaryrefslogtreecommitdiffstats
path: root/chrome/browser/usb
diff options
context:
space:
mode:
authorikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-02 00:56:15 +0000
committerikarienator@chromium.org <ikarienator@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-02 00:56:15 +0000
commitb6eb1ac86ad7b25035984d6ab9f78ba6898835be (patch)
tree5bea453844c7d6c386c83746e38a10b97bf89def /chrome/browser/usb
parente6c97f6de21efd3e693a1afda435cc3b9a9eb2c7 (diff)
downloadchromium_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.cc73
-rw-r--r--chrome/browser/usb/usb_context.h46
-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.cc14
-rw-r--r--chrome/browser/usb/usb_device_handle.h2
-rw-r--r--chrome/browser/usb/usb_service.cc162
-rw-r--r--chrome/browser/usb/usb_service.h55
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