| OLD | NEW | 
|---|
|  | (Empty) | 
| 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 |  | 
| 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::AddDeviceCallback, |  | 
| 48       this); |  | 
| 49   IOHIDManagerRegisterDeviceRemovalCallback( |  | 
| 50       hid_manager_ref_.get(), |  | 
| 51       &HidServiceMac::RemoveDeviceCallback, |  | 
| 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::AddDeviceCallback(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::PlatformAddDevice, |  | 
| 124                  base::Unretained(service), |  | 
| 125                  base::Unretained(ref))); |  | 
| 126 } |  | 
| 127 |  | 
| 128 void HidServiceMac::RemoveDeviceCallback(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::PlatformRemoveDevice, |  | 
| 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 (GetHidIntProperty(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     PlatformAddDevice(device_refs[i]); |  | 
| 174   } |  | 
| 175 } |  | 
| 176 |  | 
| 177 void HidServiceMac::PlatformAddDevice(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 (GetHidIntProperty(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 (GetHidIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) { |  | 
| 194     device.vendor_id = int_property; |  | 
| 195   } |  | 
| 196   if (GetHidIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) { |  | 
| 197     device.product_id = int_property; |  | 
| 198   } |  | 
| 199   if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) { |  | 
| 200     device.usage = int_property; |  | 
| 201   } |  | 
| 202   if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) { |  | 
| 203     device.usage_page = int_property; |  | 
| 204   } |  | 
| 205   if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey), |  | 
| 206                         &int_property)) { |  | 
| 207     device.input_report_size = int_property; |  | 
| 208   } |  | 
| 209   if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey), |  | 
| 210                         &int_property)) { |  | 
| 211     device.output_report_size = int_property; |  | 
| 212   } |  | 
| 213   if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxFeatureReportSizeKey), |  | 
| 214                      &int_property)) { |  | 
| 215     device.feature_report_size = int_property; |  | 
| 216   } |  | 
| 217   if (GetHidStringProperty(ref, CFSTR(kIOHIDProductKey), &str_property)) { |  | 
| 218     device.product_name = str_property; |  | 
| 219   } |  | 
| 220   if (GetHidStringProperty(ref, CFSTR(kIOHIDSerialNumberKey), &str_property)) { |  | 
| 221     device.serial_number = str_property; |  | 
| 222   } |  | 
| 223   HidService::AddDevice(device); |  | 
| 224 } |  | 
| 225 |  | 
| 226 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef raw_ref) { |  | 
| 227   std::string device_id; |  | 
| 228   int32_t int_property = 0; |  | 
| 229   // Auto-release. |  | 
| 230   base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); |  | 
| 231   if (!GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { |  | 
| 232     return; |  | 
| 233   } |  | 
| 234   device_id = base::HexEncode(&int_property, sizeof(int_property)); |  | 
| 235   HidService::RemoveDevice(device_id); |  | 
| 236 } |  | 
| 237 |  | 
| 238 scoped_refptr<HidConnection> |  | 
| 239 HidServiceMac::Connect(std::string device_id) { |  | 
| 240   if (!ContainsKey(devices_, device_id)) |  | 
| 241     return NULL; |  | 
| 242 |  | 
| 243   IOHIDDeviceRef ref = FindDevice(device_id); |  | 
| 244   if (ref == NULL) |  | 
| 245     return NULL; |  | 
| 246   return scoped_refptr<HidConnection>( |  | 
| 247       new HidConnectionMac(this, devices_[device_id], ref)); |  | 
| 248 } |  | 
| 249 |  | 
| 250 }  // namespace device |  | 
| OLD | NEW | 
|---|