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 "extensions/browser/api/hid/hid_api.h"
6
7#include <string>
8#include <vector>
9
10#include "device/core/device_client.h"
11#include "device/hid/hid_connection.h"
12#include "device/hid/hid_device_filter.h"
13#include "device/hid/hid_device_info.h"
14#include "device/hid/hid_service.h"
15#include "extensions/browser/api/api_resource_manager.h"
16#include "extensions/common/api/hid.h"
17#include "net/base/io_buffer.h"
18
19namespace hid = extensions::core_api::hid;
20
21using device::HidConnection;
22using device::HidDeviceFilter;
23using device::HidDeviceInfo;
24using device::HidService;
25
26namespace {
27
28const char kErrorPermissionDenied[] = "Permission to access device was denied.";
29const char kErrorInvalidDeviceId[] = "Invalid HID device ID.";
30const char kErrorFailedToOpenDevice[] = "Failed to open HID device.";
31const char kErrorConnectionNotFound[] = "Connection not established.";
32const char kErrorTransfer[] = "Transfer failed.";
33
34base::Value* PopulateHidConnection(int connection_id,
35                                   scoped_refptr<HidConnection> connection) {
36  hid::HidConnectInfo connection_value;
37  connection_value.connection_id = connection_id;
38  return connection_value.ToValue().release();
39}
40
41void ConvertHidDeviceFilter(linked_ptr<hid::DeviceFilter> input,
42                            HidDeviceFilter* output) {
43  if (input->vendor_id) {
44    output->SetVendorId(*input->vendor_id);
45  }
46  if (input->product_id) {
47    output->SetProductId(*input->product_id);
48  }
49  if (input->usage_page) {
50    output->SetUsagePage(*input->usage_page);
51  }
52  if (input->usage) {
53    output->SetUsage(*input->usage);
54  }
55}
56
57}  // namespace
58
59namespace extensions {
60
61HidAsyncApiFunction::HidAsyncApiFunction()
62    : device_manager_(NULL), connection_manager_(NULL) {}
63
64HidAsyncApiFunction::~HidAsyncApiFunction() {}
65
66bool HidAsyncApiFunction::PrePrepare() {
67#if defined(OS_MACOSX)
68  // Migration from FILE thread to UI thread. OS X gets it first.
69  set_work_thread_id(content::BrowserThread::UI);
70#else
71  // TODO(reillyg): Migrate Linux/CrOS and Windows as well.
72  set_work_thread_id(content::BrowserThread::FILE);
73#endif
74  device_manager_ = HidDeviceManager::Get(browser_context());
75  DCHECK(device_manager_);
76  connection_manager_ =
77      ApiResourceManager<HidConnectionResource>::Get(browser_context());
78  DCHECK(connection_manager_);
79  return true;
80}
81
82bool HidAsyncApiFunction::Respond() { return error_.empty(); }
83
84HidConnectionResource* HidAsyncApiFunction::GetHidConnectionResource(
85    int api_resource_id) {
86  return connection_manager_->Get(extension_->id(), api_resource_id);
87}
88
89void HidAsyncApiFunction::RemoveHidConnectionResource(int api_resource_id) {
90  connection_manager_->Remove(extension_->id(), api_resource_id);
91}
92
93void HidAsyncApiFunction::CompleteWithError(const std::string& error) {
94  SetError(error);
95  AsyncWorkCompleted();
96}
97
98HidGetDevicesFunction::HidGetDevicesFunction() {}
99
100HidGetDevicesFunction::~HidGetDevicesFunction() {}
101
102bool HidGetDevicesFunction::Prepare() {
103  parameters_ = hid::GetDevices::Params::Create(*args_);
104  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
105  return true;
106}
107
108void HidGetDevicesFunction::AsyncWorkStart() {
109  std::vector<HidDeviceFilter> filters;
110  if (parameters_->options.filters) {
111    filters.resize(parameters_->options.filters->size());
112    for (size_t i = 0; i < parameters_->options.filters->size(); ++i) {
113      ConvertHidDeviceFilter(parameters_->options.filters->at(i), &filters[i]);
114    }
115  }
116  if (parameters_->options.vendor_id) {
117    HidDeviceFilter legacy_filter;
118    legacy_filter.SetVendorId(*parameters_->options.vendor_id);
119    if (parameters_->options.product_id) {
120      legacy_filter.SetProductId(*parameters_->options.product_id);
121    }
122    filters.push_back(legacy_filter);
123  }
124
125  SetResult(device_manager_->GetApiDevices(extension(), filters).release());
126  AsyncWorkCompleted();
127}
128
129HidConnectFunction::HidConnectFunction() {}
130
131HidConnectFunction::~HidConnectFunction() {}
132
133bool HidConnectFunction::Prepare() {
134  parameters_ = hid::Connect::Params::Create(*args_);
135  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
136  return true;
137}
138
139void HidConnectFunction::AsyncWorkStart() {
140  device::HidDeviceInfo device_info;
141  if (!device_manager_->GetDeviceInfo(parameters_->device_id, &device_info)) {
142    CompleteWithError(kErrorInvalidDeviceId);
143    return;
144  }
145
146  if (!device_manager_->HasPermission(extension(), device_info)) {
147    LOG(WARNING) << "Insufficient permissions to access device.";
148    CompleteWithError(kErrorPermissionDenied);
149    return;
150  }
151
152  HidService* hid_service = device::DeviceClient::Get()->GetHidService();
153  DCHECK(hid_service);
154  scoped_refptr<HidConnection> connection =
155      hid_service->Connect(device_info.device_id);
156  if (!connection.get()) {
157    CompleteWithError(kErrorFailedToOpenDevice);
158    return;
159  }
160  int connection_id = connection_manager_->Add(
161      new HidConnectionResource(extension_->id(), connection));
162  SetResult(PopulateHidConnection(connection_id, connection));
163  AsyncWorkCompleted();
164}
165
166HidDisconnectFunction::HidDisconnectFunction() {}
167
168HidDisconnectFunction::~HidDisconnectFunction() {}
169
170bool HidDisconnectFunction::Prepare() {
171  parameters_ = hid::Disconnect::Params::Create(*args_);
172  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
173  return true;
174}
175
176void HidDisconnectFunction::AsyncWorkStart() {
177  int connection_id = parameters_->connection_id;
178  HidConnectionResource* resource =
179      connection_manager_->Get(extension_->id(), connection_id);
180  if (!resource) {
181    CompleteWithError(kErrorConnectionNotFound);
182    return;
183  }
184  connection_manager_->Remove(extension_->id(), connection_id);
185  AsyncWorkCompleted();
186}
187
188HidReceiveFunction::HidReceiveFunction() {}
189
190HidReceiveFunction::~HidReceiveFunction() {}
191
192bool HidReceiveFunction::Prepare() {
193  parameters_ = hid::Receive::Params::Create(*args_);
194  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
195  return true;
196}
197
198void HidReceiveFunction::AsyncWorkStart() {
199  int connection_id = parameters_->connection_id;
200  HidConnectionResource* resource =
201      connection_manager_->Get(extension_->id(), connection_id);
202  if (!resource) {
203    CompleteWithError(kErrorConnectionNotFound);
204    return;
205  }
206
207  scoped_refptr<device::HidConnection> connection = resource->connection();
208  connection->Read(base::Bind(&HidReceiveFunction::OnFinished, this));
209}
210
211void HidReceiveFunction::OnFinished(bool success,
212                                    scoped_refptr<net::IOBuffer> buffer,
213                                    size_t size) {
214  if (!success) {
215    CompleteWithError(kErrorTransfer);
216    return;
217  }
218
219  DCHECK_GE(size, 1u);
220  int report_id = reinterpret_cast<uint8_t*>(buffer->data())[0];
221
222  scoped_ptr<base::ListValue> result(new base::ListValue());
223  result->Append(new base::FundamentalValue(report_id));
224  result->Append(
225      base::BinaryValue::CreateWithCopiedBuffer(buffer->data() + 1, size - 1));
226  SetResultList(result.Pass());
227  AsyncWorkCompleted();
228}
229
230HidSendFunction::HidSendFunction() {}
231
232HidSendFunction::~HidSendFunction() {}
233
234bool HidSendFunction::Prepare() {
235  parameters_ = hid::Send::Params::Create(*args_);
236  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
237  return true;
238}
239
240void HidSendFunction::AsyncWorkStart() {
241  int connection_id = parameters_->connection_id;
242  HidConnectionResource* resource =
243      connection_manager_->Get(extension_->id(), connection_id);
244  if (!resource) {
245    CompleteWithError(kErrorConnectionNotFound);
246    return;
247  }
248
249  scoped_refptr<net::IOBufferWithSize> buffer(
250      new net::IOBufferWithSize(parameters_->data.size() + 1));
251  buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
252  memcpy(
253      buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
254  resource->connection()->Write(
255      buffer, buffer->size(), base::Bind(&HidSendFunction::OnFinished, this));
256}
257
258void HidSendFunction::OnFinished(bool success) {
259  if (!success) {
260    CompleteWithError(kErrorTransfer);
261    return;
262  }
263  AsyncWorkCompleted();
264}
265
266HidReceiveFeatureReportFunction::HidReceiveFeatureReportFunction() {}
267
268HidReceiveFeatureReportFunction::~HidReceiveFeatureReportFunction() {}
269
270bool HidReceiveFeatureReportFunction::Prepare() {
271  parameters_ = hid::ReceiveFeatureReport::Params::Create(*args_);
272  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
273  return true;
274}
275
276void HidReceiveFeatureReportFunction::AsyncWorkStart() {
277  int connection_id = parameters_->connection_id;
278  HidConnectionResource* resource =
279      connection_manager_->Get(extension_->id(), connection_id);
280  if (!resource) {
281    CompleteWithError(kErrorConnectionNotFound);
282    return;
283  }
284
285  scoped_refptr<device::HidConnection> connection = resource->connection();
286  connection->GetFeatureReport(
287      static_cast<uint8_t>(parameters_->report_id),
288      base::Bind(&HidReceiveFeatureReportFunction::OnFinished, this));
289}
290
291void HidReceiveFeatureReportFunction::OnFinished(
292    bool success,
293    scoped_refptr<net::IOBuffer> buffer,
294    size_t size) {
295  if (!success) {
296    CompleteWithError(kErrorTransfer);
297    return;
298  }
299
300  SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer->data(), size));
301  AsyncWorkCompleted();
302}
303
304HidSendFeatureReportFunction::HidSendFeatureReportFunction() {}
305
306HidSendFeatureReportFunction::~HidSendFeatureReportFunction() {}
307
308bool HidSendFeatureReportFunction::Prepare() {
309  parameters_ = hid::SendFeatureReport::Params::Create(*args_);
310  EXTENSION_FUNCTION_VALIDATE(parameters_.get());
311  return true;
312}
313
314void HidSendFeatureReportFunction::AsyncWorkStart() {
315  int connection_id = parameters_->connection_id;
316  HidConnectionResource* resource =
317      connection_manager_->Get(extension_->id(), connection_id);
318  if (!resource) {
319    CompleteWithError(kErrorConnectionNotFound);
320    return;
321  }
322
323  scoped_refptr<net::IOBufferWithSize> buffer(
324      new net::IOBufferWithSize(parameters_->data.size() + 1));
325  buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id);
326  memcpy(
327      buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size());
328  resource->connection()->SendFeatureReport(
329      buffer,
330      buffer->size(),
331      base::Bind(&HidSendFeatureReportFunction::OnFinished, this));
332}
333
334void HidSendFeatureReportFunction::OnFinished(bool success) {
335  if (!success) {
336    CompleteWithError(kErrorTransfer);
337    return;
338  }
339  AsyncWorkCompleted();
340}
341
342}  // namespace extensions
343