Move touchscreen device caching to DeviceDataManager
[chromium-blink-merge.git] / device / test / usb_test_gadget_impl.cc
bloba0bf4d33f30401edf296fb13230244607b310e8c
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
3 // found in the LICENSE file.
5 #include "device/test/usb_test_gadget.h"
7 #include <string>
8 #include <vector>
10 #include "base/command_line.h"
11 #include "base/compiler_specific.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/path_service.h"
19 #include "base/process/process.h"
20 #include "base/run_loop.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/stringprintf.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "base/threading/platform_thread.h"
25 #include "base/time/time.h"
26 #include "device/usb/usb_device.h"
27 #include "device/usb/usb_device_handle.h"
28 #include "device/usb/usb_service.h"
29 #include "net/proxy/proxy_service.h"
30 #include "net/url_request/url_fetcher.h"
31 #include "net/url_request/url_fetcher_delegate.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_builder.h"
34 #include "net/url_request/url_request_context_getter.h"
35 #include "url/gurl.h"
37 using ::base::PlatformThread;
38 using ::base::TimeDelta;
40 namespace device {
42 namespace {
44 static const char kCommandLineSwitch[] = "enable-gadget-tests";
45 static const int kClaimRetries = 100; // 5 seconds
46 static const int kDisconnectRetries = 100; // 5 seconds
47 static const int kRetryPeriod = 50; // 0.05 seconds
48 static const int kReconnectRetries = 100; // 5 seconds
49 static const int kUpdateRetries = 100; // 5 seconds
51 struct UsbTestGadgetConfiguration {
52 UsbTestGadget::Type type;
53 const char* http_resource;
54 uint16 product_id;
57 static const struct UsbTestGadgetConfiguration kConfigurations[] = {
58 {UsbTestGadget::DEFAULT, "/unconfigure", 0x58F0},
59 {UsbTestGadget::KEYBOARD, "/keyboard/configure", 0x58F1},
60 {UsbTestGadget::MOUSE, "/mouse/configure", 0x58F2},
61 {UsbTestGadget::HID_ECHO, "/hid_echo/configure", 0x58F3},
62 {UsbTestGadget::ECHO, "/echo/configure", 0x58F4},
65 class UsbTestGadgetImpl : public UsbTestGadget {
66 public:
67 virtual ~UsbTestGadgetImpl();
69 virtual bool Unclaim() OVERRIDE;
70 virtual bool Disconnect() OVERRIDE;
71 virtual bool Reconnect() OVERRIDE;
72 virtual bool SetType(Type type) OVERRIDE;
73 virtual UsbDevice* GetDevice() const OVERRIDE;
74 virtual std::string GetSerial() const OVERRIDE;
76 protected:
77 UsbTestGadgetImpl();
79 private:
80 scoped_ptr<net::URLFetcher> CreateURLFetcher(
81 const GURL& url,
82 net::URLFetcher::RequestType request_type,
83 net::URLFetcherDelegate* delegate);
84 int SimplePOSTRequest(const GURL& url, const std::string& form_data);
85 bool FindUnclaimed();
86 bool GetVersion(std::string* version);
87 bool Update();
88 bool FindClaimed();
89 bool ReadLocalVersion(std::string* version);
90 bool ReadLocalPackage(std::string* package);
91 bool ReadFile(const base::FilePath& file_path, std::string* content);
93 class Delegate : public net::URLFetcherDelegate {
94 public:
95 Delegate() {}
96 virtual ~Delegate() {}
98 void WaitForCompletion() {
99 run_loop_.Run();
102 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
103 run_loop_.Quit();
106 private:
107 base::RunLoop run_loop_;
109 DISALLOW_COPY_AND_ASSIGN(Delegate);
112 scoped_refptr<UsbDevice> device_;
113 std::string device_address_;
114 scoped_ptr<net::URLRequestContext> request_context_;
115 std::string session_id_;
116 UsbService* usb_service_;
118 friend class UsbTestGadget;
120 DISALLOW_COPY_AND_ASSIGN(UsbTestGadgetImpl);
123 } // namespace
125 bool UsbTestGadget::IsTestEnabled() {
126 base::CommandLine* command_line = CommandLine::ForCurrentProcess();
127 return command_line->HasSwitch(kCommandLineSwitch);
130 scoped_ptr<UsbTestGadget> UsbTestGadget::Claim() {
131 scoped_ptr<UsbTestGadgetImpl> gadget(new UsbTestGadgetImpl);
133 int retries = kClaimRetries;
134 while (!gadget->FindUnclaimed()) {
135 if (--retries == 0) {
136 LOG(ERROR) << "Failed to find an unclaimed device.";
137 return scoped_ptr<UsbTestGadget>();
139 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
141 VLOG(1) << "It took " << (kClaimRetries - retries)
142 << " retries to find an unclaimed device.";
144 return gadget.PassAs<UsbTestGadget>();
147 UsbTestGadgetImpl::UsbTestGadgetImpl() {
148 net::URLRequestContextBuilder context_builder;
149 context_builder.set_proxy_service(net::ProxyService::CreateDirect());
150 request_context_.reset(context_builder.Build());
152 base::ProcessId process_id = base::Process::Current().pid();
153 session_id_ = base::StringPrintf(
154 "%s:%p", base::HexEncode(&process_id, sizeof(process_id)).c_str(), this);
156 usb_service_ = UsbService::GetInstance(NULL);
159 UsbTestGadgetImpl::~UsbTestGadgetImpl() {
160 if (!device_address_.empty()) {
161 Unclaim();
165 UsbDevice* UsbTestGadgetImpl::GetDevice() const {
166 return device_.get();
169 std::string UsbTestGadgetImpl::GetSerial() const {
170 return device_address_;
173 scoped_ptr<net::URLFetcher> UsbTestGadgetImpl::CreateURLFetcher(
174 const GURL& url, net::URLFetcher::RequestType request_type,
175 net::URLFetcherDelegate* delegate) {
176 scoped_ptr<net::URLFetcher> url_fetcher(
177 net::URLFetcher::Create(url, request_type, delegate));
179 url_fetcher->SetRequestContext(
180 new net::TrivialURLRequestContextGetter(
181 request_context_.get(),
182 base::MessageLoop::current()->message_loop_proxy()));
184 return url_fetcher.PassAs<net::URLFetcher>();
187 int UsbTestGadgetImpl::SimplePOSTRequest(const GURL& url,
188 const std::string& form_data) {
189 Delegate delegate;
190 scoped_ptr<net::URLFetcher> url_fetcher =
191 CreateURLFetcher(url, net::URLFetcher::POST, &delegate);
193 url_fetcher->SetUploadData("application/x-www-form-urlencoded", form_data);
194 url_fetcher->Start();
195 delegate.WaitForCompletion();
197 return url_fetcher->GetResponseCode();
200 bool UsbTestGadgetImpl::FindUnclaimed() {
201 std::vector<scoped_refptr<UsbDevice> > devices;
202 usb_service_->GetDevices(&devices);
204 for (std::vector<scoped_refptr<UsbDevice> >::const_iterator iter =
205 devices.begin(); iter != devices.end(); ++iter) {
206 const scoped_refptr<UsbDevice> &device = *iter;
207 if (device->vendor_id() == 0x18D1 && device->product_id() == 0x58F0) {
208 scoped_refptr<UsbDeviceHandle> handle = device->Open();
209 if (handle.get() == NULL) {
210 continue;
213 base::string16 serial_utf16;
214 if (!handle->GetSerial(&serial_utf16)) {
215 continue;
218 const std::string serial = base::UTF16ToUTF8(serial_utf16);
219 const GURL url("http://" + serial + "/claim");
220 const std::string form_data = base::StringPrintf(
221 "session_id=%s",
222 net::EscapeUrlEncodedData(session_id_, true).c_str());
223 const int response_code = SimplePOSTRequest(url, form_data);
225 if (response_code == 200) {
226 device_address_ = serial;
227 device_ = device;
228 break;
231 // The device is probably claimed by another process.
232 if (response_code != 403) {
233 LOG(WARNING) << "Unexpected HTTP " << response_code << " from /claim.";
238 std::string local_version;
239 std::string version;
240 if (!ReadLocalVersion(&local_version) ||
241 !GetVersion(&version)) {
242 return false;
245 if (version == local_version) {
246 return true;
249 return Update();
252 bool UsbTestGadgetImpl::GetVersion(std::string* version) {
253 Delegate delegate;
254 const GURL url("http://" + device_address_ + "/version");
255 scoped_ptr<net::URLFetcher> url_fetcher =
256 CreateURLFetcher(url, net::URLFetcher::GET, &delegate);
258 url_fetcher->Start();
259 delegate.WaitForCompletion();
261 const int response_code = url_fetcher->GetResponseCode();
262 if (response_code != 200) {
263 VLOG(2) << "Unexpected HTTP " << response_code << " from /version.";
264 return false;
267 STLClearObject(version);
268 if (!url_fetcher->GetResponseAsString(version)) {
269 VLOG(2) << "Failed to read body from /version.";
270 return false;
272 return true;
275 bool UsbTestGadgetImpl::Update() {
276 std::string version;
277 if (!ReadLocalVersion(&version)) {
278 return false;
280 LOG(INFO) << "Updating " << device_address_ << " to " << version << "...";
282 Delegate delegate;
283 const GURL url("http://" + device_address_ + "/update");
284 scoped_ptr<net::URLFetcher> url_fetcher =
285 CreateURLFetcher(url, net::URLFetcher::POST, &delegate);
287 const std::string mime_header =
288 base::StringPrintf(
289 "--foo\r\n"
290 "Content-Disposition: form-data; name=\"file\"; "
291 "filename=\"usb_gadget-%s.zip\"\r\n"
292 "Content-Type: application/octet-stream\r\n"
293 "\r\n", version.c_str());
294 const std::string mime_footer("\r\n--foo--\r\n");
296 std::string package;
297 if (!ReadLocalPackage(&package)) {
298 return false;
301 url_fetcher->SetUploadData("multipart/form-data; boundary=foo",
302 mime_header + package + mime_footer);
303 url_fetcher->Start();
304 delegate.WaitForCompletion();
306 const int response_code = url_fetcher->GetResponseCode();
307 if (response_code != 200) {
308 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /update.";
309 return false;
312 int retries = kUpdateRetries;
313 std::string new_version;
314 while (!GetVersion(&new_version) || new_version != version) {
315 if (--retries == 0) {
316 LOG(ERROR) << "Device not responding with new version.";
317 return false;
319 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
321 VLOG(1) << "It took " << (kUpdateRetries - retries)
322 << " retries to see the new version.";
324 // Release the old reference to the device and try to open a new one.
325 device_ = NULL;
326 retries = kReconnectRetries;
327 while (!FindClaimed()) {
328 if (--retries == 0) {
329 LOG(ERROR) << "Failed to find updated device.";
330 return false;
332 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
334 VLOG(1) << "It took " << (kReconnectRetries - retries)
335 << " retries to find the updated device.";
337 return true;
340 bool UsbTestGadgetImpl::FindClaimed() {
341 CHECK(!device_.get());
343 std::string expected_serial = GetSerial();
345 std::vector<scoped_refptr<UsbDevice> > devices;
346 usb_service_->GetDevices(&devices);
348 for (std::vector<scoped_refptr<UsbDevice> >::iterator iter =
349 devices.begin(); iter != devices.end(); ++iter) {
350 scoped_refptr<UsbDevice> &device = *iter;
352 if (device->vendor_id() == 0x18D1) {
353 const uint16 product_id = device->product_id();
354 bool found = false;
355 for (size_t i = 0; i < arraysize(kConfigurations); ++i) {
356 if (product_id == kConfigurations[i].product_id) {
357 found = true;
358 break;
361 if (!found) {
362 continue;
365 scoped_refptr<UsbDeviceHandle> handle(device->Open());
366 if (handle.get() == NULL) {
367 continue;
370 base::string16 serial_utf16;
371 if (!handle->GetSerial(&serial_utf16)) {
372 continue;
375 std::string serial = base::UTF16ToUTF8(serial_utf16);
376 if (serial != expected_serial) {
377 continue;
380 device_ = device;
381 return true;
385 return false;
388 bool UsbTestGadgetImpl::ReadLocalVersion(std::string* version) {
389 base::FilePath file_path;
390 CHECK(PathService::Get(base::DIR_EXE, &file_path));
391 file_path = file_path.AppendASCII("usb_gadget.zip.md5");
393 return ReadFile(file_path, version);
396 bool UsbTestGadgetImpl::ReadLocalPackage(std::string* package) {
397 base::FilePath file_path;
398 CHECK(PathService::Get(base::DIR_EXE, &file_path));
399 file_path = file_path.AppendASCII("usb_gadget.zip");
401 return ReadFile(file_path, package);
404 bool UsbTestGadgetImpl::ReadFile(const base::FilePath& file_path,
405 std::string* content) {
406 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
407 if (!file.IsValid()) {
408 LOG(ERROR) << "Cannot open " << file_path.MaybeAsASCII() << ": "
409 << base::File::ErrorToString(file.error_details());
410 return false;
413 STLClearObject(content);
414 int rv;
415 do {
416 char buf[4096];
417 rv = file.ReadAtCurrentPos(buf, sizeof buf);
418 if (rv == -1) {
419 LOG(ERROR) << "Cannot read " << file_path.MaybeAsASCII() << ": "
420 << base::File::ErrorToString(file.error_details());
421 return false;
423 content->append(buf, rv);
424 } while (rv > 0);
426 return true;
429 bool UsbTestGadgetImpl::Unclaim() {
430 VLOG(1) << "Releasing the device at " << device_address_ << ".";
432 const GURL url("http://" + device_address_ + "/unclaim");
433 const int response_code = SimplePOSTRequest(url, "");
435 if (response_code != 200) {
436 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /unclaim.";
437 return false;
439 return true;
442 bool UsbTestGadgetImpl::SetType(Type type) {
443 const struct UsbTestGadgetConfiguration* config = NULL;
444 for (size_t i = 0; i < arraysize(kConfigurations); ++i) {
445 if (kConfigurations[i].type == type) {
446 config = &kConfigurations[i];
449 CHECK(config);
451 const GURL url("http://" + device_address_ + config->http_resource);
452 const int response_code = SimplePOSTRequest(url, "");
454 if (response_code != 200) {
455 LOG(ERROR) << "Unexpected HTTP " << response_code
456 << " from " << config->http_resource << ".";
457 return false;
460 // Release the old reference to the device and try to open a new one.
461 int retries = kReconnectRetries;
462 while (true) {
463 device_ = NULL;
464 if (FindClaimed() && device_->product_id() == config->product_id) {
465 break;
467 if (--retries == 0) {
468 LOG(ERROR) << "Failed to find updated device.";
469 return false;
471 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
473 VLOG(1) << "It took " << (kReconnectRetries - retries)
474 << " retries to find the updated device.";
476 return true;
479 bool UsbTestGadgetImpl::Disconnect() {
480 const GURL url("http://" + device_address_ + "/disconnect");
481 const int response_code = SimplePOSTRequest(url, "");
483 if (response_code != 200) {
484 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /disconnect.";
485 return false;
488 // Release the old reference to the device and wait until it can't be found.
489 int retries = kDisconnectRetries;
490 while (true) {
491 device_ = NULL;
492 if (!FindClaimed()) {
493 break;
495 if (--retries == 0) {
496 LOG(ERROR) << "Device did not disconnect.";
497 return false;
499 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
501 VLOG(1) << "It took " << (kDisconnectRetries - retries)
502 << " retries for the device to disconnect.";
504 return true;
507 bool UsbTestGadgetImpl::Reconnect() {
508 const GURL url("http://" + device_address_ + "/reconnect");
509 const int response_code = SimplePOSTRequest(url, "");
511 if (response_code != 200) {
512 LOG(ERROR) << "Unexpected HTTP " << response_code << " from /reconnect.";
513 return false;
516 int retries = kDisconnectRetries;
517 while (true) {
518 if (FindClaimed()) {
519 break;
521 if (--retries == 0) {
522 LOG(ERROR) << "Device did not reconnect.";
523 return false;
525 PlatformThread::Sleep(TimeDelta::FromMilliseconds(kRetryPeriod));
527 VLOG(1) << "It took " << (kDisconnectRetries - retries)
528 << " retries for the device to reconnect.";
530 return true;
533 } // namespace device