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.
4
5 #include "services/device/usb/usb_device_handle.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10
11 #include "base/bind.h"
12 #include "base/memory/ref_counted_memory.h"
13 #include "base/run_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/task_environment.h"
16 #include "base/test/test_io_thread.h"
17 #include "services/device/test/usb_test_gadget.h"
18 #include "services/device/usb/usb_device.h"
19 #include "services/device/usb/usb_service.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 namespace device {
23
24 using mojom::UsbControlTransferRecipient;
25 using mojom::UsbControlTransferType;
26 using mojom::UsbTransferDirection;
27 using mojom::UsbTransferStatus;
28
29 namespace {
30
31 class UsbDeviceHandleTest : public ::testing::Test {
32 public:
UsbDeviceHandleTest()33 UsbDeviceHandleTest()
34 : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
35 usb_service_(UsbService::Create()),
36 io_thread_(base::TestIOThread::kAutoStart) {}
37
38 protected:
39 base::test::TaskEnvironment task_environment_;
40 std::unique_ptr<UsbService> usb_service_;
41 base::TestIOThread io_thread_;
42 };
43
44 class TestOpenCallback {
45 public:
46 TestOpenCallback() = default;
47
WaitForResult()48 scoped_refptr<UsbDeviceHandle> WaitForResult() {
49 run_loop_.Run();
50 return device_handle_;
51 }
52
GetCallback()53 UsbDevice::OpenCallback GetCallback() {
54 return base::BindOnce(&TestOpenCallback::SetResult, base::Unretained(this));
55 }
56
57 private:
SetResult(scoped_refptr<UsbDeviceHandle> device_handle)58 void SetResult(scoped_refptr<UsbDeviceHandle> device_handle) {
59 device_handle_ = device_handle;
60 run_loop_.Quit();
61 }
62
63 base::RunLoop run_loop_;
64 scoped_refptr<UsbDeviceHandle> device_handle_;
65 };
66
67 class TestResultCallback {
68 public:
69 TestResultCallback() = default;
70
WaitForResult()71 bool WaitForResult() {
72 run_loop_.Run();
73 return success_;
74 }
75
GetCallback()76 UsbDeviceHandle::ResultCallback GetCallback() {
77 return base::BindOnce(&TestResultCallback::SetResult,
78 base::Unretained(this));
79 }
80
81 private:
SetResult(bool success)82 void SetResult(bool success) {
83 success_ = success;
84 run_loop_.Quit();
85 }
86
87 base::RunLoop run_loop_;
88 bool success_;
89 };
90
91 class TestCompletionCallback {
92 public:
93 TestCompletionCallback() = default;
94
WaitForResult()95 void WaitForResult() { run_loop_.Run(); }
96
GetCallback()97 UsbDeviceHandle::TransferCallback GetCallback() {
98 return base::BindOnce(&TestCompletionCallback::SetResult,
99 base::Unretained(this));
100 }
status() const101 UsbTransferStatus status() const { return status_; }
transferred() const102 size_t transferred() const { return transferred_; }
103
104 private:
SetResult(UsbTransferStatus status,scoped_refptr<base::RefCountedBytes> buffer,size_t transferred)105 void SetResult(UsbTransferStatus status,
106 scoped_refptr<base::RefCountedBytes> buffer,
107 size_t transferred) {
108 status_ = status;
109 transferred_ = transferred;
110 run_loop_.Quit();
111 }
112
113 base::RunLoop run_loop_;
114 UsbTransferStatus status_;
115 size_t transferred_;
116 };
117
ExpectTimeoutAndClose(scoped_refptr<UsbDeviceHandle> handle,base::OnceClosure quit_closure,UsbTransferStatus status,scoped_refptr<base::RefCountedBytes> buffer,size_t transferred)118 void ExpectTimeoutAndClose(scoped_refptr<UsbDeviceHandle> handle,
119 base::OnceClosure quit_closure,
120 UsbTransferStatus status,
121 scoped_refptr<base::RefCountedBytes> buffer,
122 size_t transferred) {
123 EXPECT_EQ(UsbTransferStatus::TIMEOUT, status);
124 handle->Close();
125 std::move(quit_closure).Run();
126 }
127
TEST_F(UsbDeviceHandleTest,InterruptTransfer)128 TEST_F(UsbDeviceHandleTest, InterruptTransfer) {
129 if (!UsbTestGadget::IsTestEnabled()) {
130 return;
131 }
132
133 std::unique_ptr<UsbTestGadget> gadget =
134 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
135 ASSERT_TRUE(gadget.get());
136 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
137
138 TestOpenCallback open_device;
139 gadget->GetDevice()->Open(open_device.GetCallback());
140 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
141 ASSERT_TRUE(handle.get());
142
143 TestResultCallback claim_interface;
144 handle->ClaimInterface(0, claim_interface.GetCallback());
145 ASSERT_TRUE(claim_interface.WaitForResult());
146
147 const mojom::UsbInterfaceInfo* interface =
148 handle->FindInterfaceByEndpoint(0x81);
149 EXPECT_TRUE(interface);
150 EXPECT_EQ(0, interface->interface_number);
151 interface = handle->FindInterfaceByEndpoint(0x01);
152 EXPECT_TRUE(interface);
153 EXPECT_EQ(0, interface->interface_number);
154 EXPECT_FALSE(handle->FindInterfaceByEndpoint(0x82));
155 EXPECT_FALSE(handle->FindInterfaceByEndpoint(0x02));
156
157 auto in_buffer = base::MakeRefCounted<base::RefCountedBytes>(64);
158 TestCompletionCallback in_completion;
159 handle->GenericTransfer(UsbTransferDirection::INBOUND, 0x81, in_buffer,
160 5000, // 5 second timeout
161 in_completion.GetCallback());
162
163 auto out_buffer =
164 base::MakeRefCounted<base::RefCountedBytes>(in_buffer->size());
165 TestCompletionCallback out_completion;
166 for (size_t i = 0; i < out_buffer->size(); ++i) {
167 out_buffer->data()[i] = i;
168 }
169
170 handle->GenericTransfer(UsbTransferDirection::OUTBOUND, 0x01, out_buffer,
171 5000, // 5 second timeout
172 out_completion.GetCallback());
173 out_completion.WaitForResult();
174 ASSERT_EQ(UsbTransferStatus::COMPLETED, out_completion.status());
175 EXPECT_EQ(static_cast<size_t>(out_buffer->size()),
176 out_completion.transferred());
177
178 in_completion.WaitForResult();
179 ASSERT_EQ(UsbTransferStatus::COMPLETED, in_completion.status());
180 EXPECT_EQ(static_cast<size_t>(in_buffer->size()),
181 in_completion.transferred());
182 for (size_t i = 0; i < in_completion.transferred(); ++i) {
183 EXPECT_EQ(out_buffer->front()[i], in_buffer->front()[i])
184 << "Mismatch at index " << i << ".";
185 }
186
187 TestResultCallback release_interface;
188 handle->ReleaseInterface(0, release_interface.GetCallback());
189 ASSERT_TRUE(release_interface.WaitForResult());
190
191 handle->Close();
192 }
193
TEST_F(UsbDeviceHandleTest,BulkTransfer)194 TEST_F(UsbDeviceHandleTest, BulkTransfer) {
195 if (!UsbTestGadget::IsTestEnabled()) {
196 return;
197 }
198
199 std::unique_ptr<UsbTestGadget> gadget =
200 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
201 ASSERT_TRUE(gadget.get());
202 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
203
204 TestOpenCallback open_device;
205 gadget->GetDevice()->Open(open_device.GetCallback());
206 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
207 ASSERT_TRUE(handle.get());
208
209 TestResultCallback claim_interface;
210 handle->ClaimInterface(1, claim_interface.GetCallback());
211 ASSERT_TRUE(claim_interface.WaitForResult());
212
213 EXPECT_FALSE(handle->FindInterfaceByEndpoint(0x81));
214 EXPECT_FALSE(handle->FindInterfaceByEndpoint(0x01));
215 const mojom::UsbInterfaceInfo* interface =
216 handle->FindInterfaceByEndpoint(0x82);
217 EXPECT_TRUE(interface);
218 EXPECT_EQ(1, interface->interface_number);
219 interface = handle->FindInterfaceByEndpoint(0x02);
220 EXPECT_TRUE(interface);
221 EXPECT_EQ(1, interface->interface_number);
222
223 auto in_buffer = base::MakeRefCounted<base::RefCountedBytes>(512);
224 TestCompletionCallback in_completion;
225 handle->GenericTransfer(UsbTransferDirection::INBOUND, 0x82, in_buffer,
226 5000, // 5 second timeout
227 in_completion.GetCallback());
228
229 auto out_buffer =
230 base::MakeRefCounted<base::RefCountedBytes>(in_buffer->size());
231 TestCompletionCallback out_completion;
232 for (size_t i = 0; i < out_buffer->size(); ++i) {
233 out_buffer->data()[i] = i;
234 }
235
236 handle->GenericTransfer(UsbTransferDirection::OUTBOUND, 0x02, out_buffer,
237 5000, // 5 second timeout
238 out_completion.GetCallback());
239 out_completion.WaitForResult();
240 ASSERT_EQ(UsbTransferStatus::COMPLETED, out_completion.status());
241 EXPECT_EQ(static_cast<size_t>(out_buffer->size()),
242 out_completion.transferred());
243
244 in_completion.WaitForResult();
245 ASSERT_EQ(UsbTransferStatus::COMPLETED, in_completion.status());
246 EXPECT_EQ(static_cast<size_t>(in_buffer->size()),
247 in_completion.transferred());
248 for (size_t i = 0; i < in_completion.transferred(); ++i) {
249 EXPECT_EQ(out_buffer->front()[i], in_buffer->front()[i])
250 << "Mismatch at index " << i << ".";
251 }
252
253 TestResultCallback release_interface;
254 handle->ReleaseInterface(1, release_interface.GetCallback());
255 ASSERT_TRUE(release_interface.WaitForResult());
256
257 handle->Close();
258 }
259
TEST_F(UsbDeviceHandleTest,ControlTransfer)260 TEST_F(UsbDeviceHandleTest, ControlTransfer) {
261 if (!UsbTestGadget::IsTestEnabled())
262 return;
263
264 std::unique_ptr<UsbTestGadget> gadget =
265 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
266 ASSERT_TRUE(gadget.get());
267
268 TestOpenCallback open_device;
269 gadget->GetDevice()->Open(open_device.GetCallback());
270 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
271 ASSERT_TRUE(handle.get());
272
273 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(255);
274 TestCompletionCallback completion;
275 handle->ControlTransfer(UsbTransferDirection::INBOUND,
276 UsbControlTransferType::STANDARD,
277 UsbControlTransferRecipient::DEVICE, 0x06, 0x0301,
278 0x0409, buffer, 0, completion.GetCallback());
279 completion.WaitForResult();
280 ASSERT_EQ(UsbTransferStatus::COMPLETED, completion.status());
281 const char expected_str[] = "\x18\x03G\0o\0o\0g\0l\0e\0 \0I\0n\0c\0.\0";
282 EXPECT_EQ(sizeof(expected_str) - 1, completion.transferred());
283 for (size_t i = 0; i < completion.transferred(); ++i) {
284 EXPECT_EQ(expected_str[i], buffer->front()[i])
285 << "Mismatch at index " << i << ".";
286 }
287
288 handle->Close();
289 }
290
TEST_F(UsbDeviceHandleTest,SetInterfaceAlternateSetting)291 TEST_F(UsbDeviceHandleTest, SetInterfaceAlternateSetting) {
292 if (!UsbTestGadget::IsTestEnabled()) {
293 return;
294 }
295
296 std::unique_ptr<UsbTestGadget> gadget =
297 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
298 ASSERT_TRUE(gadget.get());
299 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
300
301 TestOpenCallback open_device;
302 gadget->GetDevice()->Open(open_device.GetCallback());
303 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
304 ASSERT_TRUE(handle.get());
305
306 TestResultCallback claim_interface;
307 handle->ClaimInterface(2, claim_interface.GetCallback());
308 ASSERT_TRUE(claim_interface.WaitForResult());
309
310 TestResultCallback set_interface;
311 handle->SetInterfaceAlternateSetting(2, 1, set_interface.GetCallback());
312 ASSERT_TRUE(set_interface.WaitForResult());
313
314 TestResultCallback release_interface;
315 handle->ReleaseInterface(2, release_interface.GetCallback());
316 ASSERT_TRUE(release_interface.WaitForResult());
317
318 handle->Close();
319 }
320
TEST_F(UsbDeviceHandleTest,CancelOnClose)321 TEST_F(UsbDeviceHandleTest, CancelOnClose) {
322 if (!UsbTestGadget::IsTestEnabled()) {
323 return;
324 }
325
326 std::unique_ptr<UsbTestGadget> gadget =
327 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
328 ASSERT_TRUE(gadget.get());
329 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
330
331 TestOpenCallback open_device;
332 gadget->GetDevice()->Open(open_device.GetCallback());
333 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
334 ASSERT_TRUE(handle.get());
335
336 TestResultCallback claim_interface;
337 handle->ClaimInterface(1, claim_interface.GetCallback());
338 ASSERT_TRUE(claim_interface.WaitForResult());
339
340 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(512);
341 TestCompletionCallback completion;
342 handle->GenericTransfer(UsbTransferDirection::INBOUND, 0x82, buffer,
343 5000, // 5 second timeout
344 completion.GetCallback());
345
346 handle->Close();
347 completion.WaitForResult();
348 ASSERT_EQ(UsbTransferStatus::CANCELLED, completion.status());
349 }
350
TEST_F(UsbDeviceHandleTest,ErrorOnDisconnect)351 TEST_F(UsbDeviceHandleTest, ErrorOnDisconnect) {
352 if (!UsbTestGadget::IsTestEnabled()) {
353 return;
354 }
355
356 std::unique_ptr<UsbTestGadget> gadget =
357 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
358 ASSERT_TRUE(gadget.get());
359 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
360
361 TestOpenCallback open_device;
362 gadget->GetDevice()->Open(open_device.GetCallback());
363 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
364 ASSERT_TRUE(handle.get());
365
366 TestResultCallback claim_interface;
367 handle->ClaimInterface(1, claim_interface.GetCallback());
368 ASSERT_TRUE(claim_interface.WaitForResult());
369
370 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(512);
371 TestCompletionCallback completion;
372 handle->GenericTransfer(UsbTransferDirection::INBOUND, 0x82, buffer,
373 5000, // 5 second timeout
374 completion.GetCallback());
375
376 ASSERT_TRUE(gadget->Disconnect());
377 completion.WaitForResult();
378 // Depending on timing the transfer can be cancelled by the disconnection, be
379 // rejected because the device is already missing or result in another generic
380 // error as the device drops off the bus.
381 EXPECT_TRUE(completion.status() == UsbTransferStatus::CANCELLED ||
382 completion.status() == UsbTransferStatus::DISCONNECT ||
383 completion.status() == UsbTransferStatus::TRANSFER_ERROR);
384
385 handle->Close();
386 }
387
TEST_F(UsbDeviceHandleTest,Timeout)388 TEST_F(UsbDeviceHandleTest, Timeout) {
389 if (!UsbTestGadget::IsTestEnabled()) {
390 return;
391 }
392
393 std::unique_ptr<UsbTestGadget> gadget =
394 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
395 ASSERT_TRUE(gadget.get());
396 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
397
398 TestOpenCallback open_device;
399 gadget->GetDevice()->Open(open_device.GetCallback());
400 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
401 ASSERT_TRUE(handle.get());
402
403 TestResultCallback claim_interface;
404 handle->ClaimInterface(1, claim_interface.GetCallback());
405 ASSERT_TRUE(claim_interface.WaitForResult());
406
407 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(512);
408 TestCompletionCallback completion;
409 handle->GenericTransfer(UsbTransferDirection::INBOUND, 0x82, buffer,
410 10, // 10 millisecond timeout
411 completion.GetCallback());
412
413 completion.WaitForResult();
414 ASSERT_EQ(UsbTransferStatus::TIMEOUT, completion.status());
415
416 handle->Close();
417 }
418
TEST_F(UsbDeviceHandleTest,CloseReentrancy)419 TEST_F(UsbDeviceHandleTest, CloseReentrancy) {
420 if (!UsbTestGadget::IsTestEnabled())
421 return;
422
423 std::unique_ptr<UsbTestGadget> gadget =
424 UsbTestGadget::Claim(usb_service_.get(), io_thread_.task_runner());
425 ASSERT_TRUE(gadget.get());
426 ASSERT_TRUE(gadget->SetType(UsbTestGadget::ECHO));
427
428 TestOpenCallback open_device;
429 gadget->GetDevice()->Open(open_device.GetCallback());
430 scoped_refptr<UsbDeviceHandle> handle = open_device.WaitForResult();
431 ASSERT_TRUE(handle.get());
432
433 TestResultCallback claim_interface;
434 handle->ClaimInterface(1, claim_interface.GetCallback());
435 ASSERT_TRUE(claim_interface.WaitForResult());
436
437 base::RunLoop run_loop;
438 auto buffer = base::MakeRefCounted<base::RefCountedBytes>(512);
439 handle->GenericTransfer(
440 UsbTransferDirection::INBOUND, 0x82, buffer,
441 10, // 10 millisecond timeout
442 base::BindOnce(&ExpectTimeoutAndClose, handle, run_loop.QuitClosure()));
443 // Drop handle so that the completion callback holds the last reference.
444 handle = nullptr;
445 run_loop.Run();
446 }
447
448 } // namespace
449
450 } // namespace device
451