Revert of Instrumented libraries: update the libnspr4 build script. (patchset #1...
[chromium-blink-merge.git] / device / hid / hid_service_mac.cc
blobfe4876141d7ff7b32554e80f0ff75b1e0809576b
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.
5 #include "device/hid/hid_service_mac.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <IOKit/hid/IOHIDManager.h>
10 #include <set>
11 #include <string>
12 #include <vector>
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/mac/foundation_util.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/message_loop/message_loop_proxy.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/sys_string_conversions.h"
22 #include "base/threading/thread_restrictions.h"
23 #include "device/hid/hid_connection_mac.h"
25 namespace device {
27 class HidServiceMac;
29 namespace {
31 typedef std::vector<IOHIDDeviceRef> HidDeviceList;
33 HidServiceMac* HidServiceFromContext(void* context) {
34 return static_cast<HidServiceMac*>(context);
37 // Callback for CFSetApplyFunction as used by EnumerateHidDevices.
38 void HidEnumerationBackInserter(const void* value, void* context) {
39 HidDeviceList* devices = static_cast<HidDeviceList*>(context);
40 const IOHIDDeviceRef device =
41 static_cast<IOHIDDeviceRef>(const_cast<void*>(value));
42 devices->push_back(device);
45 void EnumerateHidDevices(IOHIDManagerRef hid_manager,
46 HidDeviceList* device_list) {
47 DCHECK(device_list->size() == 0);
48 // Note that our ownership of each copied device is implied.
49 base::ScopedCFTypeRef<CFSetRef> devices(IOHIDManagerCopyDevices(hid_manager));
50 if (devices)
51 CFSetApplyFunction(devices, HidEnumerationBackInserter, device_list);
54 bool TryGetHidIntProperty(IOHIDDeviceRef device,
55 CFStringRef key,
56 int32_t* result) {
57 CFNumberRef ref =
58 base::mac::CFCast<CFNumberRef>(IOHIDDeviceGetProperty(device, key));
59 return ref && CFNumberGetValue(ref, kCFNumberSInt32Type, result);
62 int32_t GetHidIntProperty(IOHIDDeviceRef device, CFStringRef key) {
63 int32_t value;
64 if (TryGetHidIntProperty(device, key, &value))
65 return value;
66 return 0;
69 bool TryGetHidStringProperty(IOHIDDeviceRef device,
70 CFStringRef key,
71 std::string* result) {
72 CFStringRef ref =
73 base::mac::CFCast<CFStringRef>(IOHIDDeviceGetProperty(device, key));
74 if (!ref) {
75 return false;
77 *result = base::SysCFStringRefToUTF8(ref);
78 return true;
81 std::string GetHidStringProperty(IOHIDDeviceRef device, CFStringRef key) {
82 std::string value;
83 TryGetHidStringProperty(device, key, &value);
84 return value;
87 void GetReportIds(IOHIDElementRef element, std::set<int>* reportIDs) {
88 uint32_t reportID = IOHIDElementGetReportID(element);
89 if (reportID) {
90 reportIDs->insert(reportID);
93 CFArrayRef children = IOHIDElementGetChildren(element);
94 if (!children) {
95 return;
98 CFIndex childrenCount = CFArrayGetCount(children);
99 for (CFIndex j = 0; j < childrenCount; ++j) {
100 const IOHIDElementRef child = static_cast<IOHIDElementRef>(
101 const_cast<void*>(CFArrayGetValueAtIndex(children, j)));
102 GetReportIds(child, reportIDs);
106 void GetCollectionInfos(IOHIDDeviceRef device,
107 bool* has_report_id,
108 std::vector<HidCollectionInfo>* top_level_collections) {
109 STLClearObject(top_level_collections);
110 CFMutableDictionaryRef collections_filter =
111 CFDictionaryCreateMutable(kCFAllocatorDefault,
113 &kCFTypeDictionaryKeyCallBacks,
114 &kCFTypeDictionaryValueCallBacks);
115 const int kCollectionTypeValue = kIOHIDElementTypeCollection;
116 CFNumberRef collection_type_id = CFNumberCreate(
117 kCFAllocatorDefault, kCFNumberIntType, &kCollectionTypeValue);
118 CFDictionarySetValue(
119 collections_filter, CFSTR(kIOHIDElementTypeKey), collection_type_id);
120 CFRelease(collection_type_id);
121 CFArrayRef collections = IOHIDDeviceCopyMatchingElements(
122 device, collections_filter, kIOHIDOptionsTypeNone);
123 CFIndex collectionsCount = CFArrayGetCount(collections);
124 *has_report_id = false;
125 for (CFIndex i = 0; i < collectionsCount; i++) {
126 const IOHIDElementRef collection = static_cast<IOHIDElementRef>(
127 const_cast<void*>(CFArrayGetValueAtIndex(collections, i)));
128 // Top-Level Collection has no parent
129 if (IOHIDElementGetParent(collection) == 0) {
130 HidCollectionInfo collection_info;
131 HidUsageAndPage::Page page = static_cast<HidUsageAndPage::Page>(
132 IOHIDElementGetUsagePage(collection));
133 uint16_t usage = IOHIDElementGetUsage(collection);
134 collection_info.usage = HidUsageAndPage(usage, page);
135 // Explore children recursively and retrieve their report IDs
136 GetReportIds(collection, &collection_info.report_ids);
137 if (collection_info.report_ids.size() > 0) {
138 *has_report_id = true;
140 top_level_collections->push_back(collection_info);
145 } // namespace
147 HidServiceMac::HidServiceMac() {
148 DCHECK(thread_checker_.CalledOnValidThread());
149 message_loop_ = base::MessageLoopProxy::current();
150 DCHECK(message_loop_.get());
151 hid_manager_.reset(IOHIDManagerCreate(NULL, 0));
152 if (!hid_manager_) {
153 LOG(ERROR) << "Failed to initialize HidManager";
154 return;
156 DCHECK(CFGetTypeID(hid_manager_) == IOHIDManagerGetTypeID());
157 IOHIDManagerOpen(hid_manager_, kIOHIDOptionsTypeNone);
158 IOHIDManagerSetDeviceMatching(hid_manager_, NULL);
160 // Enumerate all the currently known devices.
161 Enumerate();
163 // Register for plug/unplug notifications.
164 StartWatchingDevices();
167 HidServiceMac::~HidServiceMac() {
168 StopWatchingDevices();
171 void HidServiceMac::StartWatchingDevices() {
172 DCHECK(thread_checker_.CalledOnValidThread());
173 IOHIDManagerRegisterDeviceMatchingCallback(
174 hid_manager_, &AddDeviceCallback, this);
175 IOHIDManagerRegisterDeviceRemovalCallback(
176 hid_manager_, &RemoveDeviceCallback, this);
177 IOHIDManagerScheduleWithRunLoop(
178 hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
181 void HidServiceMac::StopWatchingDevices() {
182 DCHECK(thread_checker_.CalledOnValidThread());
183 if (!hid_manager_)
184 return;
185 IOHIDManagerUnscheduleFromRunLoop(
186 hid_manager_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
187 IOHIDManagerClose(hid_manager_, kIOHIDOptionsTypeNone);
190 void HidServiceMac::AddDeviceCallback(void* context,
191 IOReturn result,
192 void* sender,
193 IOHIDDeviceRef hid_device) {
194 DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
195 // Claim ownership of the device.
196 CFRetain(hid_device);
197 HidServiceMac* service = HidServiceFromContext(context);
198 service->message_loop_->PostTask(FROM_HERE,
199 base::Bind(&HidServiceMac::PlatformAddDevice,
200 base::Unretained(service),
201 base::Unretained(hid_device)));
204 void HidServiceMac::RemoveDeviceCallback(void* context,
205 IOReturn result,
206 void* sender,
207 IOHIDDeviceRef hid_device) {
208 DCHECK(CFRunLoopGetMain() == CFRunLoopGetCurrent());
209 HidServiceMac* service = HidServiceFromContext(context);
210 service->message_loop_->PostTask(
211 FROM_HERE,
212 base::Bind(&HidServiceMac::PlatformRemoveDevice,
213 base::Unretained(service),
214 base::Unretained(hid_device)));
217 void HidServiceMac::Enumerate() {
218 DCHECK(thread_checker_.CalledOnValidThread());
219 HidDeviceList devices;
220 EnumerateHidDevices(hid_manager_, &devices);
221 for (HidDeviceList::const_iterator iter = devices.begin();
222 iter != devices.end();
223 ++iter) {
224 IOHIDDeviceRef hid_device = *iter;
225 PlatformAddDevice(hid_device);
229 void HidServiceMac::PlatformAddDevice(IOHIDDeviceRef hid_device) {
230 // Note that our ownership of hid_device is implied if calling this method.
231 // It is balanced in PlatformRemoveDevice.
232 DCHECK(thread_checker_.CalledOnValidThread());
233 HidDeviceInfo device_info;
234 device_info.device_id = hid_device;
235 device_info.vendor_id =
236 GetHidIntProperty(hid_device, CFSTR(kIOHIDVendorIDKey));
237 device_info.product_id =
238 GetHidIntProperty(hid_device, CFSTR(kIOHIDProductIDKey));
239 device_info.product_name =
240 GetHidStringProperty(hid_device, CFSTR(kIOHIDProductKey));
241 device_info.serial_number =
242 GetHidStringProperty(hid_device, CFSTR(kIOHIDSerialNumberKey));
243 GetCollectionInfos(hid_device,
244 &device_info.has_report_id,
245 &device_info.collections);
246 device_info.max_input_report_size =
247 GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxInputReportSizeKey));
248 if (device_info.has_report_id && device_info.max_input_report_size > 0) {
249 device_info.max_input_report_size--;
251 device_info.max_output_report_size =
252 GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxOutputReportSizeKey));
253 if (device_info.has_report_id && device_info.max_output_report_size > 0) {
254 device_info.max_output_report_size--;
256 device_info.max_feature_report_size =
257 GetHidIntProperty(hid_device, CFSTR(kIOHIDMaxFeatureReportSizeKey));
258 if (device_info.has_report_id && device_info.max_feature_report_size > 0) {
259 device_info.max_feature_report_size--;
261 AddDevice(device_info);
264 void HidServiceMac::PlatformRemoveDevice(IOHIDDeviceRef hid_device) {
265 DCHECK(thread_checker_.CalledOnValidThread());
266 RemoveDevice(hid_device);
267 CFRelease(hid_device);
270 scoped_refptr<HidConnection> HidServiceMac::Connect(
271 const HidDeviceId& device_id) {
272 DCHECK(thread_checker_.CalledOnValidThread());
273 HidDeviceInfo device_info;
274 if (!GetDeviceInfo(device_id, &device_info))
275 return NULL;
276 return scoped_refptr<HidConnection>(new HidConnectionMac(device_info));
279 } // namespace device