// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "extensions/browser/api/hid/hid_api.h" #include <string> #include <vector> #include "device/core/device_client.h" #include "device/hid/hid_connection.h" #include "device/hid/hid_device_filter.h" #include "device/hid/hid_device_info.h" #include "device/hid/hid_service.h" #include "extensions/browser/api/api_resource_manager.h" #include "extensions/common/api/hid.h" #include "net/base/io_buffer.h" namespace hid = extensions::core_api::hid; using device::HidConnection; using device::HidDeviceFilter; using device::HidDeviceInfo; using device::HidService; namespace { const char kErrorPermissionDenied[] = "Permission to access device was denied."; const char kErrorInvalidDeviceId[] = "Invalid HID device ID."; const char kErrorFailedToOpenDevice[] = "Failed to open HID device."; const char kErrorConnectionNotFound[] = "Connection not established."; const char kErrorTransfer[] = "Transfer failed."; base::Value* PopulateHidConnection(int connection_id, scoped_refptr<HidConnection> connection) { hid::HidConnectInfo connection_value; connection_value.connection_id = connection_id; return connection_value.ToValue().release(); } void ConvertHidDeviceFilter(linked_ptr<hid::DeviceFilter> input, HidDeviceFilter* output) { if (input->vendor_id) { output->SetVendorId(*input->vendor_id); } if (input->product_id) { output->SetProductId(*input->product_id); } if (input->usage_page) { output->SetUsagePage(*input->usage_page); } if (input->usage) { output->SetUsage(*input->usage); } } } // namespace namespace extensions { HidAsyncApiFunction::HidAsyncApiFunction() : device_manager_(NULL), connection_manager_(NULL) {} HidAsyncApiFunction::~HidAsyncApiFunction() {} bool HidAsyncApiFunction::PrePrepare() { #if defined(OS_MACOSX) // Migration from FILE thread to UI thread. OS X gets it first. set_work_thread_id(content::BrowserThread::UI); #else // TODO(reillyg): Migrate Linux/CrOS and Windows as well. set_work_thread_id(content::BrowserThread::FILE); #endif device_manager_ = HidDeviceManager::Get(browser_context()); DCHECK(device_manager_); connection_manager_ = ApiResourceManager<HidConnectionResource>::Get(browser_context()); DCHECK(connection_manager_); return true; } bool HidAsyncApiFunction::Respond() { return error_.empty(); } HidConnectionResource* HidAsyncApiFunction::GetHidConnectionResource( int api_resource_id) { return connection_manager_->Get(extension_->id(), api_resource_id); } void HidAsyncApiFunction::RemoveHidConnectionResource(int api_resource_id) { connection_manager_->Remove(extension_->id(), api_resource_id); } void HidAsyncApiFunction::CompleteWithError(const std::string& error) { SetError(error); AsyncWorkCompleted(); } HidGetDevicesFunction::HidGetDevicesFunction() {} HidGetDevicesFunction::~HidGetDevicesFunction() {} bool HidGetDevicesFunction::Prepare() { parameters_ = hid::GetDevices::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidGetDevicesFunction::AsyncWorkStart() { std::vector<HidDeviceFilter> filters; if (parameters_->options.filters) { filters.resize(parameters_->options.filters->size()); for (size_t i = 0; i < parameters_->options.filters->size(); ++i) { ConvertHidDeviceFilter(parameters_->options.filters->at(i), &filters[i]); } } if (parameters_->options.vendor_id) { HidDeviceFilter legacy_filter; legacy_filter.SetVendorId(*parameters_->options.vendor_id); if (parameters_->options.product_id) { legacy_filter.SetProductId(*parameters_->options.product_id); } filters.push_back(legacy_filter); } SetResult(device_manager_->GetApiDevices(extension(), filters).release()); AsyncWorkCompleted(); } HidConnectFunction::HidConnectFunction() {} HidConnectFunction::~HidConnectFunction() {} bool HidConnectFunction::Prepare() { parameters_ = hid::Connect::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidConnectFunction::AsyncWorkStart() { device::HidDeviceInfo device_info; if (!device_manager_->GetDeviceInfo(parameters_->device_id, &device_info)) { CompleteWithError(kErrorInvalidDeviceId); return; } if (!device_manager_->HasPermission(extension(), device_info)) { LOG(WARNING) << "Insufficient permissions to access device."; CompleteWithError(kErrorPermissionDenied); return; } HidService* hid_service = device::DeviceClient::Get()->GetHidService(); DCHECK(hid_service); scoped_refptr<HidConnection> connection = hid_service->Connect(device_info.device_id); if (!connection.get()) { CompleteWithError(kErrorFailedToOpenDevice); return; } int connection_id = connection_manager_->Add( new HidConnectionResource(extension_->id(), connection)); SetResult(PopulateHidConnection(connection_id, connection)); AsyncWorkCompleted(); } HidDisconnectFunction::HidDisconnectFunction() {} HidDisconnectFunction::~HidDisconnectFunction() {} bool HidDisconnectFunction::Prepare() { parameters_ = hid::Disconnect::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidDisconnectFunction::AsyncWorkStart() { int connection_id = parameters_->connection_id; HidConnectionResource* resource = connection_manager_->Get(extension_->id(), connection_id); if (!resource) { CompleteWithError(kErrorConnectionNotFound); return; } connection_manager_->Remove(extension_->id(), connection_id); AsyncWorkCompleted(); } HidReceiveFunction::HidReceiveFunction() {} HidReceiveFunction::~HidReceiveFunction() {} bool HidReceiveFunction::Prepare() { parameters_ = hid::Receive::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidReceiveFunction::AsyncWorkStart() { int connection_id = parameters_->connection_id; HidConnectionResource* resource = connection_manager_->Get(extension_->id(), connection_id); if (!resource) { CompleteWithError(kErrorConnectionNotFound); return; } scoped_refptr<device::HidConnection> connection = resource->connection(); connection->Read(base::Bind(&HidReceiveFunction::OnFinished, this)); } void HidReceiveFunction::OnFinished(bool success, scoped_refptr<net::IOBuffer> buffer, size_t size) { if (!success) { CompleteWithError(kErrorTransfer); return; } DCHECK_GE(size, 1u); int report_id = reinterpret_cast<uint8_t*>(buffer->data())[0]; scoped_ptr<base::ListValue> result(new base::ListValue()); result->Append(new base::FundamentalValue(report_id)); result->Append( base::BinaryValue::CreateWithCopiedBuffer(buffer->data() + 1, size - 1)); SetResultList(result.Pass()); AsyncWorkCompleted(); } HidSendFunction::HidSendFunction() {} HidSendFunction::~HidSendFunction() {} bool HidSendFunction::Prepare() { parameters_ = hid::Send::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidSendFunction::AsyncWorkStart() { int connection_id = parameters_->connection_id; HidConnectionResource* resource = connection_manager_->Get(extension_->id(), connection_id); if (!resource) { CompleteWithError(kErrorConnectionNotFound); return; } scoped_refptr<net::IOBufferWithSize> buffer( new net::IOBufferWithSize(parameters_->data.size() + 1)); buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id); memcpy( buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size()); resource->connection()->Write( buffer, buffer->size(), base::Bind(&HidSendFunction::OnFinished, this)); } void HidSendFunction::OnFinished(bool success) { if (!success) { CompleteWithError(kErrorTransfer); return; } AsyncWorkCompleted(); } HidReceiveFeatureReportFunction::HidReceiveFeatureReportFunction() {} HidReceiveFeatureReportFunction::~HidReceiveFeatureReportFunction() {} bool HidReceiveFeatureReportFunction::Prepare() { parameters_ = hid::ReceiveFeatureReport::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidReceiveFeatureReportFunction::AsyncWorkStart() { int connection_id = parameters_->connection_id; HidConnectionResource* resource = connection_manager_->Get(extension_->id(), connection_id); if (!resource) { CompleteWithError(kErrorConnectionNotFound); return; } scoped_refptr<device::HidConnection> connection = resource->connection(); connection->GetFeatureReport( static_cast<uint8_t>(parameters_->report_id), base::Bind(&HidReceiveFeatureReportFunction::OnFinished, this)); } void HidReceiveFeatureReportFunction::OnFinished( bool success, scoped_refptr<net::IOBuffer> buffer, size_t size) { if (!success) { CompleteWithError(kErrorTransfer); return; } SetResult(base::BinaryValue::CreateWithCopiedBuffer(buffer->data(), size)); AsyncWorkCompleted(); } HidSendFeatureReportFunction::HidSendFeatureReportFunction() {} HidSendFeatureReportFunction::~HidSendFeatureReportFunction() {} bool HidSendFeatureReportFunction::Prepare() { parameters_ = hid::SendFeatureReport::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters_.get()); return true; } void HidSendFeatureReportFunction::AsyncWorkStart() { int connection_id = parameters_->connection_id; HidConnectionResource* resource = connection_manager_->Get(extension_->id(), connection_id); if (!resource) { CompleteWithError(kErrorConnectionNotFound); return; } scoped_refptr<net::IOBufferWithSize> buffer( new net::IOBufferWithSize(parameters_->data.size() + 1)); buffer->data()[0] = static_cast<uint8_t>(parameters_->report_id); memcpy( buffer->data() + 1, parameters_->data.c_str(), parameters_->data.size()); resource->connection()->SendFeatureReport( buffer, buffer->size(), base::Bind(&HidSendFeatureReportFunction::OnFinished, this)); } void HidSendFeatureReportFunction::OnFinished(bool success) { if (!success) { CompleteWithError(kErrorTransfer); return; } AsyncWorkCompleted(); } } // namespace extensions