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"
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.";
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
<< "'.";
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.";
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.";
75 base::ScopedCFTypeRef
<CFDictionaryRef
> disk_description(
76 DADiskCopyDescription(disk
));
77 if (disk_description
.get() == NULL
) {
78 VLOG(1) << "Error getting disk description.";
82 CFUUIDRef volume_uuid
= base::mac::GetValueFromDictionary
<CFUUIDRef
>(
84 kDADiskDescriptionVolumeUUIDKey
);
85 if (volume_uuid
== NULL
) {
86 VLOG(1) << "Error getting volume UUID of disk.";
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.";
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();
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
);
113 class MacAddressProcessor
{
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
),
128 if (!mac_address_data
)
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
))
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
),
145 if (provider_class
) {
146 if (CFStringCompare(provider_class
, CFSTR("IOPCIDevice"), 0) ==
148 // MAC address from built-in network card is always best choice.
149 found_mac_address_
= mac_address_string
;
155 // Fall back to using non built-in card MAC address, but keep looking.
156 found_mac_address_
= mac_address_string
;
160 std::string
mac_address() const { return found_mac_address_
; }
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
;
178 CFMutableDictionaryRef match_classes
=
179 IOServiceMatching(kIOEthernetInterfaceClass
);
180 if (!match_classes
) {
181 LOG(ERROR
) << "IOServiceMatching returned a NULL dictionary";
185 io_iterator_t iterator_ref
;
186 kr
= IOServiceGetMatchingServices(master_port
,
189 if (kr
!= KERN_SUCCESS
) {
190 LOG(ERROR
) << "IOServiceGetMatchingServices failed: " << kr
;
193 base::mac::ScopedIOObject
<io_iterator_t
> iterator(iterator_ref
);
195 MacAddressProcessor
processor(is_valid_mac_address
);
197 // Note: interface_service should not be released.
198 io_object_t interface_service
= IOIteratorNext(iterator
);
199 if (!interface_service
)
202 io_object_t controller_service_ref
;
203 kr
= IORegistryEntryGetParentEntry(interface_service
,
205 &controller_service_ref
);
206 if (kr
!= KERN_SUCCESS
) {
207 LOG(ERROR
) << "IORegistryEntryGetParentEntry failed: " << kr
;
209 base::mac::ScopedIOObject
<io_object_t
> controller_service(
210 controller_service_ref
);
211 bool keep_going
= processor
.ProcessNetworkController(controller_service
);
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
,
233 base::Bind(callback
, raw_device_id
));
238 namespace extensions
{
242 void DeviceId::GetRawDeviceId(const IdCallback
& callback
) {
243 DCHECK_CURRENTLY_ON(content::BrowserThread::UI
);
245 content::BrowserThread::PostTask(
246 content::BrowserThread::FILE,
248 base::Bind(GetRawDeviceIdImpl
,
249 base::Bind(DeviceId::IsValidMacAddress
),
254 } // namespace extensions