Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / music_manager_private / device_id_mac.cc
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.
4
5 #include "chrome/browser/extensions/api/music_manager_private/device_id.h"
6
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>
15
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"
25
26 namespace {
27
28 using extensions::api::DeviceId;
29
30 const char kRootDirectory[] = "/";
31
32 typedef base::Callback<bool(const void* bytes, size_t size)>
33     IsValidMacAddressCallback;
34
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();
44   }
45
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);
50     }
51   }
52
53   VLOG(1) << "Cannot find disk mounted as '" << kRootDirectory << "'.";
54   return std::string();
55 }
56
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;
61
62   base::ScopedCFTypeRef<DASessionRef> session(DASessionCreate(allocator));
63   if (session.get() == NULL) {
64     VLOG(1) << "Error creating DA Session.";
65     return std::string();
66   }
67
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();
73   }
74
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();
80   }
81
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();
88   }
89
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();
95   }
96
97   return base::SysCFStringRefToUTF8(volume_uuid_string.get());
98 }
99
100 // Return Volume UUID property of disk mounted as "/".
101 std::string GetVolumeUUID() {
102   base::ThreadRestrictions::AssertIOAllowed();
103
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);
109   }
110   return result;
111 }
112
113 class MacAddressProcessor {
114  public:
115   MacAddressProcessor(const IsValidMacAddressCallback& is_valid_mac_address)
116     : is_valid_mac_address_(is_valid_mac_address) {
117   }
118
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;
130
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;
135
136     std::string mac_address_string = base::StringToLowerASCII(base::HexEncode(
137         mac_address, mac_address_size));
138
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;
152       }
153     }
154
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;
158   }
159
160   std::string mac_address() const { return found_mac_address_; }
161
162  private:
163   const IsValidMacAddressCallback& is_valid_mac_address_;
164   std::string found_mac_address_;
165 };
166
167 std::string GetMacAddress(
168     const IsValidMacAddressCallback& is_valid_mac_address) {
169   base::ThreadRestrictions::AssertIOAllowed();
170
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 "";
176   }
177
178   CFMutableDictionaryRef match_classes =
179       IOServiceMatching(kIOEthernetInterfaceClass);
180   if (!match_classes) {
181     LOG(ERROR) << "IOServiceMatching returned a NULL dictionary";
182     return "";
183   }
184
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 "";
192   }
193   base::mac::ScopedIOObject<io_iterator_t> iterator(iterator_ref);
194
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;
201
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;
214       }
215     }
216   }
217   return processor.mac_address();
218 }
219
220 void GetRawDeviceIdImpl(const IsValidMacAddressCallback& is_valid_mac_address,
221                         const DeviceId::IdCallback& callback) {
222   base::ThreadRestrictions::AssertIOAllowed();
223
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;
229   }
230   content::BrowserThread::PostTask(
231       content::BrowserThread::UI,
232       FROM_HERE,
233       base::Bind(callback, raw_device_id));
234 }
235
236 }  // namespace
237
238 namespace extensions {
239 namespace api {
240
241 // static
242 void DeviceId::GetRawDeviceId(const IdCallback& callback) {
243   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
244
245   content::BrowserThread::PostTask(
246       content::BrowserThread::FILE,
247       FROM_HERE,
248       base::Bind(GetRawDeviceIdImpl,
249           base::Bind(DeviceId::IsValidMacAddress),
250           callback));
251 }
252
253 }  // namespace api
254 }  // namespace extensions