 Chromium Code Reviews
 Chromium Code Reviews Issue 143883005:
  HID backend  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@hid-impl-base
    
  
    Issue 143883005:
  HID backend  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@hid-impl-base| Index: device/hid/hid_service_mac.cc | 
| diff --git a/device/hid/hid_service_mac.cc b/device/hid/hid_service_mac.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..12da7cc2d0c46afb4ad152b423c553ef7388e4f8 | 
| --- /dev/null | 
| +++ b/device/hid/hid_service_mac.cc | 
| @@ -0,0 +1,250 @@ | 
| +// Copyright (c) 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "device/hid/hid_service_mac.h" | 
| + | 
| +#include <map> | 
| 
Mark Mentovai
2014/02/12 19:35:59
Unused.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| +#include <set> | 
| 
Mark Mentovai
2014/02/12 19:35:59
Unused.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| +#include <string> | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/callback.h" | 
| +#include "base/lazy_instance.h" | 
| +#include "base/logging.h" | 
| +#include "base/mac/foundation_util.h" | 
| +#include "base/memory/scoped_vector.h" | 
| +#include "base/strings/string_number_conversions.h" | 
| +#include "base/threading/thread_restrictions.h" | 
| +#include "device/hid/hid_connection.h" | 
| +#include "device/hid/hid_connection_mac.h" | 
| +#include "device/hid/hid_utils_mac.h" | 
| +#include "net/base/io_buffer.h" | 
| + | 
| +#include <CoreFoundation/CoreFoundation.h> | 
| 
Mark Mentovai
2014/02/12 19:35:59
C system headers before C++ system headers. This i
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| +#include <IOKit/hid/IOHIDManager.h> | 
| + | 
| +namespace device { | 
| + | 
| +class HidServiceMac; | 
| + | 
| +HidServiceMac::HidServiceMac() : enumeration_runloop_init_(true, false) { | 
| + base::ThreadRestrictions::AssertIOAllowed(); | 
| 
Mark Mentovai
2014/02/12 19:35:59
Which thread exactly are you expecting this to run
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
FILE thread
 | 
| + | 
| + hid_manager_ref_.reset(IOHIDManagerCreate(kCFAllocatorDefault, | 
| 
Mark Mentovai
2014/02/12 19:35:59
It’s normal to write NULL, which has the same mean
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + kIOHIDOptionsTypeNone)); | 
| 
Mark Mentovai
2014/02/12 19:35:59
This is an option value for IOHIDManagerOpen, but
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + if (!hid_manager_ref_ || | 
| + CFGetTypeID(hid_manager_ref_) != IOHIDManagerGetTypeID()) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
Does this ever actually happen, or is it just para
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
As far as I can tell, paranoia.
 | 
| + LOG(ERROR) << "Failed to initialize HidManager"; | 
| + return; | 
| + } | 
| + CFRetain(hid_manager_ref_); | 
| 
Mark Mentovai
2014/02/12 19:35:59
IOHIDManagerCreate gave you a reference already, w
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Don't. Done.
 | 
| + | 
| + // Register for plug/unplug notifications. | 
| + IOHIDManagerSetDeviceMatching(hid_manager_ref_.get(), NULL); | 
| + IOHIDManagerRegisterDeviceMatchingCallback( | 
| + hid_manager_ref_.get(), | 
| + &HidServiceMac::AddDeviceCallback, | 
| 
Mark Mentovai
2014/02/12 19:35:59
HidServiceMac:: is implied. Same on line 51.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + this); | 
| + IOHIDManagerRegisterDeviceRemovalCallback( | 
| + hid_manager_ref_.get(), | 
| + &HidServiceMac::RemoveDeviceCallback, | 
| + this); | 
| + | 
| + // Blocking operation to enumerate all the pre-existing devices. | 
| 
Mark Mentovai
2014/02/12 19:35:59
What about Enumerate is blocking?
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
I think in this case the original concern (which s
 | 
| + Enumerate(); | 
| + | 
| + // Save the owner message loop. | 
| + message_loop_ = base::MessageLoopProxy::current(); | 
| + | 
| + // Start a thread to monitor HID device changes. | 
| + // The reason to create a new thread is that by default the only thread in the | 
| + // browser process having a CFRunLoop is the UI thread. We do not want to | 
| + // run potentially blocking routines on it. | 
| 
Mark Mentovai
2014/02/12 19:35:59
What might block?
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
In this case I can only imagine the concern was th
 | 
| + enumeration_runloop_thread_.reset( | 
| + new base::Thread("HidService Device Enumeration Thread")); | 
| + | 
| + base::Thread::Options options; | 
| + options.message_loop_type = base::MessageLoop::TYPE_UI; | 
| + enumeration_runloop_thread_->StartWithOptions(options); | 
| + enumeration_runloop_thread_->message_loop()->PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&HidServiceMac::ScheduleRunLoop, base::Unretained(this))); | 
| + | 
| + enumeration_runloop_init_.Wait(); | 
| + initialized_ = true; | 
| +} | 
| + | 
| +HidServiceMac::~HidServiceMac() { | 
| + enumeration_runloop_thread_->message_loop()->PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&HidServiceMac::UnscheduleRunLoop, base::Unretained(this))); | 
| + | 
| + enumeration_runloop_thread_->Stop(); | 
| +} | 
| + | 
| +void HidServiceMac::ScheduleRunLoop() { | 
| + enumeration_runloop_ = CFRunLoopGetCurrent(); | 
| + | 
| + IOHIDManagerScheduleWithRunLoop( | 
| + hid_manager_ref_, | 
| + enumeration_runloop_, | 
| + kCFRunLoopDefaultMode); | 
| + | 
| + IOHIDManagerOpen(hid_manager_ref_, kIOHIDOptionsTypeNone); | 
| + | 
| + enumeration_runloop_init_.Signal(); | 
| +} | 
| + | 
| +void HidServiceMac::UnscheduleRunLoop() { | 
| + IOHIDManagerUnscheduleFromRunLoop( | 
| + hid_manager_ref_, | 
| + enumeration_runloop_, | 
| + kCFRunLoopDefaultMode); | 
| + | 
| + IOHIDManagerClose(hid_manager_ref_, kIOHIDOptionsTypeNone); | 
| + | 
| +} | 
| + | 
| +HidServiceMac* HidServiceMac::InstanceFromContext(void* context) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
private + static + no need to access member data =
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + return reinterpret_cast<HidServiceMac*>(context); | 
| +} | 
| + | 
| +void HidServiceMac::AddDeviceCallback(void* context, | 
| + IOReturn result, | 
| + void* sender, | 
| + IOHIDDeviceRef ref) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
Everything in the world of CF is a reference. You’
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + HidServiceMac* service = InstanceFromContext(context); | 
| + | 
| + // Takes ownership of ref. Will be released inside PlatformDeviceAdd. | 
| + CFRetain(ref); | 
| + service->message_loop_->PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&HidServiceMac::PlatformAddDevice, | 
| + base::Unretained(service), | 
| + base::Unretained(ref))); | 
| +} | 
| + | 
| +void HidServiceMac::RemoveDeviceCallback(void* context, | 
| + IOReturn result, | 
| + void* sender, | 
| + IOHIDDeviceRef ref) { | 
| + HidServiceMac* service = InstanceFromContext(context); | 
| + | 
| + // Takes ownership of ref. Will be released inside PlatformDeviceRemove. | 
| + CFRetain(ref); | 
| + service->message_loop_->PostTask( | 
| + FROM_HERE, | 
| + base::Bind(&HidServiceMac::PlatformRemoveDevice, | 
| + base::Unretained(service), | 
| + base::Unretained(ref))); | 
| +} | 
| + | 
| +IOHIDDeviceRef HidServiceMac::FindDevice(std::string id) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
If you were just using the location ID as an inter
 
