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 |