Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / api / music_manager_private / device_id_mac.cc
blob06a3ba79db505744f4670389177d65b91a6c9c0f
1 // Copyright 2013 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 "chrome/browser/extensions/api/music_manager_private/device_id.h"
7 #include <CoreFoundation/CoreFoundation.h>
8 #include <DiskArbitration/DASession.h>
9 #include <DiskArbitration/DADisk.h>
10 #include <IOKit/IOKitLib.h>
11 #include <IOKit/network/IOEthernetController.h>
12 #include <IOKit/network/IOEthernetInterface.h>
13 #include <IOKit/network/IONetworkInterface.h>
14 #include <sys/mount.h>
16 #include "base/bind.h"
17 #include "base/mac/foundation_util.h"
18 #include "base/mac/scoped_cftyperef.h"
19 #include "base/mac/scoped_ioobject.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_util.h"
22 #include "base/strings/sys_string_conversions.h"
23 #include "base/threading/thread_restrictions.h"
24 #include "content/public/browser/browser_thread.h"
26 namespace {
28 using extensions::api::DeviceId;
30 const char kRootDirectory[] = "/";
32 typedef base::Callback<bool(const void* bytes, size_t size)>
33 IsValidMacAddressCallback;
35 // Return the BSD name (e.g. '/dev/disk1') of the root directory by enumerating
36 // through the mounted volumes .
37 // Return "" if an error occured.
38 std::string FindBSDNameOfSystemDisk() {
39 struct statfs* mounted_volumes;
40 int num_volumes = getmntinfo(&mounted_volumes, 0);
41 if (num_volumes == 0) {
42 VLOG(1) << "Cannot enumerate list of mounted volumes.";
43 return std::string();
46 for (int i = 0; i < num_volumes; i++) {
47 struct statfs* vol = &mounted_volumes[i];
48 if (std::string(vol->f_mntonname) == kRootDirectory) {
49 return std::string(vol->f_mntfromname);
53 VLOG(1) << "Cannot find disk mounted as '" << kRootDirectory << "'.";
54 return std::string();
57 // Return the Volume UUID property of a BSD disk name (e.g. '/dev/disk1').
58 // Return "" if an error occured.
59 std::string GetVolumeUUIDFromBSDName(const std::string& bsd_name) {
60 const CFAllocatorRef allocator = NULL;
62 base::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(allocator));
63 if (session.get() == NULL) {
64 VLOG(1) << "Error creating DA Session.";
65 return std::string();
68 base::ScopedCFTypeRef<DADiskRef> disk(
69 DADiskCreateFromBSDName(allocator, session, bsd_name.c_str()));
70 if (disk.get() == NULL) {
71 VLOG(1) << "Error creating DA disk from BSD disk name.";
72 return std::string();
75 base::ScopedCFTypeRef<CFDictionaryRef> disk_description(
76 DADiskCopyDescription(disk));
77 if (disk_description.get() == NULL) {
78 VLOG(1) << "Error getting disk description.";
79 return std::string();
82 CFUUIDRef volume_uuid = base::mac::GetValueFromDictionary<CFUUIDRef>(
83 disk_description,
84 kDADiskDescriptionVolumeUUIDKey);
85 if (volume_uuid == NULL) {
86 VLOG(1) << "Error getting volume UUID of disk.";
87 return std::string();
90 base::ScopedCFTypeRef<CFStringRef> volume_uuid_string(
91 CFUUIDCreateString(allocator, volume_uuid));
92 if (volume_uuid_string.get() == NULL) {
93 VLOG(1) << "Error creating string from CSStringRef.";
94 return std::string();
97 return base::SysCFStringRefToUTF8(volume_uuid_string.get());
100 // Return Volume UUID property of disk mounted as "/".
101 std::string GetVolumeUUID() {
102 base::ThreadRestrictions::AssertIOAllowed();
104 std::string result;
105 std::string bsd_name = FindBSDNameOfSystemDisk();
106 if (!bsd_name.empty()) {
107 VLOG(4) << "BSD name of root directory: '" << bsd_name << "'";
108 result = GetVolumeUUIDFromBSDName(bsd_name);
110 return result;
113 class MacAddressProcessor {
114 public:
115 MacAddressProcessor(const IsValidMacAddressCallback& is_valid_mac_address)
116 : is_valid_mac_address_(is_valid_mac_address) {
119 bool ProcessNetworkController(io_object_t network_controller) {
120 // Use the MAC address of the first network interface.
121 bool keep_going = true;
122 base::ScopedCFTypeRef<CFDataRef> mac_address_data(
123 static_cast<CFDataRef>(
124 IORegistryEntryCreateCFProperty(network_controller,
125 CFSTR(kIOMACAddress),
126 kCFAllocatorDefault,
127 0)));
128 if (!mac_address_data)
129 return keep_going;
131 const UInt8* mac_address = CFDataGetBytePtr(mac_address_data);
132 size_t mac_address_size = CFDataGetLength(mac_address_data);
133 if (!is_valid_mac_address_.Run(mac_address, mac_address_size))
134 return keep_going;
136 std::string mac_address_string = base::ToLowerASCII(base::HexEncode(
137 mac_address, mac_address_size));
139 base::ScopedCFTypeRef<CFStringRef> provider_class(
140 static_cast<CFStringRef>(
141 IORegistryEntryCreateCFProperty(network_controller,
142 CFSTR(kIOProviderClassKey),
143 kCFAllocatorDefault,
144 0)));
145 if (provider_class) {
146 if (CFStringCompare(provider_class, CFSTR("IOPCIDevice"), 0) ==
147 kCFCompareEqualTo) {
148 // MAC address from built-in network card is always best choice.
149 found_mac_address_ = mac_address_string;
150 keep_going = false;
151 return keep_going;
155 // Fall back to using non built-in card MAC address, but keep looking.
156 found_mac_address_ = mac_address_string;
157 return keep_going;
160 std::string mac_address() const { return found_mac_address_; }
162 private:
163 const IsValidMacAddressCallback& is_valid_mac_address_;
164 std::string found_mac_address_;
167 std::string GetMacAddress(
168 const IsValidMacAddressCallback& is_valid_mac_address) {
169 base::ThreadRestrictions::AssertIOAllowed();
171 mach_port_t master_port;
172 kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &master_port);
173 if (kr != KERN_SUCCESS) {
174 LOG(ERROR) << "IOMasterPort failed: " << kr;
175 return "";
178 CFMutableDictionaryRef match_classes =
179 IOServiceMatching(kIOEthernetInterfaceClass);
180 if (!match_classes) {
181 LOG(ERROR) << "IOServiceMatching returned a NULL dictionary";
182 return "";
185 io_iterator_t iterator_ref;
186 kr = IOServiceGetMatchingServices(master_port,
187 match_classes,
188 &iterator_ref);
189 if (kr != KERN_SUCCESS) {
190 LOG(ERROR) << "IOServiceGetMatchingServices failed: " << kr;
191 return "";
193 base::mac::ScopedIOObject<io_iterator_t> iterator(iterator_ref);
195 MacAddressProcessor processor(is_valid_mac_address);
196 while (true) {
197 // Note: interface_service should not be released.
198 io_object_t interface_service = IOIteratorNext(iterator);
199 if (!interface_service)
200 break;
202 io_object_t controller_service_ref;
203 kr = IORegistryEntryGetParentEntry(interface_service,
204 kIOServicePlane,
205 &controller_service_ref);
206 if (kr != KERN_SUCCESS) {
207 LOG(ERROR) << "IORegistryEntryGetParentEntry failed: " << kr;
208 } else {
209 base::mac::ScopedIOObject<io_object_t> controller_service(
210 controller_service_ref);
211 bool keep_going = processor.ProcessNetworkController(controller_service);
212 if (!keep_going) {
213 break;
217 return processor.mac_address();
220 void GetRawDeviceIdImpl(const IsValidMacAddressCallback& is_valid_mac_address,
221 const DeviceId::IdCallback& callback) {
222 base::ThreadRestrictions::AssertIOAllowed();
224 std::string raw_device_id;
225 std::string mac_address = GetMacAddress(is_valid_mac_address);
226 std::string disk_id = GetVolumeUUID();
227 if (!mac_address.empty() && !disk_id.empty()) {
228 raw_device_id = mac_address + disk_id;
230 content::BrowserThread::PostTask(
231 content::BrowserThread::UI,
232 FROM_HERE,
233 base::Bind(callback, raw_device_id));
236 } // namespace
238 namespace extensions {
239 namespace api {
241 // static
242 void DeviceId::GetRawDeviceId(const IdCallback& callback) {
243 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
245 content::BrowserThread::PostTask(
246 content::BrowserThread::FILE,
247 FROM_HERE,
248 base::Bind(GetRawDeviceIdImpl,
249 base::Bind(DeviceId::IsValidMacAddress),
250 callback));
253 } // namespace api
254 } // namespace extensions