Chromium Code Reviews| Index: content/browser/device_monitor_mac.cc |
| diff --git a/content/browser/device_monitor_mac.cc b/content/browser/device_monitor_mac.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..0bc69a96029c04f715d5d5f55ab30dcb774f92ee |
| --- /dev/null |
| +++ b/content/browser/device_monitor_mac.cc |
| @@ -0,0 +1,147 @@ |
| +// Copyright (c) 2012 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 "content/browser/device_monitor_mac.h" |
| + |
| +#include <IOKit/usb/IOUSBLib.h> |
| + |
| +#include "base/logging.h" |
| +#include "base/mac/scoped_cftyperef.h" |
| +#include "base/mac/scoped_ioobject.h" |
| + |
| +namespace content { |
| + |
| +namespace { |
| + |
| +struct { |
|
no longer working on chromium
2012/08/08 09:23:16
We can't use const struct here because we need to
Mark Mentovai
2012/08/08 12:38:03
That’s exactly the problem I was illustrating. It
|
| + base::SystemMonitor::DeviceType device_type; |
| + const io_name_t service_type; |
| +} kDeviceServices[] = { |
| + // Add new services here if needed. |
| + { base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE, kIOMatchedNotification }, |
| + { base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE, kIOTerminatedNotification }, |
| + { base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kIOMatchedNotification }, |
| + { base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE, kIOTerminatedNotification }, |
| +}; |
| + |
| +CFMutableDictionaryRef CreateMatchingDictionary( |
| + SInt32 interface_class_code, SInt32 interface_subclass_code) { |
| + CFMutableDictionaryRef matching_dictionary = IOServiceMatching( |
| + kIOUSBInterfaceClassName); |
| + base::mac::ScopedCFTypeRef<CFNumberRef> number_ref(CFNumberCreate( |
| + kCFAllocatorDefault, kCFNumberSInt32Type, &interface_class_code)); |
| + DCHECK(number_ref); |
| + CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBInterfaceClass), |
| + number_ref); |
| + |
| + number_ref.reset(CFNumberCreate(kCFAllocatorDefault, |
| + kCFNumberSInt32Type, |
| + &interface_subclass_code)); |
| + DCHECK(number_ref); |
| + CFDictionaryAddValue(matching_dictionary, CFSTR(kUSBInterfaceSubClass), |
| + number_ref); |
| + |
| + return matching_dictionary; |
| +} |
| + |
| +void AddCallbackToIOService(IONotificationPortRef port, |
| + const io_name_t type, |
| + CFMutableDictionaryRef dictionary, |
| + IOServiceMatchingCallback callback, |
| + void* context, |
| + io_iterator_t* notification) { |
| + kern_return_t err = IOServiceAddMatchingNotification(port, |
| + type, |
| + dictionary, |
| + callback, |
| + context, |
| + notification); |
| + if (err) { |
| + NOTREACHED() << "Failed to register the IO matched notification for type " |
| + << type; |
| + return; |
| + } |
| + DCHECK(*notification); |
| + |
| + // Iterate over set of matching devices to access already-present devices |
| + // and to arm the notification. |
| + base::mac::ScopedIOObject<io_service_t> this_object( |
| + IOIteratorNext(*notification)); |
| + for (; this_object; this_object.reset(IOIteratorNext(*notification))); |
| +} |
| + |
| +} // namespace |
| + |
| +DeviceMonitorMac::DeviceMonitorMac() { |
| + |
| + // Add the notification port to the run loop. |
| + notification_port_ = IONotificationPortCreate(kIOMasterPortDefault); |
| + DCHECK(notification_port_); |
| + |
| + RegisterServices(); |
| + |
| + CFRunLoopAddSource(CFRunLoopGetCurrent(), |
| + IONotificationPortGetRunLoopSource(notification_port_), |
| + kCFRunLoopCommonModes); |
| +} |
| + |
| +DeviceMonitorMac::~DeviceMonitorMac() { |
| + // Stop the notifications and free the objects. |
| + for (size_t i = 0; i < arraysize(kDeviceServices); ++i) { |
| + IOObjectRelease(notification_iterators_[i]); |
| + } |
| + |
| + // Remove the sleep notification port from the application runloop. |
| + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), |
| + IONotificationPortGetRunLoopSource(notification_port_), |
| + kCFRunLoopCommonModes); |
| + |
| + // Destroy the notification port allocated by IONotificationPortCreate. |
| + IONotificationPortDestroy(notification_port_); |
| +} |
| + |
| +void DeviceMonitorMac::RegisterServices() { |
| + notification_iterators_.reset(new io_iterator_t[arraysize(kDeviceServices)]); |
| + CFMutableDictionaryRef matching_dictionary; |
| + for (size_t i = 0; i < arraysize(kDeviceServices); ++i) { |
| + switch (kDeviceServices[i].device_type) { |
| + case base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE: |
| + matching_dictionary = CreateMatchingDictionary( |
| + kUSBAudioInterfaceClass, kUSBAudioControlSubClass); |
| + break; |
| + case base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE: |
| + matching_dictionary = CreateMatchingDictionary( |
| + kUSBVideoInterfaceClass, kUSBVideoControlSubClass); |
| + break; |
| + default: |
| + NOTREACHED(); |
| + return; |
| + } |
| + |
| + // Add callback to the service. |
| + AddCallbackToIOService(notification_port_, |
| + kDeviceServices[i].service_type, |
| + matching_dictionary, |
| + &DeviceChangedCallback, |
| + static_cast<void*>(&kDeviceServices[i].device_type), |
| + ¬ification_iterators_[i]); |
| + } |
| +} |
| + |
| +void DeviceMonitorMac::DeviceChangedCallback(void *context, |
| + io_iterator_t iterator) { |
| + base::mac::ScopedIOObject<io_service_t> this_object(IOIteratorNext(iterator)); |
| + for (; this_object; this_object.reset(IOIteratorNext(iterator))) { |
| + if (context) { |
| + base::SystemMonitor::DeviceType device_type = |
| + *reinterpret_cast<base::SystemMonitor::DeviceType*>(context); |
| + DCHECK(device_type == base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE || |
| + device_type == base::SystemMonitor::DEVTYPE_VIDEO_CAPTURE); |
| + // TODO(xians): Remove the global variable for SystemMonitor. |
| + base::SystemMonitor::Get()->ProcessDevicesChanged(device_type); |
| + } |
| + } |
| +} |
| + |
| +} // namespace content |