mac: Add metrics to record Bluetooth availability and capabilities.
The new Handoff feature in OSX 10.10 only works with devices that support
Bluetooth LE. Record metrics to determine the percentage of Chrome users that
this affects.
BUG=392166
Review URL: https://codereview.chromium.org/374203004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@282771 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/mac/bluetooth_utility.mm b/chrome/browser/mac/bluetooth_utility.mm
new file mode 100644
index 0000000..26824d9e
--- /dev/null
+++ b/chrome/browser/mac/bluetooth_utility.mm
@@ -0,0 +1,82 @@
+// Copyright 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 "chrome/browser/mac/bluetooth_utility.h"
+
+#import <Foundation/Foundation.h>
+#include <IOKit/IOKitLib.h>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_util.h"
+#include "base/mac/scoped_ioobject.h"
+#include "base/mac/sdk_forward_declarations.h"
+
+namespace bluetooth_utility {
+
+BluetoothAvailability GetBluetoothAvailability() {
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> matching_dict(
+ IOServiceMatching("IOBluetoothHCIController"));
+ if (!matching_dict)
+ return BLUETOOTH_AVAILABILITY_ERROR;
+
+ // IOServiceGetMatchingServices takes ownership of matching_dict.
+ io_iterator_t iter;
+ int kr = IOServiceGetMatchingServices(
+ kIOMasterPortDefault, matching_dict.release(), &iter);
+ if (kr != KERN_SUCCESS)
+ return BLUETOOTH_NOT_AVAILABLE;
+ base::mac::ScopedIOObject<io_iterator_t> scoped_iter(iter);
+
+ int bluetooth_available = false;
+ base::mac::ScopedIOObject<io_service_t> device;
+ while (device.reset(IOIteratorNext(scoped_iter.get())), device) {
+ bluetooth_available = true;
+
+ CFMutableDictionaryRef dict;
+ kr = IORegistryEntryCreateCFProperties(
+ device, &dict, kCFAllocatorDefault, kNilOptions);
+ if (kr != KERN_SUCCESS)
+ continue;
+ base::ScopedCFTypeRef<CFMutableDictionaryRef> scoped_dict(dict);
+
+ NSDictionary* objc_dict = base::mac::CFToNSCast(scoped_dict.get());
+ NSNumber* lmp_version =
+ base::mac::ObjCCast<NSNumber>([objc_dict objectForKey:@"LMPVersion"]);
+ if (!lmp_version)
+ continue;
+
+ // The LMP version is too low to support Bluetooth LE.
+ if ([lmp_version intValue] < 6)
+ continue;
+
+ // Check the supported features registry entry for Bluetooth LE
+ // availability. The relevant bit has a different meaning on OSX 10.6, and
+ // could change again in the future.
+ if (base::mac::IsOSSnowLeopard())
+ return BLUETOOTH_AVAILABLE_LE_UNKNOWN;
+
+ NSData* data = base::mac::ObjCCast<NSData>(
+ [objc_dict objectForKey:@"HCISupportedFeatures"]);
+
+ NSUInteger supported_features_index = 4;
+ NSUInteger length = [data length];
+ if (length < supported_features_index + 1)
+ continue;
+
+ // The bytes are indexed in reverse order.
+ NSUInteger index = length - supported_features_index - 1;
+
+ const unsigned char* bytes =
+ static_cast<const unsigned char*>([data bytes]);
+ const unsigned char byte = bytes[index];
+ bool le_supported = byte & kBluetoothFeatureLESupportedController;
+ if (le_supported)
+ return BLUETOOTH_AVAILABLE_WITH_LE;
+ }
+
+ return bluetooth_available ? BLUETOOTH_AVAILABLE_WITHOUT_LE
+ : BLUETOOTH_AVAILABILITY_ERROR;
+}
+
+} // namespace bluetooth_utility