1 // Copyright (c) 2012 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 "base/ios/device_util.h" 6 7 #include <CommonCrypto/CommonDigest.h> 8 #import <UIKit/UIKit.h> 9 10 #include <ifaddrs.h> 11 #include <net/if_dl.h> 12 #include <string.h> 13 #include <sys/socket.h> 14 #include <sys/sysctl.h> 15 16 #include "base/ios/ios_util.h" 17 #include "base/logging.h" 18 #include "base/mac/scoped_cftyperef.h" 19 #include "base/memory/scoped_ptr.h" 20 #include "base/strings/string_util.h" 21 #include "base/strings/stringprintf.h" 22 #include "base/strings/sys_string_conversions.h" 23 24 namespace { 25 26 // Client ID key in the user preferences. 27 NSString* const kLegacyClientIdPreferenceKey = @"ChromiumClientID"; 28 NSString* const kClientIdPreferenceKey = @"ChromeClientID"; 29 // Current hardware type. This is used to detect that a device has been backed 30 // up and restored to another device, and allows regenerating a new device id. 31 NSString* const kHardwareTypePreferenceKey = @"ClientIDGenerationHardwareType"; 32 // Default salt for device ids. 33 const char kDefaultSalt[] = "Salt"; 34 // Zero UUID returned on buggy iOS devices. 35 NSString* const kZeroUUID = @"00000000-0000-0000-0000-000000000000"; 36 37 NSString* GenerateClientId() { 38 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 39 40 // Try to migrate from legacy client id. 41 NSString* client_id = [defaults stringForKey:kLegacyClientIdPreferenceKey]; 42 43 // Some iOS6 devices return a buggy identifierForVendor: 44 // http://openradar.appspot.com/12377282. If this is the case, revert to 45 // generating a new one. 46 if (!client_id || [client_id isEqualToString:kZeroUUID]) { 47 if (base::ios::IsRunningOnIOS6OrLater()) { 48 client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; 49 if ([client_id isEqualToString:kZeroUUID]) 50 client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); 51 } else { 52 client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); 53 } 54 } 55 return client_id; 56 } 57 58 } // namespace 59 60 namespace ios { 61 namespace device_util { 62 63 std::string GetPlatform() { 64 std::string platform; 65 size_t size = 0; 66 sysctlbyname("hw.machine", NULL, &size, NULL, 0); 67 sysctlbyname("hw.machine", WriteInto(&platform, size), &size, NULL, 0); 68 return platform; 69 } 70 71 bool IsRunningOnHighRamDevice() { 72 uint64_t memory_size = 0; 73 size_t size = sizeof(memory_size); 74 if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) { 75 // Anything >= 250M, call high ram. 76 return memory_size >= 250 * 1024 * 1024; 77 } 78 return false; 79 } 80 81 bool IsSingleCoreDevice() { 82 uint64_t cpu_number = 0; 83 size_t sizes = sizeof(cpu_number); 84 sysctlbyname("hw.physicalcpu", &cpu_number, &sizes, NULL, 0); 85 return cpu_number == 1; 86 } 87 88 std::string GetMacAddress(const std::string& interface_name) { 89 std::string mac_string; 90 struct ifaddrs* addresses; 91 if (getifaddrs(&addresses) == 0) { 92 for (struct ifaddrs* address = addresses; address; 93 address = address->ifa_next) { 94 if ((address->ifa_addr->sa_family == AF_LINK) && 95 strcmp(interface_name.c_str(), address->ifa_name) == 0) { 96 const struct sockaddr_dl* found_address_struct = 97 reinterpret_cast<const struct sockaddr_dl*>(address->ifa_addr); 98 99 // |found_address_struct->sdl_data| contains the interface name followed 100 // by the interface address. The address part can be accessed based on 101 // the length of the name, that is, |found_address_struct->sdl_nlen|. 102 const unsigned char* found_address = 103 reinterpret_cast<const unsigned char*>( 104 &found_address_struct->sdl_data[ 105 found_address_struct->sdl_nlen]); 106 107 int found_address_length = found_address_struct->sdl_alen; 108 for (int i = 0; i < found_address_length; ++i) { 109 if (i != 0) 110 mac_string.push_back(':'); 111 base::StringAppendF(&mac_string, "%02X", found_address[i]); 112 } 113 break; 114 } 115 } 116 freeifaddrs(addresses); 117 } 118 return mac_string; 119 } 120 121 std::string GetRandomId() { 122 base::ScopedCFTypeRef<CFUUIDRef> uuid_object( 123 CFUUIDCreate(kCFAllocatorDefault)); 124 base::ScopedCFTypeRef<CFStringRef> uuid_string( 125 CFUUIDCreateString(kCFAllocatorDefault, uuid_object)); 126 return base::SysCFStringRefToUTF8(uuid_string); 127 } 128 129 std::string GetDeviceIdentifier(const char* salt) { 130 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 131 132 NSString* last_seen_hardware = 133 [defaults stringForKey:kHardwareTypePreferenceKey]; 134 NSString* current_hardware = base::SysUTF8ToNSString(GetPlatform()); 135 if (!last_seen_hardware) { 136 last_seen_hardware = current_hardware; 137 [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; 138 [defaults synchronize]; 139 } 140 141 NSString* client_id = [defaults stringForKey:kClientIdPreferenceKey]; 142 143 if (!client_id || ![last_seen_hardware isEqualToString:current_hardware]) { 144 client_id = GenerateClientId(); 145 [defaults setObject:client_id forKey:kClientIdPreferenceKey]; 146 [defaults setObject:current_hardware forKey:kHardwareTypePreferenceKey]; 147 [defaults synchronize]; 148 } 149 150 NSData* hash_data = [[NSString stringWithFormat:@"%@%s", client_id, 151 salt ? salt : kDefaultSalt] dataUsingEncoding:NSUTF8StringEncoding]; 152 153 unsigned char hash[CC_SHA256_DIGEST_LENGTH]; 154 CC_SHA256([hash_data bytes], [hash_data length], hash); 155 CFUUIDBytes* uuid_bytes = reinterpret_cast<CFUUIDBytes*>(hash); 156 157 base::ScopedCFTypeRef<CFUUIDRef> uuid_object( 158 CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuid_bytes)); 159 base::ScopedCFTypeRef<CFStringRef> device_id( 160 CFUUIDCreateString(kCFAllocatorDefault, uuid_object)); 161 return base::SysCFStringRefToUTF8(device_id); 162 } 163 164 } // namespace device_util 165 } // namespace ios 166