1// Copyright 2012 The Chromium Authors 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#include <ifaddrs.h> 10#include <net/if_dl.h> 11#include <stddef.h> 12#include <string.h> 13#include <sys/socket.h> 14#include <sys/sysctl.h> 15 16#include <memory> 17 18#include "base/apple/scoped_cftyperef.h" 19#include "base/check.h" 20#include "base/numerics/safe_conversions.h" 21#include "base/posix/sysctl.h" 22#include "base/strings/stringprintf.h" 23#include "base/strings/sys_string_conversions.h" 24#include "base/system/sys_info.h" 25 26namespace { 27 28// Client ID key in the user preferences. 29NSString* const kLegacyClientIdPreferenceKey = @"ChromiumClientID"; 30NSString* const kClientIdPreferenceKey = @"ChromeClientID"; 31// Current hardware type. This is used to detect that a device has been backed 32// up and restored to another device, and allows regenerating a new device id. 33NSString* const kHardwareTypePreferenceKey = @"ClientIDGenerationHardwareType"; 34// Default salt for device ids. 35const char kDefaultSalt[] = "Salt"; 36// Zero UUID returned on buggy iOS devices. 37NSString* const kZeroUUID = @"00000000-0000-0000-0000-000000000000"; 38 39NSString* GenerateClientId() { 40 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 41 42 // Try to migrate from legacy client id. 43 NSString* client_id = [defaults stringForKey:kLegacyClientIdPreferenceKey]; 44 45 // Some iOS6 devices return a buggy identifierForVendor: 46 // https://openradar.appspot.com/12377282. If this is the case, revert to 47 // generating a new one. 48 if (!client_id || [client_id isEqualToString:kZeroUUID]) { 49 client_id = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; 50 if ([client_id isEqualToString:kZeroUUID]) 51 client_id = base::SysUTF8ToNSString(ios::device_util::GetRandomId()); 52 } 53 return client_id; 54} 55 56} // namespace 57 58namespace ios::device_util { 59 60bool RamIsAtLeast512Mb() { 61 // 512MB devices report anywhere from 502-504 MB, use 450 MB just to be safe. 62 return RamIsAtLeast(450); 63} 64 65bool RamIsAtLeast1024Mb() { 66 // 1GB devices report anywhere from 975-999 MB, use 900 MB just to be safe. 67 return RamIsAtLeast(900); 68} 69 70bool RamIsAtLeast(uint64_t ram_in_mb) { 71 uint64_t memory_size = 0; 72 size_t size = sizeof(memory_size); 73 if (sysctlbyname("hw.memsize", &memory_size, &size, NULL, 0) == 0) { 74 // Anything >= 500M, call high ram. 75 return memory_size >= ram_in_mb * 1024 * 1024; 76 } 77 return false; 78} 79 80bool IsSingleCoreDevice() { 81 uint64_t cpu_number = 0; 82 size_t sizes = sizeof(cpu_number); 83 sysctlbyname("hw.physicalcpu", &cpu_number, &sizes, NULL, 0); 84 return cpu_number == 1; 85} 86 87std::string GetMacAddress(const std::string& interface_name) { 88 std::string mac_string; 89 struct ifaddrs* addresses; 90 if (getifaddrs(&addresses) == 0) { 91 for (struct ifaddrs* address = addresses; address; 92 address = address->ifa_next) { 93 if ((address->ifa_addr->sa_family == AF_LINK) && 94 strcmp(interface_name.c_str(), address->ifa_name) == 0) { 95 const struct sockaddr_dl* found_address_struct = 96 reinterpret_cast<const struct sockaddr_dl*>(address->ifa_addr); 97 98 // |found_address_struct->sdl_data| contains the interface name followed 99 // by the interface address. The address part can be accessed based on 100 // the length of the name, that is, |found_address_struct->sdl_nlen|. 101 const unsigned char* found_address = 102 reinterpret_cast<const unsigned char*>( 103 &found_address_struct->sdl_data[ 104 found_address_struct->sdl_nlen]); 105 106 int found_address_length = found_address_struct->sdl_alen; 107 for (int i = 0; i < found_address_length; ++i) { 108 if (i != 0) 109 mac_string.push_back(':'); 110 base::StringAppendF(&mac_string, "%02X", found_address[i]); 111 } 112 break; 113 } 114 } 115 freeifaddrs(addresses); 116 } 117 return mac_string; 118} 119 120std::string GetRandomId() { 121 base::apple::ScopedCFTypeRef<CFUUIDRef> uuid_object( 122 CFUUIDCreate(kCFAllocatorDefault)); 123 base::apple::ScopedCFTypeRef<CFStringRef> uuid_string( 124 CFUUIDCreateString(kCFAllocatorDefault, uuid_object.get())); 125 return base::SysCFStringRefToUTF8(uuid_string.get()); 126} 127 128std::string GetDeviceIdentifier(const char* salt) { 129 NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; 130 131 NSString* last_seen_hardware = 132 [defaults stringForKey:kHardwareTypePreferenceKey]; 133 NSString* current_hardware = 134 base::SysUTF8ToNSString(base::SysInfo::HardwareModelName()); 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 return GetSaltedString(base::SysNSStringToUTF8(client_id), 151 salt ? salt : kDefaultSalt); 152} 153 154std::string GetVendorId() { 155 return base::SysNSStringToUTF8( 156 [[[UIDevice currentDevice] identifierForVendor] UUIDString]); 157} 158 159std::string GetSaltedString(const std::string& in_string, 160 const std::string& salt) { 161 DCHECK(salt.length()); 162 NSData* hash_data = [base::SysUTF8ToNSString(in_string + salt) 163 dataUsingEncoding:NSUTF8StringEncoding]; 164 165 unsigned char hash[CC_SHA256_DIGEST_LENGTH]; 166 CC_SHA256([hash_data bytes], base::checked_cast<CC_LONG>([hash_data length]), 167 hash); 168 CFUUIDBytes* uuid_bytes = reinterpret_cast<CFUUIDBytes*>(hash); 169 170 base::apple::ScopedCFTypeRef<CFUUIDRef> uuid_object( 171 CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault, *uuid_bytes)); 172 base::apple::ScopedCFTypeRef<CFStringRef> device_id( 173 CFUUIDCreateString(kCFAllocatorDefault, uuid_object.get())); 174 return base::SysCFStringRefToUTF8(device_id.get()); 175} 176 177} // namespace ios::device_util 178