| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/test/usb_test_gadget.h" | 5 #include "device/test/usb_test_gadget.h" |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 #include <vector> | 8 #include <vector> |
| 9 | 9 |
| 10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/compiler_specific.h" | 11 #include "base/compiler_specific.h" |
| 12 #include "base/files/file.h" | 12 #include "base/files/file.h" |
| 13 #include "base/files/file_path.h" | 13 #include "base/files/file_path.h" |
| 14 #include "base/logging.h" | 14 #include "base/logging.h" |
| 15 #include "base/macros.h" | 15 #include "base/macros.h" |
| 16 #include "base/memory/ref_counted.h" | 16 #include "base/memory/ref_counted.h" |
| 17 #include "base/memory/scoped_ptr.h" | 17 #include "base/memory/scoped_ptr.h" |
| 18 #include "base/path_service.h" | 18 #include "base/path_service.h" |
| 19 #include "base/process/process_handle.h" | 19 #include "base/process/process_handle.h" |
| 20 #include "base/run_loop.h" | 20 #include "base/run_loop.h" |
| 21 #include "base/strings/string_number_conversions.h" | 21 #include "base/scoped_observer.h" |
| 22 #include "base/strings/stringprintf.h" | 22 #include "base/strings/stringprintf.h" |
| 23 #include "base/strings/utf_string_conversions.h" | 23 #include "base/strings/utf_string_conversions.h" |
| 24 #include "base/thread_task_runner_handle.h" |
| 24 #include "base/time/time.h" | 25 #include "base/time/time.h" |
| 25 #include "device/usb/usb_device.h" | 26 #include "device/usb/usb_device.h" |
| 26 #include "device/usb/usb_device_handle.h" | 27 #include "device/usb/usb_device_handle.h" |
| 27 #include "device/usb/usb_service.h" | 28 #include "device/usb/usb_service.h" |
| 28 #include "net/proxy/proxy_service.h" | 29 #include "net/proxy/proxy_service.h" |
| 29 #include "net/url_request/url_fetcher.h" | 30 #include "net/url_request/url_fetcher.h" |
| 30 #include "net/url_request/url_fetcher_delegate.h" | 31 #include "net/url_request/url_fetcher_delegate.h" |
| 31 #include "net/url_request/url_request_context.h" | 32 #include "net/url_request/url_request_context.h" |
| 32 #include "net/url_request/url_request_context_builder.h" | 33 #include "net/url_request/url_request_context_builder.h" |
| 33 #include "net/url_request/url_request_context_getter.h" | 34 #include "net/url_request/url_request_context_getter.h" |
| 34 #include "url/gurl.h" | 35 #include "url/gurl.h" |
| 35 | 36 |
| 36 using ::base::PlatformThread; | 37 namespace device { |
| 37 using ::base::TimeDelta; | |
| 38 | 38 |
| 39 namespace device { | 39 class UsbTestGadgetImpl : public UsbTestGadget { |
| 40 public: |
| 41 UsbTestGadgetImpl( |
| 42 scoped_refptr<net::URLRequestContextGetter> request_context_getter, |
| 43 UsbService* usb_service, |
| 44 scoped_refptr<UsbDevice> device); |
| 45 ~UsbTestGadgetImpl() override; |
| 46 |
| 47 bool Unclaim() override; |
| 48 bool Disconnect() override; |
| 49 bool Reconnect() override; |
| 50 bool SetType(Type type) override; |
| 51 UsbDevice* GetDevice() const override; |
| 52 |
| 53 private: |
| 54 std::string device_address_; |
| 55 scoped_refptr<UsbDevice> device_; |
| 56 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| 57 UsbService* usb_service_; |
| 58 |
| 59 DISALLOW_COPY_AND_ASSIGN(UsbTestGadgetImpl); |
| 60 }; |
| 40 | 61 |
| 41 namespace { | 62 namespace { |
| 42 | 63 |
| 43 static const char kCommandLineSwitch[] = "enable-gadget-tests"; | 64 static const char kCommandLineSwitch[] = "enable-gadget-tests"; |
| 44 static const int kClaimRetries = 100; // 5 seconds | 65 static const int kReenumeratePeriod = 100; // 0.1 seconds |
| 45 static const int kDisconnectRetries = 100; // 5 seconds | |
| 46 static const int kRetryPeriod = 50; // 0.05 seconds | |
| 47 static const int kReconnectRetries = 100; // 5 seconds | |
| 48 static const int kUpdateRetries = 100; // 5 seconds | |
| 49 | |
| 50 // Wait for the given time delta while still running the main loop. This is | |
| 51 // necessary so that device add/remove events are processed by the UsbService. | |
| 52 void SleepWithRunLoop(base::TimeDelta delta) { | |
| 53 base::RunLoop run_loop; | |
| 54 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 55 run_loop.QuitClosure(), delta); | |
| 56 run_loop.Run(); | |
| 57 } | |
| 58 | 66 |
| 59 struct UsbTestGadgetConfiguration { | 67 struct UsbTestGadgetConfiguration { |
| 60 UsbTestGadget::Type type; | 68 UsbTestGadget::Type type; |
| 61 const char* http_resource; | 69 const char* http_resource; |
| 62 uint16 product_id; | 70 uint16 product_id; |
| 63 }; | 71 }; |
| 64 | 72 |
| 65 static const struct UsbTestGadgetConfiguration kConfigurations[] = { | 73 static const struct UsbTestGadgetConfiguration kConfigurations[] = { |
| 66 {UsbTestGadget::DEFAULT, "/unconfigure", 0x58F0}, | 74 {UsbTestGadget::DEFAULT, "/unconfigure", 0x58F0}, |
| 67 {UsbTestGadget::KEYBOARD, "/keyboard/configure", 0x58F1}, | 75 {UsbTestGadget::KEYBOARD, "/keyboard/configure", 0x58F1}, |
| 68 {UsbTestGadget::MOUSE, "/mouse/configure", 0x58F2}, | 76 {UsbTestGadget::MOUSE, "/mouse/configure", 0x58F2}, |
| 69 {UsbTestGadget::HID_ECHO, "/hid_echo/configure", 0x58F3}, | 77 {UsbTestGadget::HID_ECHO, "/hid_echo/configure", 0x58F3}, |
| 70 {UsbTestGadget::ECHO, "/echo/configure", 0x58F4}, | 78 {UsbTestGadget::ECHO, "/echo/configure", 0x58F4}, |
| 71 }; | 79 }; |
| 72 | 80 |
| 73 class UsbTestGadgetImpl : public UsbTestGadget { | 81 bool ReadFile(const base::FilePath& file_path, std::string* content) { |
| 74 public: | 82 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); |
| 75 ~UsbTestGadgetImpl() override; | 83 if (!file.IsValid()) { |
| 84 LOG(ERROR) << "Cannot open " << file_path.MaybeAsASCII() << ": " |
| 85 << base::File::ErrorToString(file.error_details()); |
| 86 return false; |
| 87 } |
| 76 | 88 |
| 77 bool Unclaim() override; | 89 STLClearObject(content); |
| 78 bool Disconnect() override; | 90 int rv; |
| 79 bool Reconnect() override; | 91 do { |
| 80 bool SetType(Type type) override; | 92 char buf[4096]; |
| 81 UsbDevice* GetDevice() const override; | 93 rv = file.ReadAtCurrentPos(buf, sizeof buf); |
| 82 const std::string& GetSerialNumber() const override; | 94 if (rv == -1) { |
| 95 LOG(ERROR) << "Cannot read " << file_path.MaybeAsASCII() << ": " |
| 96 << base::File::ErrorToString(file.error_details()); |
| 97 return false; |
| 98 } |
| 99 content->append(buf, rv); |
| 100 } while (rv > 0); |
| 83 | 101 |
| 84 protected: | 102 return true; |
| 85 UsbTestGadgetImpl(); | |
| 86 | |
| 87 private: | |
| 88 scoped_ptr<net::URLFetcher> CreateURLFetcher( | |
| 89 const GURL& url, | |
| 90 net::URLFetcher::RequestType request_type, | |
| 91 net::URLFetcherDelegate* delegate); | |
| 92 int SimplePOSTRequest(const GURL& url, const std::string& form_data); | |
| 93 bool FindUnclaimed(); | |
| 94 bool GetVersion(std::string* version); | |
| 95 bool Update(); | |
| 96 bool FindClaimed(); | |
| 97 bool ReadLocalVersion(std::string* version); | |
| 98 bool ReadLocalPackage(std::string* package); | |
| 99 bool ReadFile(const base::FilePath& file_path, std::string* content); | |
| 100 | |
| 101 class Delegate : public net::URLFetcherDelegate { | |
| 102 public: | |
| 103 Delegate() {} | |
| 104 ~Delegate() override {} | |
| 105 | |
| 106 void WaitForCompletion() { | |
| 107 run_loop_.Run(); | |
| 108 } | |
| 109 | |
| 110 void OnURLFetchComplete(const net::URLFetcher* source) override { | |
| 111 run_loop_.Quit(); | |
| 112 } | |
| 113 | |
| 114 private: | |
| 115 base::RunLoop run_loop_; | |
| 116 | |
| 117 DISALLOW_COPY_AND_ASSIGN(Delegate); | |
| 118 }; | |
| 119 | |
| 120 scoped_refptr<UsbDevice> device_; | |
| 121 std::string device_address_; | |
| 122 scoped_ptr<net::URLRequestContext> request_context_; | |
| 123 std::string session_id_; | |
| 124 UsbService* usb_service_; | |
| 125 | |
| 126 friend class UsbTestGadget; | |
| 127 | |
| 128 DISALLOW_COPY_AND_ASSIGN(UsbTestGadgetImpl); | |
| 129 }; | |
| 130 | |
| 131 } // namespace | |
| 132 | |
| 133 bool UsbTestGadget::IsTestEnabled() { | |
| 134 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | |
| 135 return command_line->HasSwitch(kCommandLineSwitch); | |
| 136 } | 103 } |
| 137 | 104 |
| 138 scoped_ptr<UsbTestGadget> UsbTestGadget::Claim() { | 105 bool ReadLocalVersion(std::string* version) { |
| 139 scoped_ptr<UsbTestGadgetImpl> gadget(new UsbTestGadgetImpl); | 106 base::FilePath file_path; |
| 107 CHECK(PathService::Get(base::DIR_EXE, &file_path)); |
| 108 file_path = file_path.AppendASCII("usb_gadget.zip.md5"); |
| 140 | 109 |
| 141 int retries = kClaimRetries; | 110 return ReadFile(file_path, version); |
| 142 while (!gadget->FindUnclaimed()) { | |
| 143 if (--retries == 0) { | |
| 144 LOG(ERROR) << "Failed to find an unclaimed device."; | |
| 145 return scoped_ptr<UsbTestGadget>(); | |
| 146 } | |
| 147 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | |
| 148 } | |
| 149 VLOG(1) << "It took " << (kClaimRetries - retries) | |
| 150 << " retries to find an unclaimed device."; | |
| 151 | |
| 152 return gadget.Pass(); | |
| 153 } | 111 } |
| 154 | 112 |
| 155 UsbTestGadgetImpl::UsbTestGadgetImpl() { | 113 bool ReadLocalPackage(std::string* package) { |
| 156 net::URLRequestContextBuilder context_builder; | 114 base::FilePath file_path; |
| 157 context_builder.set_proxy_service(net::ProxyService::CreateDirect()); | 115 CHECK(PathService::Get(base::DIR_EXE, &file_path)); |
| 158 request_context_.reset(context_builder.Build()); | 116 file_path = file_path.AppendASCII("usb_gadget.zip"); |
| 159 | 117 |
| 160 base::ProcessId process_id = base::GetCurrentProcId(); | 118 return ReadFile(file_path, package); |
| 161 session_id_ = base::StringPrintf( | |
| 162 "%s:%p", base::HexEncode(&process_id, sizeof(process_id)).c_str(), this); | |
| 163 | |
| 164 usb_service_ = UsbService::GetInstance(NULL); | |
| 165 } | 119 } |
| 166 | 120 |
| 167 UsbTestGadgetImpl::~UsbTestGadgetImpl() { | 121 scoped_ptr<net::URLFetcher> CreateURLFetcher( |
| 168 if (!device_address_.empty()) { | 122 scoped_refptr<net::URLRequestContextGetter> request_context_getter, |
| 169 Unclaim(); | 123 const GURL& url, |
| 170 } | 124 net::URLFetcher::RequestType request_type, |
| 171 } | |
| 172 | |
| 173 UsbDevice* UsbTestGadgetImpl::GetDevice() const { | |
| 174 return device_.get(); | |
| 175 } | |
| 176 | |
| 177 const std::string& UsbTestGadgetImpl::GetSerialNumber() const { | |
| 178 return device_address_; | |
| 179 } | |
| 180 | |
| 181 scoped_ptr<net::URLFetcher> UsbTestGadgetImpl::CreateURLFetcher( | |
| 182 const GURL& url, net::URLFetcher::RequestType request_type, | |
| 183 net::URLFetcherDelegate* delegate) { | 125 net::URLFetcherDelegate* delegate) { |
| 184 scoped_ptr<net::URLFetcher> url_fetcher( | 126 scoped_ptr<net::URLFetcher> url_fetcher( |
| 185 net::URLFetcher::Create(url, request_type, delegate)); | 127 net::URLFetcher::Create(url, request_type, delegate)); |
| 186 | 128 |
| 187 url_fetcher->SetRequestContext(new net::TrivialURLRequestContextGetter( | 129 url_fetcher->SetRequestContext(request_context_getter.get()); |
| 188 request_context_.get(), base::MessageLoop::current()->task_runner())); | |
| 189 | 130 |
| 190 return url_fetcher; | 131 return url_fetcher; |
| 191 } | 132 } |
| 192 | 133 |
| 193 int UsbTestGadgetImpl::SimplePOSTRequest(const GURL& url, | 134 class URLRequestContextGetter : public net::URLRequestContextGetter { |
| 194 const std::string& form_data) { | 135 public: |
| 195 Delegate delegate; | 136 URLRequestContextGetter( |
| 196 scoped_ptr<net::URLFetcher> url_fetcher = | 137 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner) |
| 197 CreateURLFetcher(url, net::URLFetcher::POST, &delegate); | 138 : network_task_runner_(network_task_runner) {} |
| 139 |
| 140 private: |
| 141 ~URLRequestContextGetter() override {} |
| 142 |
| 143 // net::URLRequestContextGetter implementation |
| 144 net::URLRequestContext* GetURLRequestContext() override { |
| 145 context_builder_.set_proxy_service(net::ProxyService::CreateDirect()); |
| 146 return context_builder_.Build(); |
| 147 } |
| 148 |
| 149 scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() |
| 150 const override { |
| 151 return network_task_runner_; |
| 152 } |
| 153 |
| 154 net::URLRequestContextBuilder context_builder_; |
| 155 scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; |
| 156 }; |
| 157 |
| 158 class URLFetcherDelegate : public net::URLFetcherDelegate { |
| 159 public: |
| 160 URLFetcherDelegate() {} |
| 161 ~URLFetcherDelegate() override {} |
| 162 |
| 163 void WaitForCompletion() { run_loop_.Run(); } |
| 164 |
| 165 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 166 run_loop_.Quit(); |
| 167 } |
| 168 |
| 169 private: |
| 170 base::RunLoop run_loop_; |
| 171 |
| 172 DISALLOW_COPY_AND_ASSIGN(URLFetcherDelegate); |
| 173 }; |
| 174 |
| 175 int SimplePOSTRequest( |
| 176 scoped_refptr<net::URLRequestContextGetter> request_context_getter, |
| 177 const GURL& url, |
| 178 const std::string& form_data) { |
| 179 URLFetcherDelegate delegate; |
| 180 scoped_ptr<net::URLFetcher> url_fetcher = CreateURLFetcher( |
| 181 request_context_getter, url, net::URLFetcher::POST, &delegate); |
| 198 | 182 |
| 199 url_fetcher->SetUploadData("application/x-www-form-urlencoded", form_data); | 183 url_fetcher->SetUploadData("application/x-www-form-urlencoded", form_data); |
| 200 url_fetcher->Start(); | 184 url_fetcher->Start(); |
| 201 delegate.WaitForCompletion(); | 185 delegate.WaitForCompletion(); |
| 202 | 186 |
| 203 return url_fetcher->GetResponseCode(); | 187 return url_fetcher->GetResponseCode(); |
| 204 } | 188 } |
| 205 | 189 |
| 206 bool UsbTestGadgetImpl::FindUnclaimed() { | 190 class UsbGadgetFactory : public UsbService::Observer, |
| 207 std::vector<scoped_refptr<UsbDevice> > devices; | 191 public net::URLFetcherDelegate { |
| 208 usb_service_->GetDevices(&devices); | 192 public: |
| 209 | 193 UsbGadgetFactory(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) |
| 210 for (std::vector<scoped_refptr<UsbDevice> >::const_iterator iter = | 194 : observer_(this), weak_factory_(this) { |
| 211 devices.begin(); iter != devices.end(); ++iter) { | 195 usb_service_ = UsbService::GetInstance(io_task_runner); |
| 212 const scoped_refptr<UsbDevice> &device = *iter; | 196 request_context_getter_ = new URLRequestContextGetter(io_task_runner); |
| 213 if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) { | 197 |
| 214 base::string16 serial_utf16; | 198 static uint32 next_session_id; |
| 215 if (!device->GetSerialNumber(&serial_utf16)) { | 199 base::ProcessId process_id = base::GetCurrentProcId(); |
| 216 continue; | 200 session_id_ = base::StringPrintf("%d-%d", process_id, next_session_id++); |
| 217 } | 201 |
| 218 | 202 observer_.Add(usb_service_); |
| 219 const std::string serial = base::UTF16ToUTF8(serial_utf16); | 203 } |
| 220 const GURL url("http://" + serial + "/claim"); | 204 |
| 221 const std::string form_data = base::StringPrintf( | 205 ~UsbGadgetFactory() override {} |
| 222 "session_id=%s", | 206 |
| 223 net::EscapeUrlEncodedData(session_id_, true).c_str()); | 207 scoped_ptr<UsbTestGadget> WaitForDevice() { |
| 224 const int response_code = SimplePOSTRequest(url, form_data); | 208 EnumerateDevices(); |
| 225 | 209 run_loop_.Run(); |
| 210 return make_scoped_ptr( |
| 211 new UsbTestGadgetImpl(request_context_getter_, usb_service_, device_)); |
| 212 } |
| 213 |
| 214 private: |
| 215 void EnumerateDevices() { |
| 216 if (!device_) { |
| 217 usb_service_->GetDevices(base::Bind( |
| 218 &UsbGadgetFactory::OnDevicesEnumerated, weak_factory_.GetWeakPtr())); |
| 219 } |
| 220 } |
| 221 |
| 222 void OnDevicesEnumerated( |
| 223 const std::vector<scoped_refptr<UsbDevice>>& devices) { |
| 224 for (const scoped_refptr<UsbDevice>& device : devices) { |
| 225 OnDeviceAdded(device); |
| 226 } |
| 227 |
| 228 if (!device_) { |
| 229 // TODO(reillyg): This timer could be replaced by a way to use long- |
| 230 // polling to wait for claimed devices to become unclaimed. |
| 231 base::MessageLoop::current()->PostDelayedTask( |
| 232 FROM_HERE, base::Bind(&UsbGadgetFactory::EnumerateDevices, |
| 233 weak_factory_.GetWeakPtr()), |
| 234 base::TimeDelta::FromMilliseconds(kReenumeratePeriod)); |
| 235 } |
| 236 } |
| 237 |
| 238 void OnDeviceAdded(scoped_refptr<UsbDevice> device) override { |
| 239 if (device_.get()) { |
| 240 // Already trying to claim a device. |
| 241 return; |
| 242 } |
| 243 |
| 244 if (device->vendor_id() != 0x18D1 || device->product_id() != 0x58F0 || |
| 245 device->serial_number().empty()) { |
| 246 return; |
| 247 } |
| 248 |
| 249 std::string serial_number = base::UTF16ToUTF8(device->serial_number()); |
| 250 if (serial_number == serial_number_) { |
| 251 // We were waiting for the device to reappear after upgrade. |
| 252 device_ = device; |
| 253 run_loop_.Quit(); |
| 254 return; |
| 255 } |
| 256 |
| 257 device_ = device; |
| 258 serial_number_ = serial_number; |
| 259 Claim(); |
| 260 } |
| 261 |
| 262 void Claim() { |
| 263 VLOG(1) << "Trying to claim " << serial_number_ << "."; |
| 264 |
| 265 GURL url("http://" + serial_number_ + "/claim"); |
| 266 std::string form_data = base::StringPrintf( |
| 267 "session_id=%s", net::EscapeUrlEncodedData(session_id_, true).c_str()); |
| 268 url_fetcher_ = CreateURLFetcher(request_context_getter_, url, |
| 269 net::URLFetcher::POST, this); |
| 270 url_fetcher_->SetUploadData("application/x-www-form-urlencoded", form_data); |
| 271 url_fetcher_->Start(); |
| 272 } |
| 273 |
| 274 void GetVersion() { |
| 275 GURL url("http://" + serial_number_ + "/version"); |
| 276 url_fetcher_ = CreateURLFetcher(request_context_getter_, url, |
| 277 net::URLFetcher::GET, this); |
| 278 url_fetcher_->Start(); |
| 279 } |
| 280 |
| 281 bool Update(const std::string& version) { |
| 282 LOG(INFO) << "Updating " << serial_number_ << " to " << version << "..."; |
| 283 |
| 284 GURL url("http://" + serial_number_ + "/update"); |
| 285 url_fetcher_ = CreateURLFetcher(request_context_getter_, url, |
| 286 net::URLFetcher::POST, this); |
| 287 std::string mime_header = base::StringPrintf( |
| 288 "--foo\r\n" |
| 289 "Content-Disposition: form-data; name=\"file\"; " |
| 290 "filename=\"usb_gadget-%s.zip\"\r\n" |
| 291 "Content-Type: application/octet-stream\r\n" |
| 292 "\r\n", |
| 293 version.c_str()); |
| 294 std::string mime_footer("\r\n--foo--\r\n"); |
| 295 |
| 296 std::string package; |
| 297 if (!ReadLocalPackage(&package)) { |
| 298 return false; |
| 299 } |
| 300 |
| 301 url_fetcher_->SetUploadData("multipart/form-data; boundary=foo", |
| 302 mime_header + package + mime_footer); |
| 303 url_fetcher_->Start(); |
| 304 device_ = nullptr; |
| 305 return true; |
| 306 } |
| 307 |
| 308 void OnURLFetchComplete(const net::URLFetcher* source) override { |
| 309 DCHECK(!serial_number_.empty()); |
| 310 |
| 311 int response_code = source->GetResponseCode(); |
| 312 if (!claimed_) { |
| 313 // Just completed a /claim request. |
| 226 if (response_code == 200) { | 314 if (response_code == 200) { |
| 227 device_address_ = serial; | 315 claimed_ = true; |
| 228 device_ = device; | 316 GetVersion(); |
| 229 break; | 317 } else { |
| 230 } | 318 if (response_code != 403) { |
| 231 | 319 LOG(WARNING) << "Unexpected HTTP " << response_code |
| 232 // The device is probably claimed by another process. | 320 << " from /claim."; |
| 233 if (response_code != 403) { | 321 } |
| 234 LOG(WARNING) << "Unexpected HTTP " << response_code << " from /claim."; | 322 Reset(); |
| 235 } | 323 } |
| 236 } | 324 } else if (version_.empty()) { |
| 237 } | 325 // Just completed a /version request. |
| 238 | 326 if (response_code != 200) { |
| 239 std::string local_version; | 327 LOG(WARNING) << "Unexpected HTTP " << response_code |
| 240 std::string version; | 328 << " from /version."; |
| 241 if (!ReadLocalVersion(&local_version) || | 329 Reset(); |
| 242 !GetVersion(&version)) { | 330 return; |
| 243 return false; | 331 } |
| 244 } | 332 |
| 245 | 333 if (!source->GetResponseAsString(&version_)) { |
| 246 if (version == local_version) { | 334 LOG(WARNING) << "Failed to read body from /version."; |
| 247 return true; | 335 Reset(); |
| 248 } | 336 return; |
| 249 | 337 } |
| 250 return Update(); | 338 |
| 251 } | 339 std::string local_version; |
| 252 | 340 if (!ReadLocalVersion(&local_version)) { |
| 253 bool UsbTestGadgetImpl::GetVersion(std::string* version) { | 341 Reset(); |
| 254 Delegate delegate; | 342 return; |
| 255 const GURL url("http://" + device_address_ + "/version"); | 343 } |
| 256 scoped_ptr<net::URLFetcher> url_fetcher = | 344 |
| 257 CreateURLFetcher(url, net::URLFetcher::GET, &delegate); | 345 if (version_ == local_version) { |
| 258 | 346 run_loop_.Quit(); |
| 259 url_fetcher->Start(); | 347 } else { |
| 260 delegate.WaitForCompletion(); | 348 if (!Update(local_version)) { |
| 261 | 349 Reset(); |
| 262 const int response_code = url_fetcher->GetResponseCode(); | 350 } |
| 263 if (response_code != 200) { | 351 } |
| 264 VLOG(2) << "Unexpected HTTP " << response_code << " from /version."; | 352 } else { |
| 265 return false; | 353 // Just completed an /update request. |
| 266 } | 354 if (response_code != 200) { |
| 267 | 355 LOG(WARNING) << "Unexpected HTTP " << response_code << " from /update."; |
| 268 STLClearObject(version); | 356 Reset(); |
| 269 if (!url_fetcher->GetResponseAsString(version)) { | 357 return; |
| 270 VLOG(2) << "Failed to read body from /version."; | 358 } |
| 271 return false; | 359 |
| 272 } | 360 // Must wait for the device to reconnect. |
| 273 return true; | 361 } |
| 274 } | 362 } |
| 275 | 363 |
| 276 bool UsbTestGadgetImpl::Update() { | 364 void Reset() { |
| 277 std::string version; | 365 device_ = nullptr; |
| 278 if (!ReadLocalVersion(&version)) { | 366 serial_number_.clear(); |
| 279 return false; | 367 claimed_ = false; |
| 280 } | 368 version_.clear(); |
| 281 LOG(INFO) << "Updating " << device_address_ << " to " << version << "..."; | 369 |
| 282 | 370 // Wait a bit and then try again to find an available device. |
| 283 Delegate delegate; | 371 base::MessageLoop::current()->PostDelayedTask( |
| 284 const GURL url("http://" + device_address_ + "/update"); | 372 FROM_HERE, base::Bind(&UsbGadgetFactory::EnumerateDevices, |
| 285 scoped_ptr<net::URLFetcher> url_fetcher = | 373 weak_factory_.GetWeakPtr()), |
| 286 CreateURLFetcher(url, net::URLFetcher::POST, &delegate); | 374 base::TimeDelta::FromMilliseconds(kReenumeratePeriod)); |
| 287 | 375 } |
| 288 const std::string mime_header = | 376 |
| 289 base::StringPrintf( | 377 UsbService* usb_service_ = nullptr; |
| 290 "--foo\r\n" | 378 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| 291 "Content-Disposition: form-data; name=\"file\"; " | 379 std::string session_id_; |
| 292 "filename=\"usb_gadget-%s.zip\"\r\n" | 380 scoped_ptr<net::URLFetcher> url_fetcher_; |
| 293 "Content-Type: application/octet-stream\r\n" | 381 scoped_refptr<UsbDevice> device_; |
| 294 "\r\n", version.c_str()); | 382 std::string serial_number_; |
| 295 const std::string mime_footer("\r\n--foo--\r\n"); | 383 bool claimed_ = false; |
| 296 | 384 std::string version_; |
| 297 std::string package; | 385 base::RunLoop run_loop_; |
| 298 if (!ReadLocalPackage(&package)) { | 386 ScopedObserver<UsbService, UsbService::Observer> observer_; |
| 299 return false; | 387 base::WeakPtrFactory<UsbGadgetFactory> weak_factory_; |
| 300 } | 388 }; |
| 301 | 389 |
| 302 url_fetcher->SetUploadData("multipart/form-data; boundary=foo", | 390 class DeviceAddListener : public UsbService::Observer { |
| 303 mime_header + package + mime_footer); | 391 public: |
| 304 url_fetcher->Start(); | 392 DeviceAddListener(UsbService* usb_service, |
| 305 delegate.WaitForCompletion(); | 393 const std::string& serial_number, |
| 306 | 394 int product_id) |
| 307 const int response_code = url_fetcher->GetResponseCode(); | 395 : usb_service_(usb_service), |
| 308 if (response_code != 200) { | 396 serial_number_(serial_number), |
| 309 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /update."; | 397 product_id_(product_id), |
| 310 return false; | 398 observer_(this), |
| 311 } | 399 weak_factory_(this) { |
| 312 | 400 observer_.Add(usb_service_); |
| 313 int retries = kUpdateRetries; | 401 } |
| 314 std::string new_version; | 402 virtual ~DeviceAddListener() {} |
| 315 while (!GetVersion(&new_version) || new_version != version) { | 403 |
| 316 if (--retries == 0) { | 404 scoped_refptr<UsbDevice> WaitForAdd() { |
| 317 LOG(ERROR) << "Device not responding with new version."; | 405 usb_service_->GetDevices(base::Bind(&DeviceAddListener::OnDevicesEnumerated, |
| 318 return false; | 406 weak_factory_.GetWeakPtr())); |
| 319 } | 407 run_loop_.Run(); |
| 320 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | 408 return device_; |
| 321 } | 409 } |
| 322 VLOG(1) << "It took " << (kUpdateRetries - retries) | 410 |
| 323 << " retries to see the new version."; | 411 private: |
| 324 | 412 void OnDevicesEnumerated( |
| 325 // Release the old reference to the device and try to open a new one. | 413 const std::vector<scoped_refptr<UsbDevice>>& devices) { |
| 326 device_ = NULL; | 414 for (const scoped_refptr<UsbDevice>& device : devices) { |
| 327 retries = kReconnectRetries; | 415 OnDeviceAdded(device); |
| 328 while (!FindClaimed()) { | 416 } |
| 329 if (--retries == 0) { | 417 } |
| 330 LOG(ERROR) << "Failed to find updated device."; | 418 |
| 331 return false; | 419 void OnDeviceAdded(scoped_refptr<UsbDevice> device) override { |
| 332 } | 420 if (device->vendor_id() == 0x18D1 && !device->serial_number().empty()) { |
| 333 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | |
| 334 } | |
| 335 VLOG(1) << "It took " << (kReconnectRetries - retries) | |
| 336 << " retries to find the updated device."; | |
| 337 | |
| 338 return true; | |
| 339 } | |
| 340 | |
| 341 bool UsbTestGadgetImpl::FindClaimed() { | |
| 342 CHECK(!device_.get()); | |
| 343 | |
| 344 std::string expected_serial = GetSerialNumber(); | |
| 345 | |
| 346 std::vector<scoped_refptr<UsbDevice> > devices; | |
| 347 usb_service_->GetDevices(&devices); | |
| 348 | |
| 349 for (std::vector<scoped_refptr<UsbDevice> >::iterator iter = | |
| 350 devices.begin(); iter != devices.end(); ++iter) { | |
| 351 scoped_refptr<UsbDevice> &device = *iter; | |
| 352 | |
| 353 if (device->vendor_id() == 0x18D1) { | |
| 354 const uint16 product_id = device->product_id(); | 421 const uint16 product_id = device->product_id(); |
| 355 bool found = false; | 422 if (product_id_ == -1) { |
| 356 for (size_t i = 0; i < arraysize(kConfigurations); ++i) { | 423 bool found = false; |
| 357 if (product_id == kConfigurations[i].product_id) { | 424 for (size_t i = 0; i < arraysize(kConfigurations); ++i) { |
| 358 found = true; | 425 if (product_id == kConfigurations[i].product_id) { |
| 359 break; | 426 found = true; |
| 360 } | 427 break; |
| 361 } | 428 } |
| 362 if (!found) { | 429 } |
| 363 continue; | 430 if (!found) { |
| 364 } | 431 return; |
| 365 | 432 } |
| 366 base::string16 serial_utf16; | 433 } else { |
| 367 if (!device->GetSerialNumber(&serial_utf16)) { | 434 if (product_id_ != product_id) { |
| 368 continue; | 435 return; |
| 369 } | 436 } |
| 370 | 437 } |
| 371 std::string serial = base::UTF16ToUTF8(serial_utf16); | 438 |
| 372 if (serial != expected_serial) { | 439 if (serial_number_ != base::UTF16ToUTF8(device->serial_number())) { |
| 373 continue; | 440 return; |
| 374 } | 441 } |
| 375 | 442 |
| 376 device_ = device; | 443 device_ = device; |
| 377 return true; | 444 run_loop_.Quit(); |
| 378 } | 445 } |
| 379 } | 446 } |
| 380 | 447 |
| 381 return false; | 448 UsbService* usb_service_; |
| 382 } | 449 const std::string serial_number_; |
| 383 | 450 const int product_id_; |
| 384 bool UsbTestGadgetImpl::ReadLocalVersion(std::string* version) { | 451 base::RunLoop run_loop_; |
| 385 base::FilePath file_path; | 452 scoped_refptr<UsbDevice> device_; |
| 386 CHECK(PathService::Get(base::DIR_EXE, &file_path)); | 453 ScopedObserver<UsbService, UsbService::Observer> observer_; |
| 387 file_path = file_path.AppendASCII("usb_gadget.zip.md5"); | 454 base::WeakPtrFactory<DeviceAddListener> weak_factory_; |
| 388 | 455 |
| 389 return ReadFile(file_path, version); | 456 DISALLOW_COPY_AND_ASSIGN(DeviceAddListener); |
| 390 } | 457 }; |
| 391 | 458 |
| 392 bool UsbTestGadgetImpl::ReadLocalPackage(std::string* package) { | 459 class DeviceRemoveListener : public UsbService::Observer { |
| 393 base::FilePath file_path; | 460 public: |
| 394 CHECK(PathService::Get(base::DIR_EXE, &file_path)); | 461 DeviceRemoveListener(UsbService* usb_service, scoped_refptr<UsbDevice> device) |
| 395 file_path = file_path.AppendASCII("usb_gadget.zip"); | 462 : usb_service_(usb_service), |
| 396 | 463 device_(device), |
| 397 return ReadFile(file_path, package); | 464 observer_(this), |
| 398 } | 465 weak_factory_(this) { |
| 399 | 466 observer_.Add(usb_service_); |
| 400 bool UsbTestGadgetImpl::ReadFile(const base::FilePath& file_path, | 467 } |
| 401 std::string* content) { | 468 virtual ~DeviceRemoveListener() {} |
| 402 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ); | 469 |
| 403 if (!file.IsValid()) { | 470 void WaitForRemove() { |
| 404 LOG(ERROR) << "Cannot open " << file_path.MaybeAsASCII() << ": " | 471 usb_service_->GetDevices( |
| 405 << base::File::ErrorToString(file.error_details()); | 472 base::Bind(&DeviceRemoveListener::OnDevicesEnumerated, |
| 406 return false; | 473 weak_factory_.GetWeakPtr())); |
| 407 } | 474 run_loop_.Run(); |
| 408 | 475 } |
| 409 STLClearObject(content); | 476 |
| 410 int rv; | 477 private: |
| 411 do { | 478 void OnDevicesEnumerated( |
| 412 char buf[4096]; | 479 const std::vector<scoped_refptr<UsbDevice>>& devices) { |
| 413 rv = file.ReadAtCurrentPos(buf, sizeof buf); | 480 bool found = false; |
| 414 if (rv == -1) { | 481 for (const scoped_refptr<UsbDevice>& device : devices) { |
| 415 LOG(ERROR) << "Cannot read " << file_path.MaybeAsASCII() << ": " | 482 if (device_ == device) { |
| 416 << base::File::ErrorToString(file.error_details()); | 483 found = true; |
| 417 return false; | 484 } |
| 418 } | 485 } |
| 419 content->append(buf, rv); | 486 if (!found) { |
| 420 } while (rv > 0); | 487 run_loop_.Quit(); |
| 421 | 488 } |
| 422 return true; | 489 } |
| 490 |
| 491 void OnDeviceRemoved(scoped_refptr<UsbDevice> device) override { |
| 492 if (device_ == device) { |
| 493 run_loop_.Quit(); |
| 494 } |
| 495 } |
| 496 |
| 497 UsbService* usb_service_; |
| 498 base::RunLoop run_loop_; |
| 499 scoped_refptr<UsbDevice> device_; |
| 500 ScopedObserver<UsbService, UsbService::Observer> observer_; |
| 501 base::WeakPtrFactory<DeviceRemoveListener> weak_factory_; |
| 502 |
| 503 DISALLOW_COPY_AND_ASSIGN(DeviceRemoveListener); |
| 504 }; |
| 505 |
| 506 } // namespace |
| 507 |
| 508 bool UsbTestGadget::IsTestEnabled() { |
| 509 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); |
| 510 return command_line->HasSwitch(kCommandLineSwitch); |
| 511 } |
| 512 |
| 513 scoped_ptr<UsbTestGadget> UsbTestGadget::Claim( |
| 514 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { |
| 515 UsbGadgetFactory gadget_factory(io_task_runner); |
| 516 return gadget_factory.WaitForDevice().Pass(); |
| 517 } |
| 518 |
| 519 UsbTestGadgetImpl::UsbTestGadgetImpl( |
| 520 scoped_refptr<net::URLRequestContextGetter> request_context_getter_, |
| 521 UsbService* usb_service, |
| 522 scoped_refptr<UsbDevice> device) |
| 523 : device_address_(base::UTF16ToUTF8(device->serial_number())), |
| 524 device_(device), |
| 525 request_context_getter_(request_context_getter_), |
| 526 usb_service_(usb_service) { |
| 527 } |
| 528 |
| 529 UsbTestGadgetImpl::~UsbTestGadgetImpl() { |
| 530 if (!device_address_.empty()) { |
| 531 Unclaim(); |
| 532 } |
| 533 } |
| 534 |
| 535 UsbDevice* UsbTestGadgetImpl::GetDevice() const { |
| 536 return device_.get(); |
| 423 } | 537 } |
| 424 | 538 |
| 425 bool UsbTestGadgetImpl::Unclaim() { | 539 bool UsbTestGadgetImpl::Unclaim() { |
| 426 VLOG(1) << "Releasing the device at " << device_address_ << "."; | 540 VLOG(1) << "Releasing the device at " << device_address_ << "."; |
| 427 | 541 |
| 428 const GURL url("http://" + device_address_ + "/unclaim"); | 542 GURL url("http://" + device_address_ + "/unclaim"); |
| 429 const int response_code = SimplePOSTRequest(url, ""); | 543 int response_code = SimplePOSTRequest(request_context_getter_, url, ""); |
| 430 | 544 |
| 431 if (response_code != 200) { | 545 if (response_code != 200) { |
| 432 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /unclaim."; | 546 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /unclaim."; |
| 433 return false; | 547 return false; |
| 434 } | 548 } |
| 435 | 549 |
| 436 device_address_.clear(); | 550 device_address_.clear(); |
| 437 return true; | 551 return true; |
| 438 } | 552 } |
| 439 | 553 |
| 440 bool UsbTestGadgetImpl::SetType(Type type) { | 554 bool UsbTestGadgetImpl::SetType(Type type) { |
| 441 const struct UsbTestGadgetConfiguration* config = NULL; | 555 const struct UsbTestGadgetConfiguration* config = NULL; |
| 442 for (size_t i = 0; i < arraysize(kConfigurations); ++i) { | 556 for (size_t i = 0; i < arraysize(kConfigurations); ++i) { |
| 443 if (kConfigurations[i].type == type) { | 557 if (kConfigurations[i].type == type) { |
| 444 config = &kConfigurations[i]; | 558 config = &kConfigurations[i]; |
| 445 } | 559 } |
| 446 } | 560 } |
| 447 CHECK(config); | 561 CHECK(config); |
| 448 | 562 |
| 449 const GURL url("http://" + device_address_ + config->http_resource); | 563 GURL url("http://" + device_address_ + config->http_resource); |
| 450 const int response_code = SimplePOSTRequest(url, ""); | 564 int response_code = SimplePOSTRequest(request_context_getter_, url, ""); |
| 451 | 565 |
| 452 if (response_code != 200) { | 566 if (response_code != 200) { |
| 453 LOG(ERROR) << "Unexpected HTTP " << response_code | 567 LOG(ERROR) << "Unexpected HTTP " << response_code |
| 454 << " from " << config->http_resource << "."; | 568 << " from " << config->http_resource << "."; |
| 455 return false; | 569 return false; |
| 456 } | 570 } |
| 457 | 571 |
| 458 // Release the old reference to the device and try to open a new one. | 572 // Release the old reference to the device and try to open a new one. |
| 459 int retries = kReconnectRetries; | 573 DeviceAddListener add_listener(usb_service_, device_address_, |
| 460 while (true) { | 574 config->product_id); |
| 461 device_ = NULL; | 575 device_ = add_listener.WaitForAdd(); |
| 462 if (FindClaimed() && device_->product_id() == config->product_id) { | 576 DCHECK(device_.get()); |
| 463 break; | |
| 464 } | |
| 465 if (--retries == 0) { | |
| 466 LOG(ERROR) << "Failed to find updated device."; | |
| 467 return false; | |
| 468 } | |
| 469 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | |
| 470 } | |
| 471 VLOG(1) << "It took " << (kReconnectRetries - retries) | |
| 472 << " retries to find the updated device."; | |
| 473 | |
| 474 return true; | 577 return true; |
| 475 } | 578 } |
| 476 | 579 |
| 477 bool UsbTestGadgetImpl::Disconnect() { | 580 bool UsbTestGadgetImpl::Disconnect() { |
| 478 const GURL url("http://" + device_address_ + "/disconnect"); | 581 GURL url("http://" + device_address_ + "/disconnect"); |
| 479 const int response_code = SimplePOSTRequest(url, ""); | 582 int response_code = SimplePOSTRequest(request_context_getter_, url, ""); |
| 480 | 583 |
| 481 if (response_code != 200) { | 584 if (response_code != 200) { |
| 482 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /disconnect."; | 585 LOG(ERROR) << "Unexpected HTTP " << response_code << " from " << url << "."; |
| 483 return false; | 586 return false; |
| 484 } | 587 } |
| 485 | 588 |
| 486 // Release the old reference to the device and wait until it can't be found. | 589 // Release the old reference to the device and wait until it can't be found. |
| 487 int retries = kDisconnectRetries; | 590 DeviceRemoveListener remove_listener(usb_service_, device_); |
| 488 while (true) { | 591 remove_listener.WaitForRemove(); |
| 489 device_ = NULL; | 592 device_ = nullptr; |
| 490 if (!FindClaimed()) { | |
| 491 break; | |
| 492 } | |
| 493 if (--retries == 0) { | |
| 494 LOG(ERROR) << "Device did not disconnect."; | |
| 495 return false; | |
| 496 } | |
| 497 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | |
| 498 } | |
| 499 VLOG(1) << "It took " << (kDisconnectRetries - retries) | |
| 500 << " retries for the device to disconnect."; | |
| 501 | |
| 502 return true; | 593 return true; |
| 503 } | 594 } |
| 504 | 595 |
| 505 bool UsbTestGadgetImpl::Reconnect() { | 596 bool UsbTestGadgetImpl::Reconnect() { |
| 506 const GURL url("http://" + device_address_ + "/reconnect"); | 597 GURL url("http://" + device_address_ + "/reconnect"); |
| 507 const int response_code = SimplePOSTRequest(url, ""); | 598 int response_code = SimplePOSTRequest(request_context_getter_, url, ""); |
| 508 | 599 |
| 509 if (response_code != 200) { | 600 if (response_code != 200) { |
| 510 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /reconnect."; | 601 LOG(ERROR) << "Unexpected HTTP " << response_code << " from " << url << "."; |
| 511 return false; | 602 return false; |
| 512 } | 603 } |
| 513 | 604 |
| 514 int retries = kDisconnectRetries; | 605 DeviceAddListener add_listener(usb_service_, device_address_, -1); |
| 515 while (true) { | 606 device_ = add_listener.WaitForAdd(); |
| 516 if (FindClaimed()) { | 607 DCHECK(device_.get()); |
| 517 break; | |
| 518 } | |
| 519 if (--retries == 0) { | |
| 520 LOG(ERROR) << "Device did not reconnect."; | |
| 521 return false; | |
| 522 } | |
| 523 SleepWithRunLoop(TimeDelta::FromMilliseconds(kRetryPeriod)); | |
| 524 } | |
| 525 VLOG(1) << "It took " << (kDisconnectRetries - retries) | |
| 526 << " retries for the device to reconnect."; | |
| 527 | |
| 528 return true; | 608 return true; |
| 529 } | 609 } |
| 530 | 610 |
| 531 } // namespace device | 611 } // namespace device |
| OLD | NEW |