| Index: device/test/usb_test_gadget_impl.cc
|
| diff --git a/device/test/usb_test_gadget_impl.cc b/device/test/usb_test_gadget_impl.cc
|
| index d9371b4767b56b744c9d33f902aa44e8f10ea4b7..2eea05808acc1bc825d332df275b47e286469707 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;
|
| }
|
|
|
|
|