Home | History | Annotate | Download | only in music_manager_private
      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
    255