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