// Copyright (c) 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 "device/hid/hid_connection.h" #include #include "components/device_event_log/device_event_log.h" namespace device { namespace { // Functor used to filter collections by report ID. struct CollectionHasReportId { explicit CollectionHasReportId(uint8_t report_id) : report_id_(report_id) {} bool operator()(const HidCollectionInfo& info) const { if (info.report_ids.size() == 0 || report_id_ == HidConnection::kNullReportId) return false; if (report_id_ == HidConnection::kAnyReportId) return true; return std::find(info.report_ids.begin(), info.report_ids.end(), report_id_) != info.report_ids.end(); } private: const uint8_t report_id_; }; // Functor returning true if collection has a protected usage. struct CollectionIsProtected { bool operator()(const HidCollectionInfo& info) const { return info.usage.IsProtected(); } }; bool FindCollectionByReportId(const std::vector& collections, uint8_t report_id, HidCollectionInfo* collection_info) { std::vector::const_iterator collection_iter = std::find_if( collections.begin(), collections.end(), CollectionHasReportId(report_id)); if (collection_iter != collections.end()) { if (collection_info) { *collection_info = *collection_iter; } return true; } return false; } bool HasProtectedCollection(const std::vector& collections) { return std::find_if(collections.begin(), collections.end(), CollectionIsProtected()) != collections.end(); } } // namespace HidConnection::HidConnection(scoped_refptr device_info) : device_info_(device_info), closed_(false) { has_protected_collection_ = HasProtectedCollection(device_info->collections()); } HidConnection::~HidConnection() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(closed_); } void HidConnection::Close() { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(!closed_); PlatformClose(); closed_ = true; } void HidConnection::Read(const ReadCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (device_info_->max_input_report_size() == 0) { HID_LOG(USER) << "This device does not support input reports."; callback.Run(false, NULL, 0); return; } PlatformRead(callback); } void HidConnection::Write(scoped_refptr buffer, size_t size, const WriteCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (device_info_->max_output_report_size() == 0) { HID_LOG(USER) << "This device does not support output reports."; callback.Run(false); return; } if (size > device_info_->max_output_report_size() + 1) { HID_LOG(USER) << "Output report buffer too long (" << size << " > " << (device_info_->max_output_report_size() + 1) << ")."; callback.Run(false); return; } DCHECK_GE(size, 1u); uint8_t report_id = buffer->data()[0]; if (device_info_->has_report_id() != (report_id != 0)) { HID_LOG(USER) << "Invalid output report ID."; callback.Run(false); return; } if (IsReportIdProtected(report_id)) { HID_LOG(USER) << "Attempt to set a protected output report."; callback.Run(false); return; } PlatformWrite(buffer, size, callback); } void HidConnection::GetFeatureReport(uint8_t report_id, const ReadCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (device_info_->max_feature_report_size() == 0) { HID_LOG(USER) << "This device does not support feature reports."; callback.Run(false, NULL, 0); return; } if (device_info_->has_report_id() != (report_id != 0)) { HID_LOG(USER) << "Invalid feature report ID."; callback.Run(false, NULL, 0); return; } if (IsReportIdProtected(report_id)) { HID_LOG(USER) << "Attempt to get a protected feature report."; callback.Run(false, NULL, 0); return; } PlatformGetFeatureReport(report_id, callback); } void HidConnection::SendFeatureReport(scoped_refptr buffer, size_t size, const WriteCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); if (device_info_->max_feature_report_size() == 0) { HID_LOG(USER) << "This device does not support feature reports."; callback.Run(false); return; } DCHECK_GE(size, 1u); uint8_t report_id = buffer->data()[0]; if (device_info_->has_report_id() != (report_id != 0)) { HID_LOG(USER) << "Invalid feature report ID."; callback.Run(false); return; } if (IsReportIdProtected(report_id)) { HID_LOG(USER) << "Attempt to set a protected feature report."; callback.Run(false); return; } PlatformSendFeatureReport(buffer, size, callback); } bool HidConnection::CompleteRead(scoped_refptr buffer, size_t size, const ReadCallback& callback) { DCHECK_GE(size, 1u); uint8_t report_id = buffer->data()[0]; if (IsReportIdProtected(report_id)) { HID_LOG(EVENT) << "Filtered a protected input report."; return false; } callback.Run(true, buffer, size); return true; } bool HidConnection::IsReportIdProtected(uint8_t report_id) { HidCollectionInfo collection_info; if (FindCollectionByReportId(device_info_->collections(), report_id, &collection_info)) { return collection_info.usage.IsProtected(); } return has_protected_collection(); } PendingHidReport::PendingHidReport() {} PendingHidReport::PendingHidReport(const PendingHidReport& other) = default; PendingHidReport::~PendingHidReport() {} PendingHidRead::PendingHidRead() {} PendingHidRead::PendingHidRead(const PendingHidRead& other) = default; PendingHidRead::~PendingHidRead() {} } // namespace device