Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "device/hid/hid_service_mac.h" | 5 #include "device/hid/hid_service_mac.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <CoreFoundation/CoreFoundation.h> |
| 8 #include <set> | 8 #include <IOKit/hid/IOHIDManager.h> |
| 9 | |
| 9 #include <string> | 10 #include <string> |
| 10 | 11 |
| 11 #include "base/bind.h" | 12 #include "base/bind.h" |
| 12 #include "base/callback.h" | 13 #include "base/callback.h" |
| 13 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
| 14 #include "base/logging.h" | 15 #include "base/logging.h" |
| 15 #include "base/mac/foundation_util.h" | 16 #include "base/mac/foundation_util.h" |
| 16 #include "base/memory/scoped_vector.h" | 17 #include "base/memory/scoped_vector.h" |
| 17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 18 #include "base/threading/thread_restrictions.h" | 19 #include "base/threading/thread_restrictions.h" |
| 19 #include "device/hid/hid_connection.h" | 20 #include "device/hid/hid_connection.h" |
| 20 #include "device/hid/hid_connection_mac.h" | 21 #include "device/hid/hid_connection_mac.h" |
| 21 #include "device/hid/hid_utils_mac.h" | 22 #include "device/hid/hid_utils_mac.h" |
| 22 #include "net/base/io_buffer.h" | 23 #include "net/base/io_buffer.h" |
| 23 | 24 |
| 24 #include <CoreFoundation/CoreFoundation.h> | |
| 25 #include <IOKit/hid/IOHIDManager.h> | |
| 26 | |
| 27 namespace device { | 25 namespace device { |
| 28 | 26 |
| 29 class HidServiceMac; | 27 class HidServiceMac; |
| 30 | 28 |
| 31 HidServiceMac::HidServiceMac() : enumeration_runloop_init_(true, false) { | 29 namespace { |
| 32 base::ThreadRestrictions::AssertIOAllowed(); | |
| 33 | 30 |
| 34 hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault, | 31 typedef std::vector<IOHIDDeviceRef> HidDeviceList; |
|
Mark Mentovai
2014/02/13 22:37:51
#include <vector> to use this.
Or you could use a
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done. I left it as a vector since the usage is iso
| |
| 35 kIOHIDOptionsTypeNone)); | 32 |
| 36 if (!hid_manager_ref_ || | 33 HidServiceMac* HidServiceFromContext(void* context) { |
| 37 CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) { | 34 return reinterpret_cast<HidServiceMac*>(context); |
| 35 } | |
| 36 | |
| 37 // Callback for CFSetApplyFunction as used by EnumerateHidDevices. | |
| 38 void HidEnumerationBackInserter(const void* value, void* context) { | |
| 39 HidDeviceList* devices = static_cast<HidDeviceList*>(context); | |
| 40 IOHIDDeviceRef device = static_cast<IOHIDDeviceRef>(value); | |
| 41 devices->push_back(device); | |
| 42 } | |
| 43 | |
| 44 void EnumerateHidDevices(IOHIDManagerRef hid_manager, | |
| 45 HidDeviceList* device_list) { | |
| 46 base::ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(hid_manager)); | |
| 47 CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list); | |
|
Mark Mentovai
2014/02/13 22:37:51
Great! This is so much better than allocating spac
| |
| 48 } | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 HidServiceMac::HidServiceMac() { | |
| 53 hid_manager_.reset(IOHIDManagerCreate(NULL, kIOHIDManagerOptionNone)); | |
| 54 if (!hid_manager_ || CFGetTypeID(hid_manager_) != IOHIDManagerGetTypeID()) { | |
|
Mark Mentovai
2014/02/13 22:37:51
Since the type-check portion of this is pretty che
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 38 LOG(ERROR) << "Failed to initialize HidManager"; | 55 LOG(ERROR) << "Failed to initialize HidManager"; |
| 39 return; | 56 return; |
| 40 } | 57 } |
| 41 CFRetain(hid_manager_ref_); | 58 |
| 59 // Enumerate all the currently known devices. | |
| 60 Enumerate(); | |
| 42 | 61 |
| 43 // Register for plug/unplug notifications. | 62 // Register for plug/unplug notifications. |
| 44 IOHIDManagerSetDeviceMatching(hid_manager_ref_.get(), NULL); | 63 StartWatchingDevices(); |
| 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 | 64 |
| 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; | 65 initialized_ = true; |
|
Mark Mentovai
2014/02/13 22:37:51
This is unused.
Well, OK, not exactly. It’s unuse
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Yes I absolutely hate this. Removed altogether. Th
| |
| 76 } | 66 } |
| 77 | 67 |
| 78 HidServiceMac::~HidServiceMac() { | 68 HidServiceMac::~HidServiceMac() { StopWatchingDevices(); } |
|
Mark Mentovai
2014/02/13 22:37:51
This may be dangerous if you hit the early return
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 79 enumeration_runloop_thread_->message_loop()->PostTask( | |
| 80 FROM_HERE, | |
| 81 base::Bind(&HidServiceMac::UnscheduleRunLoop, base::Unretained(this))); | |
| 82 | 69 |
| 83 enumeration_runloop_thread_->Stop(); | 70 void HidServiceMac::StartWatchingDevices() { |
| 71 IOHIDManagerSetDeviceMatching(hid_manager_, NULL); | |
| 72 IOHIDManagerRegisterDeviceMatchingCallback( | |
| 73 hid_manager_, &AddDeviceCallback, this); | |
| 74 IOHIDManagerRegisterDeviceRemovalCallback( | |
| 75 hid_manager_, &RemoveDeviceCallback, this); | |
| 76 IOHIDManagerScheduleWithRunLoop( | |
| 77 hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode); | |
|
Mark Mentovai
2014/02/13 22:37:51
Now that this is expected to run on the main threa
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Thinking about it some more, I'd rather avoid the
| |
| 78 IOHIDManagerOpen(hid_manager_, kIOHIDOptionsTypeNone); | |
| 84 } | 79 } |
| 85 | 80 |
| 86 void HidServiceMac::ScheduleRunLoop() { | 81 void HidServiceMac::StopWatchingDevices() { |
| 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( | 82 IOHIDManagerUnscheduleFromRunLoop( |
| 101 hid_manager_ref_, | 83 hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode); |
| 102 enumeration_runloop_, | 84 IOHIDManagerClose(hid_manager_, kIOHIDOptionsTypeNone); |
| 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 } | 85 } |
| 112 | 86 |
| 113 void HidServiceMac::AddDeviceCallback(void* context, | 87 void HidServiceMac::AddDeviceCallback(void* context, |
| 114 IOReturn result, | 88 IOReturn result, |
| 115 void* sender, | 89 void* sender, |
| 116 IOHIDDeviceRef ref) { | 90 IOHIDDeviceRef hid_device) { |
| 117 HidServiceMac* service = InstanceFromContext(context); | 91 HidServiceMac* service = HidServiceFromContext(context); |
| 118 | 92 BrowserThread::PostTask(BrowserThread::FILE, |
|
Mark Mentovai
2014/02/13 22:37:51
To use this, you need to #include "content/public/
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
I decided to retain a handle to the current Messag
| |
| 119 // Takes ownership of ref. Will be released inside PlatformDeviceAdd. | 93 FROM_HERE, |
| 120 CFRetain(ref); | 94 base::Bind(&PlatformAddDevice, |
| 121 service->message_loop_->PostTask( | 95 base::Unretained(service), |
| 122 FROM_HERE, | 96 base::Unretained(hid_device))); |
| 123 base::Bind(&HidServiceMac::PlatformAddDevice, | |
| 124 base::Unretained(service), | |
| 125 base::Unretained(ref))); | |
| 126 } | 97 } |
| 127 | 98 |
| 128 void HidServiceMac::RemoveDeviceCallback(void* context, | 99 void HidServiceMac::RemoveDeviceCallback(void* context, |
| 129 IOReturn result, | 100 IOReturn result, |
| 130 void* sender, | 101 void* sender, |
| 131 IOHIDDeviceRef ref) { | 102 IOHIDDeviceRef hid_device) { |
| 132 HidServiceMac* service = InstanceFromContext(context); | 103 HidServiceMac* service = HidServiceFromContext(context); |
| 133 | 104 BrowserThread::PostTask(BrowserThread::FILE, |
| 134 // Takes ownership of ref. Will be released inside PlatformDeviceRemove. | 105 FROM_HERE, |
| 135 CFRetain(ref); | 106 base::Bind(&PlatformRemoveDevice, |
| 136 service->message_loop_->PostTask( | 107 base::Unretained(service), |
| 137 FROM_HERE, | 108 base::Unretained(hid_device))); |
| 138 base::Bind(&HidServiceMac::PlatformRemoveDevice, | |
| 139 base::Unretained(service), | |
| 140 base::Unretained(ref))); | |
| 141 } | 109 } |
| 142 | 110 |
| 143 IOHIDDeviceRef HidServiceMac::FindDevice(std::string id) { | 111 IOHIDDeviceRef HidServiceMac::FindDevice(const std::string& device_id) { |
| 144 base::ScopedCFTypeRef<CFSetRef> devices( | 112 HidDeviceList devices; |
| 145 IOHIDManagerCopyDevices(hid_manager_ref_)); | 113 EnumerateHidDevices(hid_manager_, &devices); |
| 146 CFIndex count = CFSetGetCount(devices); | 114 for (HidDeviceList::const_iterator iter = devices.begin(); |
| 147 scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); | 115 iter != devices.end(); |
| 148 CFSetGetValues(devices, (const void **)(device_refs.get())); | 116 ++iter) { |
| 149 | |
| 150 for (CFIndex i = 0; i < count; i++) { | |
| 151 int32_t int_property = 0; | 117 int32_t int_property = 0; |
|
Mark Mentovai
2014/02/13 22:37:51
No need to initialize.
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 152 if (GetHidIntProperty(device_refs[i], CFSTR(kIOHIDLocationIDKey), | 118 if (TryGetHidIntProperty(*iter, |
|
Mark Mentovai
2014/02/13 22:37:51
Since you’ve used *iter a couple of times in this
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 153 &int_property)) { | 119 CFSTR(kIOHIDLocationIDKey), |
| 120 &int_property)) { | |
| 154 if (id == base::HexEncode(&int_property, sizeof(int_property))) { | 121 if (id == base::HexEncode(&int_property, sizeof(int_property))) { |
|
Mark Mentovai
2014/02/13 22:37:51
Stopping here. You obviously didn’t even compile t
| |
| 155 return device_refs[i]; | 122 return *iter; |
| 156 } | 123 } |
| 157 } | 124 } |
| 158 } | 125 } |
| 159 | 126 |
| 160 return NULL; | 127 return NULL; |
| 161 } | 128 } |
| 162 | 129 |
| 163 void HidServiceMac::Enumerate() { | 130 void HidServiceMac::Enumerate() { |
| 164 base::ScopedCFTypeRef<CFSetRef> devices( | 131 HidDeviceList devices; |
| 165 IOHIDManagerCopyDevices(hid_manager_ref_)); | 132 EnumerateHidDevices(hid_manager_, &devices); |
| 166 CFIndex count = CFSetGetCount(devices); | 133 for (HidDeviceList::const_iterator iter = devices.begin(); |
| 167 scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); | 134 iter != devices.end(); |
| 168 CFSetGetValues(devices, (const void **)(device_refs.get())); | 135 ++iter) { |
| 169 | 136 // Takes ownership. Will be released inside PlatformAddDevice. |
| 170 for (CFIndex i = 0; i < count; i++) { | 137 CFRetain(*iter); |
| 171 // Takes ownership. Will be released inside PlatformDeviceAdd. | 138 PlatformAddDevice(*iter); |
| 172 CFRetain(device_refs[i]); | |
| 173 PlatformAddDevice(device_refs[i]); | |
| 174 } | 139 } |
| 175 } | 140 } |
| 176 | 141 |
| 177 void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef raw_ref) { | 142 void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device) { |
| 178 HidDeviceInfo device; | 143 HidDeviceInfo device_info; |
|
Mark Mentovai
2014/02/13 22:37:51
Don’t declare stuff ’til you use it.
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 179 int32_t int_property = 0; | 144 base::ScopedCFTypeRef<IOHIDDeviceRef> device(hid_device); |
|
Mark Mentovai
2014/02/13 22:37:51
The same discussion here applies to PlatformRemove
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
This appears to be correct, the manager does not r
| |
| 180 std::string str_property; | |
| 181 | |
| 182 // Auto-release. | |
| 183 base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); | |
| 184 | 145 |
| 185 // Unique identifier for HID device. | 146 // Unique identifier for HID device. |
| 186 if (GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { | 147 int32_t location_id = 0; |
|
Mark Mentovai
2014/02/13 22:37:51
No need to initialize.
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 187 device.device_id = base::HexEncode(&int_property, sizeof(int_property)); | 148 if (!TryGetHidIntProperty(device, CFSTR(kIOHIDLocationIDKey), &location_id)) |
|
Mark Mentovai
2014/02/13 22:37:51
Just a reminder that “is the location ID the right
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
I've looked into this and I'm pretty confident thi
| |
| 188 } else { | |
| 189 // Not an available device. | |
| 190 return; | 149 return; |
| 191 } | |
| 192 | 150 |
| 193 if (GetHidIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) { | 151 device_info.device_id = base::HexEncode(&location_id, sizeof(location_id)); |
| 194 device.vendor_id = int_property; | 152 device_info.vendor_id = GetHidIntProperty(device, CFSTR(kIOHIDVendorIDKey)); |
| 195 } | 153 device_info.product_id = GetHidIntProperty(device, CFSTR(kIOHIDProductIDKey)); |
| 196 if (GetHidIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) { | 154 device_info.usage = GetHidIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey)); |
| 197 device.product_id = int_property; | 155 device_info.usage_page = |
| 198 } | 156 GetHidIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey)); |
| 199 if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) { | 157 device_info.input_report_size = |
| 200 device.usage = int_property; | 158 GetHidIntProperty(device, CFSTR(kIOHIDMaxInputReportSizeKey)); |
| 201 } | 159 device_info.output_report_size = |
| 202 if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) { | 160 GetHidIntProperty(device, CFSTR(kIOHIDMaxOutputReportSizeKey)); |
| 203 device.usage_page = int_property; | 161 device_info.feature_report_size = |
| 204 } | 162 GetHidIntProperty(device, CFSTR(kIOHIDMaxFeatureReportSizeKey)); |
| 205 if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey), | 163 device_info.product_name = |
| 206 &int_property)) { | 164 GetHidStringProperty(device, CFSTR(kIOHIDProductKey)); |
| 207 device.input_report_size = int_property; | 165 device_info.serial_number = |
| 208 } | 166 GetHidStringProperty(device, CFSTR(kIOHIDSerialNumberKey)); |
| 209 if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey), | 167 |
| 210 &int_property)) { | 168 HidService::AddDevice(device_info); |
| 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 } | 169 } |
| 225 | 170 |
| 226 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef raw_ref) { | 171 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device) { |
| 227 std::string device_id; | 172 std::string device_id; |
|
Mark Mentovai
2014/02/13 22:37:51
Don’t declare until use. You save a line of code t
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 228 int32_t int_property = 0; | 173 int32_t int_property = 0; |
|
Mark Mentovai
2014/02/13 22:37:51
No need to initialize.
Ken Rockot(use gerrit already)
2014/02/18 19:43:09
Done.
| |
| 229 // Auto-release. | 174 base::ScopedCFTypeRef<IOHIDDeviceRef> device(hid_device); |
| 230 base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); | 175 if (!TryGetHidIntProperty(device, CFSTR(kIOHIDLocationIDKey), &int_property)) |
| 231 if (!GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { | |
| 232 return; | 176 return; |
| 233 } | |
| 234 device_id = base::HexEncode(&int_property, sizeof(int_property)); | 177 device_id = base::HexEncode(&int_property, sizeof(int_property)); |
| 235 HidService::RemoveDevice(device_id); | 178 HidService::RemoveDevice(device_id); |
| 236 } | 179 } |
| 237 | 180 |
| 238 scoped_refptr<HidConnection> | 181 scoped_refptr<HidConnection> HidServiceMac::Connect( |
| 239 HidServiceMac::Connect(std::string device_id) { | 182 const std::string& device_id) { |
| 240 if (!ContainsKey(devices_, device_id)) | 183 if (!ContainsKey(devices_, device_id)) |
| 241 return NULL; | 184 return NULL; |
| 242 | 185 |
| 243 IOHIDDeviceRef ref = FindDevice(device_id); | 186 IOHIDDeviceRef hid_device = FindDevice(device_id); |
| 244 if (ref == NULL) | 187 if (hid_device == NULL) |
| 245 return NULL; | 188 return NULL; |
| 246 return scoped_refptr<HidConnection>( | 189 return scoped_refptr<HidConnection>( |
| 247 new HidConnectionMac(this, devices_[device_id], ref)); | 190 new HidConnectionMac(this, devices_[device_id], hid_device)); |
| 248 } | 191 } |
| 249 | 192 |
| 250 } // namespace device | 193 } // namespace device |
| OLD | NEW |