hid_connection.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (c) 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. #include "services/device/hid/hid_connection.h"
  5. #include <algorithm>
  6. #include "base/containers/contains.h"
  7. #include "base/memory/ref_counted_memory.h"
  8. #include "components/device_event_log/device_event_log.h"
  9. #include "services/device/public/cpp/hid/hid_usage_and_page.h"
  10. #include "services/device/public/mojom/hid.mojom.h"
  11. namespace device {
  12. namespace {
  13. // Functor returning true if collection has a protected usage.
  14. struct CollectionIsAlwaysProtected {
  15. bool operator()(const mojom::HidCollectionInfoPtr& info) const {
  16. return IsAlwaysProtected(*info->usage);
  17. }
  18. };
  19. bool HasAlwaysProtectedCollection(
  20. const std::vector<mojom::HidCollectionInfoPtr>& collections) {
  21. return std::find_if(collections.begin(), collections.end(),
  22. CollectionIsAlwaysProtected()) != collections.end();
  23. }
  24. } // namespace
  25. HidConnection::HidConnection(scoped_refptr<HidDeviceInfo> device_info,
  26. bool allow_protected_reports,
  27. bool allow_fido_reports)
  28. : device_info_(device_info),
  29. allow_protected_reports_(allow_protected_reports),
  30. allow_fido_reports_(allow_fido_reports),
  31. closed_(false) {
  32. has_always_protected_collection_ =
  33. HasAlwaysProtectedCollection(device_info->collections());
  34. }
  35. HidConnection::~HidConnection() {
  36. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  37. DCHECK(closed_);
  38. }
  39. void HidConnection::SetClient(Client* client) {
  40. if (client) {
  41. DCHECK(pending_reads_.empty());
  42. DCHECK(pending_reports_.empty());
  43. }
  44. client_ = client;
  45. }
  46. void HidConnection::Close() {
  47. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  48. DCHECK(!closed_);
  49. PlatformClose();
  50. closed_ = true;
  51. }
  52. void HidConnection::Read(ReadCallback callback) {
  53. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  54. DCHECK(!client_);
  55. if (device_info_->max_input_report_size() == 0) {
  56. HID_LOG(USER) << "This device does not support input reports.";
  57. std::move(callback).Run(false, nullptr, 0);
  58. return;
  59. }
  60. pending_reads_.emplace(std::move(callback));
  61. ProcessReadQueue();
  62. }
  63. void HidConnection::Write(scoped_refptr<base::RefCountedBytes> buffer,
  64. WriteCallback callback) {
  65. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  66. if (device_info_->max_output_report_size() == 0) {
  67. HID_LOG(USER) << "This device does not support output reports.";
  68. std::move(callback).Run(false);
  69. return;
  70. }
  71. if (buffer->size() > device_info_->max_output_report_size() + 1) {
  72. HID_LOG(USER) << "Output report buffer too long (" << buffer->size()
  73. << " > " << (device_info_->max_output_report_size() + 1)
  74. << ").";
  75. std::move(callback).Run(false);
  76. return;
  77. }
  78. DCHECK_GE(buffer->size(), 1u);
  79. uint8_t report_id = buffer->data()[0];
  80. if (device_info_->has_report_id() != (report_id != 0)) {
  81. HID_LOG(USER) << "Invalid output report ID.";
  82. std::move(callback).Run(false);
  83. return;
  84. }
  85. if (IsReportIdProtected(report_id, HidReportType::kOutput)) {
  86. HID_LOG(USER) << "Attempt to set a protected output report.";
  87. std::move(callback).Run(false);
  88. return;
  89. }
  90. PlatformWrite(buffer, std::move(callback));
  91. }
  92. void HidConnection::GetFeatureReport(uint8_t report_id, ReadCallback callback) {
  93. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  94. if (device_info_->max_feature_report_size() == 0) {
  95. HID_LOG(USER) << "This device does not support feature reports.";
  96. std::move(callback).Run(false, nullptr, 0);
  97. return;
  98. }
  99. if (device_info_->has_report_id() != (report_id != 0)) {
  100. HID_LOG(USER) << "Invalid feature report ID.";
  101. std::move(callback).Run(false, nullptr, 0);
  102. return;
  103. }
  104. if (IsReportIdProtected(report_id, HidReportType::kFeature)) {
  105. HID_LOG(USER) << "Attempt to get a protected feature report.";
  106. std::move(callback).Run(false, nullptr, 0);
  107. return;
  108. }
  109. PlatformGetFeatureReport(report_id, std::move(callback));
  110. }
  111. void HidConnection::SendFeatureReport(
  112. scoped_refptr<base::RefCountedBytes> buffer,
  113. WriteCallback callback) {
  114. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  115. if (device_info_->max_feature_report_size() == 0) {
  116. HID_LOG(USER) << "This device does not support feature reports.";
  117. std::move(callback).Run(false);
  118. return;
  119. }
  120. DCHECK_GE(buffer->size(), 1u);
  121. uint8_t report_id = buffer->data()[0];
  122. if (device_info_->has_report_id() != (report_id != 0)) {
  123. HID_LOG(USER) << "Invalid feature report ID.";
  124. std::move(callback).Run(false);
  125. return;
  126. }
  127. if (IsReportIdProtected(report_id, HidReportType::kFeature)) {
  128. HID_LOG(USER) << "Attempt to set a protected feature report.";
  129. std::move(callback).Run(false);
  130. return;
  131. }
  132. PlatformSendFeatureReport(buffer, std::move(callback));
  133. }
  134. bool HidConnection::IsReportIdProtected(uint8_t report_id,
  135. HidReportType report_type) {
  136. if (!allow_protected_reports_) {
  137. // If |allow_fido_reports_| is true, allow access to reports in collections
  138. // with a usage from the FIDO usage page. FIDO reports are normally blocked
  139. // by the HID blocklist.
  140. if (allow_fido_reports_) {
  141. auto* collection_info =
  142. device_info_->FindCollectionWithReport(report_id, report_type);
  143. if (collection_info &&
  144. collection_info->usage->usage_page == mojom::kPageFido) {
  145. return false;
  146. }
  147. }
  148. // Deny access to reports that match HID blocklist rules.
  149. if (report_type == HidReportType::kInput) {
  150. if (device_info_->device()->protected_input_report_ids.has_value() &&
  151. base::Contains(*device_info_->device()->protected_input_report_ids,
  152. report_id)) {
  153. return true;
  154. }
  155. } else if (report_type == HidReportType::kOutput) {
  156. if (device_info_->device()->protected_output_report_ids.has_value() &&
  157. base::Contains(*device_info_->device()->protected_output_report_ids,
  158. report_id)) {
  159. return true;
  160. }
  161. } else if (report_type == HidReportType::kFeature) {
  162. if (device_info_->device()->protected_feature_report_ids.has_value() &&
  163. base::Contains(*device_info_->device()->protected_feature_report_ids,
  164. report_id)) {
  165. return true;
  166. }
  167. }
  168. }
  169. // Some types of reports are always blocked regardless of
  170. // |allow_protected_reports_|.
  171. auto* collection_info =
  172. device_info_->FindCollectionWithReport(report_id, report_type);
  173. if (collection_info) {
  174. return IsAlwaysProtected(*collection_info->usage);
  175. }
  176. return has_always_protected_collection();
  177. }
  178. void HidConnection::ProcessInputReport(
  179. scoped_refptr<base::RefCountedBytes> buffer,
  180. size_t size) {
  181. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  182. DCHECK_GE(size, 1u);
  183. uint8_t report_id = buffer->data()[0];
  184. if (IsReportIdProtected(report_id, HidReportType::kInput))
  185. return;
  186. if (client_) {
  187. client_->OnInputReport(buffer, size);
  188. } else {
  189. pending_reports_.emplace(buffer, size);
  190. ProcessReadQueue();
  191. }
  192. }
  193. void HidConnection::ProcessReadQueue() {
  194. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  195. DCHECK(!client_);
  196. // Hold a reference to |this| to prevent a callback from freeing this object
  197. // during the loop.
  198. scoped_refptr<HidConnection> self(this);
  199. while (pending_reads_.size() && pending_reports_.size()) {
  200. ReadCallback callback = std::move(pending_reads_.front());
  201. auto [buffer, size] = std::move(pending_reports_.front());
  202. pending_reads_.pop();
  203. pending_reports_.pop();
  204. std::move(callback).Run(true, std::move(buffer), size);
  205. }
  206. }
  207. } // namespace device