hid_service_mac.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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. #include "services/device/hid/hid_service_mac.h"
  5. #include <CoreFoundation/CoreFoundation.h>
  6. #include <stdint.h>
  7. #include <set>
  8. #include <string>
  9. #include <vector>
  10. #include "base/bind.h"
  11. #include "base/location.h"
  12. #include "base/logging.h"
  13. #include "base/mac/foundation_util.h"
  14. #include "base/stl_util.h"
  15. #include "base/strings/string_number_conversions.h"
  16. #include "base/strings/stringprintf.h"
  17. #include "base/strings/sys_string_conversions.h"
  18. #include "base/task/thread_pool.h"
  19. #include "base/threading/sequenced_task_runner_handle.h"
  20. #include "components/device_event_log/device_event_log.h"
  21. #include "services/device/hid/hid_connection_mac.h"
  22. #include "services/device/utils/mac_utils.h"
  23. namespace device {
  24. namespace {
  25. bool TryGetHidDataProperty(io_service_t service,
  26. CFStringRef key,
  27. std::vector<uint8_t>* result) {
  28. base::ScopedCFTypeRef<CFDataRef> ref(base::mac::CFCast<CFDataRef>(
  29. IORegistryEntryCreateCFProperty(service, key, kCFAllocatorDefault, 0)));
  30. if (!ref)
  31. return false;
  32. base::STLClearObject(result);
  33. const uint8_t* bytes = CFDataGetBytePtr(ref);
  34. result->insert(result->begin(), bytes, bytes + CFDataGetLength(ref));
  35. return true;
  36. }
  37. scoped_refptr<HidDeviceInfo> CreateDeviceInfo(
  38. base::mac::ScopedIOObject<io_service_t> service) {
  39. uint64_t entry_id;
  40. IOReturn result = IORegistryEntryGetRegistryEntryID(service, &entry_id);
  41. if (result != kIOReturnSuccess) {
  42. HID_LOG(EVENT) << "Failed to get IORegistryEntry ID: "
  43. << HexErrorCode(result);
  44. return nullptr;
  45. }
  46. std::vector<uint8_t> report_descriptor;
  47. if (!TryGetHidDataProperty(service, CFSTR(kIOHIDReportDescriptorKey),
  48. &report_descriptor)) {
  49. HID_LOG(DEBUG) << "Device report descriptor not available.";
  50. }
  51. int32_t location_id =
  52. GetIntegerProperty<int32_t>(service, CFSTR(kIOHIDLocationIDKey))
  53. .value_or(0);
  54. std::string physical_device_id =
  55. location_id == 0 ? "" : base::NumberToString(location_id);
  56. return new HidDeviceInfo(
  57. entry_id, physical_device_id,
  58. GetIntegerProperty<int32_t>(service, CFSTR(kIOHIDVendorIDKey))
  59. .value_or(0),
  60. GetIntegerProperty<int32_t>(service, CFSTR(kIOHIDProductIDKey))
  61. .value_or(0),
  62. GetStringProperty<std::string>(service, CFSTR(kIOHIDProductKey))
  63. .value_or(""),
  64. GetStringProperty<std::string>(service, CFSTR(kIOHIDSerialNumberKey))
  65. .value_or(""),
  66. // TODO(reillyg): Detect Bluetooth. crbug.com/443335
  67. mojom::HidBusType::kHIDBusTypeUSB, report_descriptor);
  68. }
  69. } // namespace
  70. HidServiceMac::HidServiceMac() : weak_factory_(this) {
  71. notify_port_.reset(IONotificationPortCreate(kIOMasterPortDefault));
  72. CFRunLoopAddSource(CFRunLoopGetMain(),
  73. IONotificationPortGetRunLoopSource(notify_port_.get()),
  74. kCFRunLoopDefaultMode);
  75. IOReturn result = IOServiceAddMatchingNotification(
  76. notify_port_.get(), kIOFirstMatchNotification,
  77. IOServiceMatching(kIOHIDDeviceKey), FirstMatchCallback, this,
  78. devices_added_iterator_.InitializeInto());
  79. if (result != kIOReturnSuccess) {
  80. HID_LOG(DEBUG) << "Failed to listen for device arrival: "
  81. << HexErrorCode(result);
  82. return;
  83. }
  84. // Drain the iterator to arm the notification.
  85. AddDevices();
  86. result = IOServiceAddMatchingNotification(
  87. notify_port_.get(), kIOTerminatedNotification,
  88. IOServiceMatching(kIOHIDDeviceKey), TerminatedCallback, this,
  89. devices_removed_iterator_.InitializeInto());
  90. if (result != kIOReturnSuccess) {
  91. HID_LOG(DEBUG) << "Failed to listen for device removal: "
  92. << HexErrorCode(result);
  93. return;
  94. }
  95. // Drain devices_added_iterator_ to arm the notification.
  96. RemoveDevices();
  97. FirstEnumerationComplete();
  98. }
  99. HidServiceMac::~HidServiceMac() {}
  100. void HidServiceMac::Connect(const std::string& device_guid,
  101. bool allow_protected_reports,
  102. bool allow_fido_reports,
  103. ConnectCallback callback) {
  104. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  105. const auto& map_entry = devices().find(device_guid);
  106. if (map_entry == devices().end()) {
  107. base::SequencedTaskRunnerHandle::Get()->PostTask(
  108. FROM_HERE, base::BindOnce(std::move(callback), nullptr));
  109. return;
  110. }
  111. base::ThreadPool::PostTaskAndReplyWithResult(
  112. FROM_HERE, kBlockingTaskTraits,
  113. base::BindOnce(&HidServiceMac::OpenOnBlockingThread, map_entry->second),
  114. base::BindOnce(&HidServiceMac::DeviceOpened, weak_factory_.GetWeakPtr(),
  115. map_entry->second, allow_protected_reports,
  116. allow_fido_reports, std::move(callback)));
  117. }
  118. base::WeakPtr<HidService> HidServiceMac::GetWeakPtr() {
  119. return weak_factory_.GetWeakPtr();
  120. }
  121. // static
  122. base::ScopedCFTypeRef<IOHIDDeviceRef> HidServiceMac::OpenOnBlockingThread(
  123. scoped_refptr<HidDeviceInfo> device_info) {
  124. DCHECK_EQ(device_info->platform_device_id_map().size(), 1u);
  125. const auto& platform_device_id =
  126. device_info->platform_device_id_map().front().platform_device_id;
  127. base::ScopedCFTypeRef<CFDictionaryRef> matching_dict(
  128. IORegistryEntryIDMatching(platform_device_id));
  129. if (!matching_dict.get()) {
  130. HID_LOG(DEBUG) << "Failed to create matching dictionary for ID: "
  131. << platform_device_id;
  132. return base::ScopedCFTypeRef<IOHIDDeviceRef>();
  133. }
  134. // IOServiceGetMatchingService consumes a reference to the matching dictionary
  135. // passed to it.
  136. base::mac::ScopedIOObject<io_service_t> service(IOServiceGetMatchingService(
  137. kIOMasterPortDefault, matching_dict.release()));
  138. if (!service.get()) {
  139. HID_LOG(DEBUG) << "IOService not found for ID: " << platform_device_id;
  140. return base::ScopedCFTypeRef<IOHIDDeviceRef>();
  141. }
  142. base::ScopedCFTypeRef<IOHIDDeviceRef> hid_device(
  143. IOHIDDeviceCreate(kCFAllocatorDefault, service));
  144. if (!hid_device) {
  145. HID_LOG(DEBUG) << "Unable to create IOHIDDevice object.";
  146. return base::ScopedCFTypeRef<IOHIDDeviceRef>();
  147. }
  148. IOReturn result = IOHIDDeviceOpen(hid_device, kIOHIDOptionsTypeNone);
  149. if (result != kIOReturnSuccess) {
  150. HID_LOG(DEBUG) << "Failed to open device: " << HexErrorCode(result);
  151. return base::ScopedCFTypeRef<IOHIDDeviceRef>();
  152. }
  153. return hid_device;
  154. }
  155. void HidServiceMac::DeviceOpened(
  156. scoped_refptr<HidDeviceInfo> device_info,
  157. bool allow_protected_reports,
  158. bool allow_fido_reports,
  159. ConnectCallback callback,
  160. base::ScopedCFTypeRef<IOHIDDeviceRef> hid_device) {
  161. if (hid_device) {
  162. std::move(callback).Run(base::MakeRefCounted<HidConnectionMac>(
  163. std::move(hid_device), std::move(device_info), allow_protected_reports,
  164. allow_fido_reports));
  165. } else {
  166. std::move(callback).Run(nullptr);
  167. }
  168. }
  169. // static
  170. void HidServiceMac::FirstMatchCallback(void* context, io_iterator_t iterator) {
  171. DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent());
  172. HidServiceMac* service = static_cast<HidServiceMac*>(context);
  173. DCHECK_EQ(service->devices_added_iterator_, iterator);
  174. service->AddDevices();
  175. }
  176. // static
  177. void HidServiceMac::TerminatedCallback(void* context, io_iterator_t iterator) {
  178. DCHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent());
  179. HidServiceMac* service = static_cast<HidServiceMac*>(context);
  180. DCHECK_EQ(service->devices_removed_iterator_, iterator);
  181. service->RemoveDevices();
  182. }
  183. void HidServiceMac::AddDevices() {
  184. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  185. base::mac::ScopedIOObject<io_service_t> device;
  186. while (device.reset(IOIteratorNext(devices_added_iterator_)), device) {
  187. scoped_refptr<HidDeviceInfo> device_info =
  188. CreateDeviceInfo(std::move(device));
  189. if (device_info)
  190. AddDevice(device_info);
  191. }
  192. }
  193. void HidServiceMac::RemoveDevices() {
  194. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  195. base::mac::ScopedIOObject<io_service_t> device;
  196. while (device.reset(IOIteratorNext(devices_removed_iterator_)), device) {
  197. uint64_t entry_id;
  198. IOReturn result = IORegistryEntryGetRegistryEntryID(device, &entry_id);
  199. if (result == kIOReturnSuccess)
  200. RemoveDevice(entry_id);
  201. }
  202. }
  203. } // namespace device