| OLD | NEW |
| (Empty) | |
| 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 |
| 5 #include "device/hid/hid_service_mac.h" |
| 6 |
| 7 #include <map> |
| 8 #include <set> |
| 9 #include <string> |
| 10 |
| 11 #include "base/bind.h" |
| 12 #include "base/callback.h" |
| 13 #include "base/lazy_instance.h" |
| 14 #include "base/logging.h" |
| 15 #include "base/mac/foundation_util.h" |
| 16 #include "base/memory/scoped_vector.h" |
| 17 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/threading/thread_restrictions.h" |
| 19 #include "device/hid/hid_connection.h" |
| 20 #include "device/hid/hid_connection_mac.h" |
| 21 #include "device/hid/hid_utils_mac.h" |
| 22 #include "net/base/io_buffer.h" |
| 23 |
| 24 #include <CoreFoundation/CoreFoundation.h> |
| 25 #include <IOKit/hid/IOHIDManager.h> |
| 26 |
| 27 namespace device { |
| 28 |
| 29 class HidServiceMac; |
| 30 |
| 31 HidServiceMac::HidServiceMac() : enumeration_runloop_init_(true, false) { |
| 32 base::ThreadRestrictions::AssertIOAllowed(); |
| 33 |
| 34 hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault, |
| 35 kIOHIDOptionsTypeNone)); |
| 36 if (!hid_manager_ref_ || |
| 37 CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) { |
| 38 LOG(ERROR) << "Failed to initialize HidManager"; |
| 39 return; |
| 40 } |
| 41 CFRetain(hid_manager_ref_); |
| 42 |
| 43 // Register for plug/unplug notifications. |
| 44 IOHIDManagerSetDeviceMatching(hid_manager_ref_.get(), NULL); |
| 45 IOHIDManagerRegisterDeviceMatchingCallback( |
| 46 hid_manager_ref_.get(), |
| 47 &HidServiceMac::DeviceAddCallback, |
| 48 this); |
| 49 IOHIDManagerRegisterDeviceRemovalCallback( |
| 50 hid_manager_ref_.get(), |
| 51 &HidServiceMac::DeviceRemoveCallback, |
| 52 this); |
| 53 |
| 54 // Blocking operation to enumerate all the pre-existing devices. |
| 55 Enumerate(); |
| 56 |
| 57 // Save the owner message loop. |
| 58 message_loop_ = base::MessageLoopProxy::current(); |
| 59 |
| 60 // Start a thread to monitor HID device changes. |
| 61 // The reason to create a new thread is that by default the only thread in the |
| 62 // browser process having a CFRunLoop is the UI thread. We do not want to |
| 63 // run potentially blocking routines on it. |
| 64 enumeration_runloop_thread_.reset( |
| 65 new base::Thread("HidService Device Enumeration Thread")); |
| 66 |
| 67 base::Thread::Options options; |
| 68 options.message_loop_type = base::MessageLoop::TYPE_UI; |
| 69 enumeration_runloop_thread_->StartWithOptions(options); |
| 70 enumeration_runloop_thread_->message_loop()->PostTask( |
| 71 FROM_HERE, |
| 72 base::Bind(&HidServiceMac::ScheduleRunLoop, base::Unretained(this))); |
| 73 |
| 74 enumeration_runloop_init_.Wait(); |
| 75 initialized_ = true; |
| 76 } |
| 77 |
| 78 HidServiceMac::~HidServiceMac() { |
| 79 enumeration_runloop_thread_->message_loop()->PostTask( |
| 80 FROM_HERE, |
| 81 base::Bind(&HidServiceMac::UnscheduleRunLoop, base::Unretained(this))); |
| 82 |
| 83 enumeration_runloop_thread_->Stop(); |
| 84 } |
| 85 |
| 86 void HidServiceMac::ScheduleRunLoop() { |
| 87 enumeration_runloop_ = CFRunLoopGetCurrent(); |
| 88 |
| 89 IOHIDManagerScheduleWithRunLoop( |
| 90 hid_manager_ref_, |
| 91 enumeration_runloop_, |
| 92 kCFRunLoopDefaultMode); |
| 93 |
| 94 IOHIDManagerOpen(hid_manager_ref_, kIOHIDOptionsTypeNone); |
| 95 |
| 96 enumeration_runloop_init_.Signal(); |
| 97 } |
| 98 |
| 99 void HidServiceMac::UnscheduleRunLoop() { |
| 100 IOHIDManagerUnscheduleFromRunLoop( |
| 101 hid_manager_ref_, |
| 102 enumeration_runloop_, |
| 103 kCFRunLoopDefaultMode); |
| 104 |
| 105 IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone); |
| 106 |
| 107 } |
| 108 |
| 109 HidServiceMac* HidServiceMac::InstanceFromContext(void* context) { |
| 110 return reinterpret_cast<HidServiceMac*>(context); |
| 111 } |
| 112 |
| 113 void HidServiceMac::DeviceAddCallback(void* context, |
| 114 IOReturn result, |
| 115 void* sender, |
| 116 IOHIDDeviceRef ref) { |
| 117 HidServiceMac* service = InstanceFromContext(context); |
| 118 |
| 119 // Takes ownership of ref. Will be released inside PlatformDeviceAdd. |
| 120 CFRetain(ref); |
| 121 service->message_loop_->PostTask( |
| 122 FROM_HERE, |
| 123 base::Bind(&HidServiceMac::PlatformDeviceAdd, |
| 124 base::Unretained(service), |
| 125 base::Unretained(ref))); |
| 126 } |
| 127 |
| 128 void HidServiceMac::DeviceRemoveCallback(void* context, |
| 129 IOReturn result, |
| 130 void* sender, |
| 131 IOHIDDeviceRef ref) { |
| 132 HidServiceMac* service = InstanceFromContext(context); |
| 133 |
| 134 // Takes ownership of ref. Will be released inside PlatformDeviceRemove. |
| 135 CFRetain(ref); |
| 136 service->message_loop_->PostTask( |
| 137 FROM_HERE, |
| 138 base::Bind(&HidServiceMac::PlatformDeviceRemove, |
| 139 base::Unretained(service), |
| 140 base::Unretained(ref))); |
| 141 } |
| 142 |
| 143 IOHIDDeviceRef HidServiceMac::FindDevice(std::string id) { |
| 144 base::ScopedCFTypeRef<CFSetRef> devices( |
| 145 IOHIDManagerCopyDevices(hid_manager_ref_)); |
| 146 CFIndex count = CFSetGetCount(devices); |
| 147 scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); |
| 148 CFSetGetValues(devices, (const void **)(device_refs.get())); |
| 149 |
| 150 for (CFIndex i = 0; i < count; i++) { |
| 151 int32_t int_property = 0; |
| 152 if (GetIntProperty(device_refs[i], CFSTR(kIOHIDLocationIDKey), |
| 153 &int_property)) { |
| 154 if (id == base::HexEncode(&int_property, sizeof(int_property))) { |
| 155 return device_refs[i]; |
| 156 } |
| 157 } |
| 158 } |
| 159 |
| 160 return NULL; |
| 161 } |
| 162 |
| 163 void HidServiceMac::Enumerate() { |
| 164 base::ScopedCFTypeRef<CFSetRef> devices( |
| 165 IOHIDManagerCopyDevices(hid_manager_ref_)); |
| 166 CFIndex count = CFSetGetCount(devices); |
| 167 scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); |
| 168 CFSetGetValues(devices, (const void **)(device_refs.get())); |
| 169 |
| 170 for (CFIndex i = 0; i < count; i++) { |
| 171 // Takes ownership. Will be released inside PlatformDeviceAdd. |
| 172 CFRetain(device_refs[i]); |
| 173 PlatformDeviceAdd(device_refs[i]); |
| 174 } |
| 175 } |
| 176 |
| 177 void HidServiceMac::PlatformDeviceAdd(IOHIDDeviceRef raw_ref) { |
| 178 HidDeviceInfo device; |
| 179 int32_t int_property = 0; |
| 180 std::string str_property; |
| 181 |
| 182 // Auto-release. |
| 183 base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); |
| 184 |
| 185 // Unique identifier for HID device. |
| 186 if (GetIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { |
| 187 device.device_id = base::HexEncode(&int_property, sizeof(int_property)); |
| 188 } else { |
| 189 // Not an available device. |
| 190 return; |
| 191 } |
| 192 |
| 193 if (GetIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) { |
| 194 device.vendor_id = int_property; |
| 195 } |
| 196 if (GetIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) { |
| 197 device.product_id = int_property; |
| 198 } |
| 199 if (GetIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) { |
| 200 device.usage = int_property; |
| 201 } |
| 202 if (GetIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) { |
| 203 device.usage_page = int_property; |
| 204 } |
| 205 if (GetIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey), &int_property)) { |
| 206 device.input_report_size = int_property; |
| 207 } |
| 208 if (GetIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey), &int_property)) { |
| 209 device.output_report_size = int_property; |
| 210 } |
| 211 if (GetIntProperty(ref, CFSTR(kIOHIDMaxFeatureReportSizeKey), |
| 212 &int_property)) { |
| 213 device.feature_report_size = int_property; |
| 214 } |
| 215 if (GetStringProperty(ref, CFSTR(kIOHIDProductKey), &str_property)) { |
| 216 device.product_name = str_property; |
| 217 } |
| 218 if (GetStringProperty(ref, CFSTR(kIOHIDSerialNumberKey), &str_property)) { |
| 219 device.serial_number = str_property; |
| 220 } |
| 221 HidService::DeviceAdd(device); |
| 222 } |
| 223 |
| 224 void HidServiceMac::PlatformDeviceRemove(IOHIDDeviceRef raw_ref) { |
| 225 std::string device_id; |
| 226 int32_t int_property = 0; |
| 227 // Auto-release. |
| 228 base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); |
| 229 if (!GetIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { |
| 230 return; |
| 231 } |
| 232 device_id = base::HexEncode(&int_property, sizeof(int_property)); |
| 233 HidService::DeviceRemove(device_id); |
| 234 } |
| 235 |
| 236 scoped_refptr<HidConnection> |
| 237 HidServiceMac::Connect(std::string device_id) { |
| 238 if (!ContainsKey(devices_, device_id)) |
| 239 return NULL; |
| 240 |
| 241 IOHIDDeviceRef ref = FindDevice(device_id); |
| 242 if (ref == NULL) |
| 243 return NULL; |
| 244 return scoped_refptr<HidConnection>( |
| 245 new HidConnectionMac(this, devices_[device_id], ref)); |
| 246 } |
| 247 |
| 248 } // namespace device |
| OLD | NEW |