| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- #import "CocosIOSPlatformBridge.h"
- #import <Security/Security.h>
- #import <UIKit/UIKit.h>
- #import <AppTrackingTransparency/AppTrackingTransparency.h>
- @implementation CocosIOSPlatformBridge
- + (void)requestTrackingAuthorization {
- dispatch_async(dispatch_get_main_queue(), ^{
- // All accesses are serialized on the main queue.
- static BOOL requestInFlight = NO;
- static id activeObserver = nil;
- if (@available(iOS 14, *)) {
- if (requestInFlight) return;
- NSString *usage = [NSBundle.mainBundle objectForInfoDictionaryKey:@"NSUserTrackingUsageDescription"];
- if (![usage isKindOfClass:NSString.class] || !usage.length) {
- NSLog(@"[ATT] Missing NSUserTrackingUsageDescription; request skipped.");
- return;
- }
- if (UIApplication.sharedApplication.applicationState != UIApplicationStateActive) {
- if (!activeObserver) {
- activeObserver = [NSNotificationCenter.defaultCenter
- addObserverForName:UIApplicationDidBecomeActiveNotification
- object:nil queue:NSOperationQueue.mainQueue
- usingBlock:^(NSNotification *notification) {
- [NSNotificationCenter.defaultCenter removeObserver:activeObserver];
- activeObserver = nil;
- [CocosIOSPlatformBridge requestTrackingAuthorization];
- }];
- }
- return;
- }
- if (activeObserver) {
- [NSNotificationCenter.defaultCenter removeObserver:activeObserver];
- activeObserver = nil;
- }
- ATTrackingManagerAuthorizationStatus status = ATTrackingManager.trackingAuthorizationStatus;
- if (status != ATTrackingManagerAuthorizationStatusNotDetermined) {
- NSLog(@"[ATT] Existing status: %ld", (long)status);
- return;
- }
- requestInFlight = YES;
- [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus result) {
- dispatch_async(dispatch_get_main_queue(), ^{
- requestInFlight = NO;
- // 0=notDetermined, 1=restricted, 2=denied, 3=authorized.
- NSLog(@"[ATT] Authorization status: %ld", (long)result);
- });
- }];
- } else {
- NSLog(@"[ATT] Not available before iOS 14.");
- }
- });
- }
- + (nullable NSString *)getDeviceID {
- @synchronized(self) {
- NSString *bundleID = NSBundle.mainBundle.bundleIdentifier;
- if (!bundleID.length) return nil;
- // Keep these keys stable between releases to preserve the identifier.
- NSDictionary *key = @{
- (__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword,
- (__bridge id)kSecAttrService: [bundleID stringByAppendingString:@".device-identifier"],
- (__bridge id)kSecAttrAccount: @"deviceUUID.v1",
- (__bridge id)kSecAttrSynchronizable: @NO,
- };
- // Retry a read if another process created the item between our read/add.
- for (NSUInteger attempt = 0; attempt < 2; ++attempt) {
- NSMutableDictionary *query = [key mutableCopy];
- query[(__bridge id)kSecReturnData] = @YES;
- query[(__bridge id)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
- CFTypeRef result = NULL;
- OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
- id data = CFBridgingRelease(result);
- if (status == errSecSuccess) {
- NSString *savedUUID = [data isKindOfClass:NSData.class]
- ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : nil;
- if (savedUUID.length && [[NSUUID alloc] initWithUUIDString:savedUUID]) {
- return savedUUID;
- }
- // A damaged value must not silently replace an existing identity.
- NSLog(@"[iOS DeviceID] Invalid Keychain UUID; keeping the existing item.");
- return nil;
- }
- if (status != errSecItemNotFound) {
- // Includes a locked/unavailable Keychain. Do not create a new ID.
- NSLog(@"[iOS DeviceID] Keychain read failed: %d", (int)status);
- return nil;
- }
- NSString *uuid = NSUUID.UUID.UUIDString;
- NSMutableDictionary *item = [key mutableCopy];
- item[(__bridge id)kSecValueData] = [uuid dataUsingEncoding:NSUTF8StringEncoding];
- // No iCloud sync or migration to another device through backups.
- item[(__bridge id)kSecAttrAccessible] = (__bridge id)kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
- status = SecItemAdd((__bridge CFDictionaryRef)item, NULL);
- if (status == errSecSuccess) return uuid;
- if (status != errSecDuplicateItem) {
- NSLog(@"[iOS DeviceID] Keychain write failed: %d", (int)status);
- return nil;
- }
- }
- NSLog(@"[iOS DeviceID] Keychain item changed concurrently; retry on next call.");
- return nil;
- }
- }
- @end
|