Mark Mentovai
2014/02/12 19:35:59
More thoughts on location ID:
Enumerating devices
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Leaving this question open for now.
 | 
| + base::ScopedCFTypeRef<CFSetRef> devices( | 
| + IOHIDManagerCopyDevices(hid_manager_ref_)); | 
| + CFIndex count = CFSetGetCount(devices); | 
| 
Mark Mentovai
2014/02/12 19:35:59
Hmm…this is kind of cumbersome.
 | 
| + scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); | 
| + CFSetGetValues(devices, (const void **)(device_refs.get())); | 
| + | 
| + for (CFIndex i = 0; i < count; i++) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
Usually use preincrement. Line 170 also. http://go
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + int32_t int_property = 0; | 
| + if (GetHidIntProperty(device_refs[i], CFSTR(kIOHIDLocationIDKey), | 
| + &int_property)) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
Weird indentation.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Ew.
 | 
| + if (id == base::HexEncode(&int_property, sizeof(int_property))) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
What’s with the weird stringification if these are
 | 
| + return device_refs[i]; | 
| + } | 
| + } | 
| + } | 
| + | 
| + return NULL; | 
| +} | 
| + | 
| +void HidServiceMac::Enumerate() { | 
| + base::ScopedCFTypeRef<CFSetRef> devices( | 
| + IOHIDManagerCopyDevices(hid_manager_ref_)); | 
| + CFIndex count = CFSetGetCount(devices); | 
| 
Mark Mentovai
2014/02/12 19:35:59
This is cumbersome just like line 146, and now you
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + scoped_ptr<IOHIDDeviceRef[]> device_refs(new IOHIDDeviceRef[count]); | 
| + CFSetGetValues(devices, (const void **)(device_refs.get())); | 
| + | 
| + for (CFIndex i = 0; i < count; i++) { | 
| + // Takes ownership. Will be released inside PlatformDeviceAdd. | 
| 
Mark Mentovai
2014/02/12 19:35:59
The function is actually named PlatformAddDevice.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + CFRetain(device_refs[i]); | 
| + PlatformAddDevice(device_refs[i]); | 
| + } | 
| +} | 
| + | 
| +void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef raw_ref) { | 
| + HidDeviceInfo device; | 
| + int32_t int_property = 0; | 
| + std::string str_property; | 
| + | 
| + // Auto-release. | 
| 
Mark Mentovai
2014/02/12 19:35:59
“Autorelease” has a special meaning in Mac program
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); | 
| + | 
| + // Unique identifier for HID device. | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { | 
| + device.device_id = base::HexEncode(&int_property, sizeof(int_property)); | 
| + } else { | 
| + // Not an available device. | 
| + return; | 
| + } | 
| + | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDVendorIDKey), &int_property)) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
All of this “if this succeeded then use its value”
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + device.vendor_id = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDProductIDKey), &int_property)) { | 
| + device.product_id = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsageKey), &int_property)) { | 
| + device.usage = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDPrimaryUsagePageKey), &int_property)) { | 
| + device.usage_page = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxInputReportSizeKey), | 
| + &int_property)) { | 
| + device.input_report_size = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxOutputReportSizeKey), | 
| + &int_property)) { | 
| + device.output_report_size = int_property; | 
| + } | 
| + if (GetHidIntProperty(ref, CFSTR(kIOHIDMaxFeatureReportSizeKey), | 
| + &int_property)) { | 
| 
Mark Mentovai
2014/02/12 19:35:59
Weird indentation
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + device.feature_report_size = int_property; | 
| + } | 
| + if (GetHidStringProperty(ref, CFSTR(kIOHIDProductKey), &str_property)) { | 
| + device.product_name = str_property; | 
| + } | 
| + if (GetHidStringProperty(ref, CFSTR(kIOHIDSerialNumberKey), &str_property)) { | 
| + device.serial_number = str_property; | 
| + } | 
| + HidService::AddDevice(device); | 
| +} | 
| + | 
| +void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef raw_ref) { | 
| + std::string device_id; | 
| + int32_t int_property = 0; | 
| + // Auto-release. | 
| 
Mark Mentovai
2014/02/12 19:35:59
Nope.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 
Ken Rockot(use gerrit already)
2014/02/13 00:04:57
Done.
 | 
| + base::ScopedCFTypeRef<IOHIDDeviceRef> ref(raw_ref); | 
| + if (!GetHidIntProperty(ref, CFSTR(kIOHIDLocationIDKey), &int_property)) { | 
| + return; | 
| + } | 
| + device_id = base::HexEncode(&int_property, sizeof(int_property)); | 
| + HidService::RemoveDevice(device_id); | 
| +} | 
| + | 
| +scoped_refptr<HidConnection> | 
| +HidServiceMac::Connect(std::string device_id) { | 
| + if (!ContainsKey(devices_, device_id)) | 
| + return NULL; | 
| + | 
| + IOHIDDeviceRef ref = FindDevice(device_id); | 
| + if (ref == NULL) | 
| + return NULL; | 
| + return scoped_refptr<HidConnection>( | 
| + new HidConnectionMac(this, devices_[device_id], ref)); | 
| +} | 
| + | 
| +} // namespace device |