Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: device/test/usb_test_gadget_impl.cc

Issue 980023002: Move device/usb classes from the FILE thread to UI thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Allow USB transfer calls from any thread again. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698