summaryrefslogtreecommitdiffstats
path: root/device
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2015-04-16 12:11:04 -0700
committerCommit bot <commit-bot@chromium.org>2015-04-16 19:12:05 +0000
commitb87cb277526092e101ce29f2ad0393f6247cc39e (patch)
treeffd0c2b8e4bb0519070cf000a872f78d83ae9791 /device
parente2ff83b4c95cbd9c0df8fd338c9afa39b861cb14 (diff)
downloadchromium_src-b87cb277526092e101ce29f2ad0393f6247cc39e.zip
chromium_src-b87cb277526092e101ce29f2ad0393f6247cc39e.tar.gz
chromium_src-b87cb277526092e101ce29f2ad0393f6247cc39e.tar.bz2
Move device/usb classes from the FILE thread to UI thread.
Code that interacts with device/usb often lives on the UI thread. As with the recent migration of device/hid (issue 422540) moving ownership of these classes to the UI thread makes calling code substancially simplier. Blocking operations are handled internally by posting tasks to the FILE thread and returning a result to the UI thread asynchronously. This change paves the way for replacing libusb with platform-specific implementations of these classes that may have different thread usage requirements (as is the case in the device/hid code). BUG=427985 Review URL: https://codereview.chromium.org/980023002 Cr-Commit-Position: refs/heads/master@{#325491}
Diffstat (limited to 'device')
-rw-r--r--device/hid/hid_connection_unittest.cc37
-rw-r--r--device/hid/hid_service.cc10
-rw-r--r--device/test/usb_test_gadget.h9
-rw-r--r--device/test/usb_test_gadget_impl.cc760
-rw-r--r--device/usb/usb_context.cc28
-rw-r--r--device/usb/usb_device.cc14
-rw-r--r--device/usb/usb_device.h45
-rw-r--r--device/usb/usb_device_filter_unittest.cc28
-rw-r--r--device/usb/usb_device_handle.h47
-rw-r--r--device/usb/usb_device_handle_impl.cc758
-rw-r--r--device/usb/usb_device_handle_impl.h137
-rw-r--r--device/usb/usb_device_handle_unittest.cc157
-rw-r--r--device/usb/usb_device_impl.cc205
-rw-r--r--device/usb/usb_device_impl.h45
-rw-r--r--device/usb/usb_service.cc46
-rw-r--r--device/usb/usb_service.h25
-rw-r--r--device/usb/usb_service_impl.cc614
-rw-r--r--device/usb/usb_service_impl.h93
-rw-r--r--device/usb/usb_service_unittest.cc27
19 files changed, 1760 insertions, 1325 deletions
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, &reg_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, &reg_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());