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.
6 #include "base/run_loop.h"
7 #include "base/thread_task_runner_handle.h"
8 #include "device/hid/hid_collection_info.h"
9 #include "device/hid/hid_connection.h"
10 #include "device/hid/hid_device_info.h"
11 #include "device/hid/hid_service.h"
12 #include "device/hid/hid_usage_and_page.h"
13 #include "extensions/shell/test/shell_apitest.h"
14 #include "extensions/test/extension_test_message_listener.h"
15 #include "net/base/io_buffer.h"
17 using base::ThreadTaskRunnerHandle
;
18 using device::HidCollectionInfo
;
19 using device::HidConnection
;
20 using device::HidDeviceId
;
21 using device::HidDeviceInfo
;
22 using device::HidService
;
23 using device::HidUsageAndPage
;
28 // These report descriptors define two devices with 8-byte input, output and
29 // feature reports. The first implements usage page 0xFF00 and has a single
30 // report without and ID. The second implements usage page 0xFF01 and has a
31 // single report with ID 1.
32 const uint8 kReportDescriptor
[] = {0x06, 0x00, 0xFF, 0x08, 0xA1, 0x01, 0x15,
33 0x00, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95,
34 0x08, 0x08, 0x81, 0x02, 0x08, 0x91, 0x02,
35 0x08, 0xB1, 0x02, 0xC0};
36 const uint8 kReportDescriptorWithIDs
[] = {
37 0x06, 0x01, 0xFF, 0x08, 0xA1, 0x01, 0x15, 0x00, 0x26,
38 0xFF, 0x00, 0x85, 0x01, 0x75, 0x08, 0x95, 0x08, 0x08,
39 0x81, 0x02, 0x08, 0x91, 0x02, 0x08, 0xB1, 0x02, 0xC0};
41 class MockHidConnection
: public HidConnection
{
43 MockHidConnection(scoped_refptr
<HidDeviceInfo
> device_info
)
44 : HidConnection(device_info
) {}
46 void PlatformClose() override
{}
48 void PlatformRead(const ReadCallback
& callback
) override
{
49 const char kResult
[] = "This is a HID input report.";
50 uint8_t report_id
= device_info()->has_report_id() ? 1 : 0;
51 scoped_refptr
<IOBuffer
> buffer(new IOBuffer(sizeof(kResult
)));
52 buffer
->data()[0] = report_id
;
53 memcpy(buffer
->data() + 1, kResult
, sizeof(kResult
) - 1);
54 ThreadTaskRunnerHandle::Get()->PostTask(
55 FROM_HERE
, base::Bind(callback
, true, buffer
, sizeof(kResult
)));
58 void PlatformWrite(scoped_refptr
<net::IOBuffer
> buffer
,
60 const WriteCallback
& callback
) override
{
61 const char kExpected
[] = "o-report"; // 8 bytes
63 if (size
== sizeof(kExpected
)) {
64 uint8_t report_id
= buffer
->data()[0];
65 uint8_t expected_report_id
= device_info()->has_report_id() ? 1 : 0;
66 if (report_id
== expected_report_id
) {
67 if (memcmp(buffer
->data() + 1, kExpected
, sizeof(kExpected
) - 1) == 0) {
72 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
73 base::Bind(callback
, result
));
76 void PlatformGetFeatureReport(uint8_t report_id
,
77 const ReadCallback
& callback
) override
{
78 const char kResult
[] = "This is a HID feature report.";
79 scoped_refptr
<IOBuffer
> buffer(new IOBuffer(sizeof(kResult
)));
81 if (device_info()->has_report_id()) {
82 buffer
->data()[offset
++] = report_id
;
84 memcpy(buffer
->data() + offset
, kResult
, sizeof(kResult
) - 1);
85 ThreadTaskRunnerHandle::Get()->PostTask(
87 base::Bind(callback
, true, buffer
, sizeof(kResult
) - 1 + offset
));
90 void PlatformSendFeatureReport(scoped_refptr
<net::IOBuffer
> buffer
,
92 const WriteCallback
& callback
) override
{
93 const char kExpected
[] = "The app is setting this HID feature report.";
95 if (size
== sizeof(kExpected
)) {
96 uint8_t report_id
= buffer
->data()[0];
97 uint8_t expected_report_id
= device_info()->has_report_id() ? 1 : 0;
98 if (report_id
== expected_report_id
&&
99 memcmp(buffer
->data() + 1, kExpected
, sizeof(kExpected
) - 1) == 0) {
103 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
104 base::Bind(callback
, result
));
108 ~MockHidConnection() override
{}
111 class MockHidService
: public HidService
{
113 MockHidService() : HidService() {
114 // Verify that devices are enumerated properly even when the first
115 // enumeration happens asynchronously.
116 ThreadTaskRunnerHandle::Get()->PostTask(
117 FROM_HERE
, base::Bind(&MockHidService::LazyFirstEnumeration
,
118 base::Unretained(this)));
121 void Connect(const HidDeviceId
& device_id
,
122 const ConnectCallback
& callback
) override
{
123 const auto& device_entry
= devices().find(device_id
);
124 scoped_refptr
<HidConnection
> connection
;
125 if (device_entry
!= devices().end()) {
126 connection
= new MockHidConnection(device_entry
->second
);
129 ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
130 base::Bind(callback
, connection
));
133 void LazyFirstEnumeration() {
134 AddDevice("A", 0x18D1, 0x58F0, false);
135 AddDevice("B", 0x18D1, 0x58F0, true);
136 AddDevice("C", 0x18D1, 0x58F1, false);
137 FirstEnumerationComplete();
140 void AddDevice(const std::string
& device_id
,
144 std::vector
<uint8
> report_descriptor
;
146 report_descriptor
.insert(
147 report_descriptor
.begin(), kReportDescriptorWithIDs
,
148 kReportDescriptorWithIDs
+ sizeof(kReportDescriptorWithIDs
));
150 report_descriptor
.insert(report_descriptor
.begin(), kReportDescriptor
,
151 kReportDescriptor
+ sizeof(kReportDescriptor
));
153 HidService::AddDevice(new HidDeviceInfo(device_id
, vendor_id
, product_id
,
154 "", "", kHIDBusTypeUSB
,
158 void RemoveDevice(const std::string
& device_id
) {
159 HidService::RemoveDevice(device_id
);
163 } // namespace device
165 namespace extensions
{
167 class HidApiTest
: public ShellApiTest
{
169 void SetUpOnMainThread() override
{
170 ShellApiTest::SetUpOnMainThread();
171 hid_service_
= new device::MockHidService();
172 HidService::SetInstanceForTest(hid_service_
);
176 device::MockHidService
* hid_service_
;
179 IN_PROC_BROWSER_TEST_F(HidApiTest
, HidApp
) {
180 ASSERT_TRUE(RunAppTest("api_test/hid/api")) << message_
;
183 IN_PROC_BROWSER_TEST_F(HidApiTest
, OnDeviceAdded
) {
184 ExtensionTestMessageListener
load_listener("loaded", false);
185 ExtensionTestMessageListener
result_listener("success", false);
186 result_listener
.set_failure_message("failure");
188 ASSERT_TRUE(LoadApp("api_test/hid/add_event"));
189 ASSERT_TRUE(load_listener
.WaitUntilSatisfied());
191 // Add a blocked device first so that the test will fail if a notification is
193 hid_service_
->AddDevice("D", 0x18D1, 0x58F1, false);
194 hid_service_
->AddDevice("E", 0x18D1, 0x58F0, false);
195 ASSERT_TRUE(result_listener
.WaitUntilSatisfied());
196 EXPECT_EQ("success", result_listener
.message());
199 IN_PROC_BROWSER_TEST_F(HidApiTest
, OnDeviceRemoved
) {
200 ExtensionTestMessageListener
load_listener("loaded", false);
201 ExtensionTestMessageListener
result_listener("success", false);
202 result_listener
.set_failure_message("failure");
204 ASSERT_TRUE(LoadApp("api_test/hid/remove_event"));
205 ASSERT_TRUE(load_listener
.WaitUntilSatisfied());
207 // Device C was not returned by chrome.hid.getDevices, the app will not get
209 hid_service_
->RemoveDevice("C");
210 // Device A was returned, the app will get a notification.
211 hid_service_
->RemoveDevice("A");
212 ASSERT_TRUE(result_listener
.WaitUntilSatisfied());
213 EXPECT_EQ("success", result_listener
.message());
216 } // namespace extensions