caid
parent
e174ec54d4
commit
92758032be
|
|
@ -2,6 +2,7 @@
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.ConstrainedExecution;
|
using System.Runtime.ConstrainedExecution;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
public class DeviceIdHandle
|
public class DeviceIdHandle
|
||||||
|
|
@ -18,6 +19,8 @@ public class DeviceIdHandle
|
||||||
public string channel;
|
public string channel;
|
||||||
public string package_name;
|
public string package_name;
|
||||||
public int platform;
|
public int platform;
|
||||||
|
public string identity_key;
|
||||||
|
public string caid_device_info_str;
|
||||||
|
|
||||||
public DeviceIdInfo()
|
public DeviceIdInfo()
|
||||||
{
|
{
|
||||||
|
|
@ -35,6 +38,7 @@ public class DeviceIdHandle
|
||||||
#else
|
#else
|
||||||
platform = 0;
|
platform = 0;
|
||||||
#endif
|
#endif
|
||||||
|
identity_key = SystemInfo.deviceUniqueIdentifier;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,4 +200,27 @@ public class DeviceIdHandle
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取 CAID 设备信息 JSON(iOS 侧采集,返回所有参数的 JSON 字符串)
|
||||||
|
/// </summary>
|
||||||
|
public string GetCAIDDeviceInfoJSON()
|
||||||
|
{
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
IntPtr ptr = GetCAIDDeviceInfoJSONNative();
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
return string.Empty;
|
||||||
|
string result = Marshal.PtrToStringAuto(ptr);
|
||||||
|
Marshal.FreeCoTaskMem(ptr);
|
||||||
|
return result ?? string.Empty;
|
||||||
|
#else
|
||||||
|
// 非 iOS 平台返回空 JSON(可用于编辑器调试)
|
||||||
|
return string.Empty;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
[DllImport("__Internal", EntryPoint = "GetCAIDDeviceInfoJSON")]
|
||||||
|
private static extern IntPtr GetCAIDDeviceInfoJSONNative();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@
|
||||||
#import <AdSupport/AdSupport.h>
|
#import <AdSupport/AdSupport.h>
|
||||||
#import <AppTrackingTransparency/AppTrackingTransparency.h>
|
#import <AppTrackingTransparency/AppTrackingTransparency.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
#import <sys/sysctl.h>
|
||||||
|
#import <sys/stat.h>
|
||||||
|
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
|
||||||
|
#import <CoreTelephony/CTCarrier.h>
|
||||||
|
#import <CommonCrypto/CommonCrypto.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
|
@ -12,15 +17,274 @@ void UnitySendMessage(const char* obj, const char* method, const char* msg);
|
||||||
|
|
||||||
// 辅助方法:将 NSString 转换为 C 语言字符串
|
// 辅助方法:将 NSString 转换为 C 语言字符串
|
||||||
const char* ConvertNSStringToCString(NSString* nsString) {
|
const char* ConvertNSStringToCString(NSString* nsString) {
|
||||||
if (nsString == NULL) {
|
if (nsString == NULL || nsString == nil) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
const char* nsStringUtf8 = [nsString UTF8String];
|
const char* nsStringUtf8 = [nsString UTF8String];
|
||||||
|
if (nsStringUtf8 == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
char* cString = (char*)malloc(strlen(nsStringUtf8) + 1);
|
char* cString = (char*)malloc(strlen(nsStringUtf8) + 1);
|
||||||
strcpy(cString, nsStringUtf8);
|
strcpy(cString, nsStringUtf8);
|
||||||
return cString;
|
return cString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// CAID 设备信息采集(参见 CAID API 接入文档 v1.17)
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
|
#pragma mark - 6.1.1 设备启动时间
|
||||||
|
static time_t bootSecTime() {
|
||||||
|
struct timeval boottime;
|
||||||
|
size_t len = sizeof(boottime);
|
||||||
|
int mib[2] = { CTL_KERN, KERN_BOOTTIME };
|
||||||
|
if (sysctl(mib, 2, &boottime, &len, NULL, 0) < 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return boottime.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* GetBootTimeInSec() {
|
||||||
|
return [NSString stringWithFormat:@"%ld", bootSecTime()];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.2 国家
|
||||||
|
NSString* GetCountryCode() {
|
||||||
|
NSLocale *locale = [NSLocale currentLocale];
|
||||||
|
NSString *countryCode = [locale objectForKey:NSLocaleCountryCode];
|
||||||
|
return countryCode ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.3 语言
|
||||||
|
NSString* GetLanguage() {
|
||||||
|
NSString *language;
|
||||||
|
NSLocale *locale = [NSLocale currentLocale];
|
||||||
|
if ([[NSLocale preferredLanguages] count] > 0) {
|
||||||
|
language = [[NSLocale preferredLanguages] objectAtIndex:0];
|
||||||
|
} else {
|
||||||
|
language = [locale objectForKey:NSLocaleLanguageCode];
|
||||||
|
}
|
||||||
|
return language ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - MD5 辅助函数(32位小写)
|
||||||
|
NSString* MD5HexDigest(NSString *input) {
|
||||||
|
if (!input || [input length] == 0) return @"";
|
||||||
|
const char *cStr = [input UTF8String];
|
||||||
|
unsigned char digest[CC_MD5_DIGEST_LENGTH];
|
||||||
|
CC_MD5(cStr, (CC_LONG)strlen(cStr), digest);
|
||||||
|
NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
|
||||||
|
for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
|
||||||
|
[output appendFormat:@"%02x", digest[i]];
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.4 设备名称(小写 MD5)
|
||||||
|
NSString* GetCAIDDeviceName() {
|
||||||
|
NSString *deviceName = [[UIDevice currentDevice] name];
|
||||||
|
if ([deviceName length] == 0) {
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
return MD5HexDigest(deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.5 系统版本
|
||||||
|
NSString* GetSystemVersion() {
|
||||||
|
return [[UIDevice currentDevice] systemVersion];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.6 设备 Machine
|
||||||
|
static NSString* getSystemHardwareByName(const char *typeSpecifier) {
|
||||||
|
size_t size;
|
||||||
|
sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
|
||||||
|
char *answer = malloc(size);
|
||||||
|
if (!answer) return @"";
|
||||||
|
sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
|
||||||
|
NSString *results = [NSString stringWithUTF8String:answer];
|
||||||
|
free(answer);
|
||||||
|
return results ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString* GetMachine() {
|
||||||
|
NSString *machine = getSystemHardwareByName("hw.machine");
|
||||||
|
return machine ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.7 运营商
|
||||||
|
NSString* GetCarrierInfo() {
|
||||||
|
#if TARGET_IPHONE_SIMULATOR
|
||||||
|
return @"SIMULATOR";
|
||||||
|
#else
|
||||||
|
static dispatch_queue_t _queue;
|
||||||
|
static dispatch_once_t once;
|
||||||
|
dispatch_once(&once, ^{
|
||||||
|
_queue = dispatch_queue_create("com.carr.devicehelper", NULL);
|
||||||
|
});
|
||||||
|
|
||||||
|
__block NSString *carr = nil;
|
||||||
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
||||||
|
|
||||||
|
dispatch_async(_queue, ^{
|
||||||
|
CTTelephonyNetworkInfo *info = [[CTTelephonyNetworkInfo alloc] init];
|
||||||
|
CTCarrier *carrier = nil;
|
||||||
|
|
||||||
|
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 12.1) {
|
||||||
|
if ([info respondsToSelector:@selector(serviceSubscriberCellularProviders)]) {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
|
||||||
|
NSArray *carrierKeysArray = [info.serviceSubscriberCellularProviders.allKeys
|
||||||
|
sortedArrayUsingSelector:@selector(compare:)];
|
||||||
|
carrier = info.serviceSubscriberCellularProviders[carrierKeysArray.firstObject];
|
||||||
|
if (!carrier.mobileNetworkCode) {
|
||||||
|
carrier = info.serviceSubscriberCellularProviders[carrierKeysArray.lastObject];
|
||||||
|
}
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!carrier) {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
|
carrier = info.subscriberCellularProvider;
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carrier != nil) {
|
||||||
|
NSString *networkCode = [carrier mobileNetworkCode];
|
||||||
|
NSString *countryCode = [carrier mobileCountryCode];
|
||||||
|
|
||||||
|
if (countryCode && [countryCode isEqualToString:@"460"] && networkCode) {
|
||||||
|
if ([networkCode isEqualToString:@"00"] ||
|
||||||
|
[networkCode isEqualToString:@"02"] ||
|
||||||
|
[networkCode isEqualToString:@"07"] ||
|
||||||
|
[networkCode isEqualToString:@"08"]) {
|
||||||
|
carr = @"中国移动";
|
||||||
|
} else if ([networkCode isEqualToString:@"01"] ||
|
||||||
|
[networkCode isEqualToString:@"06"] ||
|
||||||
|
[networkCode isEqualToString:@"09"]) {
|
||||||
|
carr = @"中国联通";
|
||||||
|
} else if ([networkCode isEqualToString:@"03"] ||
|
||||||
|
[networkCode isEqualToString:@"05"] ||
|
||||||
|
[networkCode isEqualToString:@"11"]) {
|
||||||
|
carr = @"中国电信";
|
||||||
|
} else if ([networkCode isEqualToString:@"04"]) {
|
||||||
|
carr = @"中国卫通";
|
||||||
|
} else if ([networkCode isEqualToString:@"20"]) {
|
||||||
|
carr = @"中国铁通";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
carr = [carrier.carrierName copy];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carr.length <= 0) {
|
||||||
|
carr = @"unknown";
|
||||||
|
}
|
||||||
|
dispatch_semaphore_signal(semaphore);
|
||||||
|
});
|
||||||
|
|
||||||
|
dispatch_time_t t = dispatch_time(DISPATCH_TIME_NOW, 0.5 * NSEC_PER_SEC);
|
||||||
|
dispatch_semaphore_wait(semaphore, t);
|
||||||
|
return [carr copy] ?: @"unknown";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.8 物理内存容量
|
||||||
|
NSString* GetMemory() {
|
||||||
|
return [NSString stringWithFormat:@"%lld", [NSProcessInfo processInfo].physicalMemory];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.9 硬盘容量
|
||||||
|
NSString* GetDisk() {
|
||||||
|
int64_t space = -1;
|
||||||
|
NSError *error = nil;
|
||||||
|
NSDictionary *attrs = [[NSFileManager defaultManager]
|
||||||
|
attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
|
||||||
|
if (!error) {
|
||||||
|
space = [[attrs objectForKey:NSFileSystemSize] longLongValue];
|
||||||
|
}
|
||||||
|
return [NSString stringWithFormat:@"%lld", space];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.10 系统更新时间
|
||||||
|
NSString* GetSysFileTime() {
|
||||||
|
NSString *result = nil;
|
||||||
|
// Base64 解码: /var/mobile/Library/UserConfigurationProfiles/PublicInfo/MCMeta.plist
|
||||||
|
NSString *information = @"L3Zhci9tb2JpbGUvTGlicmFyeS9Vc2VyQ29uZmlndXJhdGlvblByb2ZpbGVzL1B1YmxpY0luZm8vTUNNZXRhLnBsaXN0";
|
||||||
|
NSData *data = [[NSData alloc] initWithBase64EncodedString:information options:0];
|
||||||
|
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
NSDictionary *fileAttributes = [[NSFileManager defaultManager]
|
||||||
|
attributesOfItemAtPath:dataString error:&error];
|
||||||
|
if (fileAttributes) {
|
||||||
|
id singleAttribute = [fileAttributes objectForKey:NSFileCreationDate];
|
||||||
|
if ([singleAttribute isKindOfClass:[NSDate class]]) {
|
||||||
|
NSDate *dataDate = (NSDate *)singleAttribute;
|
||||||
|
result = [NSString stringWithFormat:@"%f", [dataDate timeIntervalSince1970]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.11 设备 Model
|
||||||
|
NSString* GetModel() {
|
||||||
|
NSString *model = getSystemHardwareByName("hw.model");
|
||||||
|
return model ?: @"";
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.12 时区
|
||||||
|
NSString* GetTimeZone() {
|
||||||
|
NSInteger offset = [NSTimeZone systemTimeZone].secondsFromGMT;
|
||||||
|
return [NSString stringWithFormat:@"%ld", (long)offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - 6.1.13 设备初始化时间
|
||||||
|
NSString* GetDeviceInitTime() {
|
||||||
|
struct stat info;
|
||||||
|
int result = stat("/var/mobile", &info);
|
||||||
|
if (result != 0) {
|
||||||
|
return @"";
|
||||||
|
}
|
||||||
|
struct timespec time = info.st_birthtimespec;
|
||||||
|
return [NSString stringWithFormat:@"%ld.%09ld", time.tv_sec, time.tv_nsec];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// Unity C# 统一调用接口:返回 CAID 设备信息的 JSON 字符串
|
||||||
|
// ==========================================
|
||||||
|
const char* GetCAIDDeviceInfoJSON() {
|
||||||
|
NSMutableDictionary *deviceInfo = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
deviceInfo[@"bootTimeInSec"] = GetBootTimeInSec() ?: @"";
|
||||||
|
deviceInfo[@"countryCode"] = GetCountryCode() ?: @"";
|
||||||
|
deviceInfo[@"language"] = GetLanguage() ?: @"";
|
||||||
|
deviceInfo[@"deviceName"] = GetCAIDDeviceName() ?: @"";
|
||||||
|
deviceInfo[@"systemVersion"] = GetSystemVersion() ?: @"";
|
||||||
|
deviceInfo[@"machine"] = GetMachine() ?: @"";
|
||||||
|
deviceInfo[@"carrierInfo"] = GetCarrierInfo() ?: @"";
|
||||||
|
deviceInfo[@"memory"] = GetMemory() ?: @"";
|
||||||
|
deviceInfo[@"disk"] = GetDisk() ?: @"";
|
||||||
|
deviceInfo[@"sysFileTime"] = GetSysFileTime() ?: @"";
|
||||||
|
deviceInfo[@"model"] = GetModel() ?: @"";
|
||||||
|
deviceInfo[@"timeZone"] = GetTimeZone() ?: @"";
|
||||||
|
deviceInfo[@"deviceInitTime"] = GetDeviceInitTime() ?: @"";
|
||||||
|
|
||||||
|
NSError *error = nil;
|
||||||
|
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:deviceInfo options:0 error:&error];
|
||||||
|
if (error || !jsonData) {
|
||||||
|
return ConvertNSStringToCString(@"");
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
||||||
|
return ConvertNSStringToCString(jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==========================================
|
||||||
|
// IDFA / IDFV (原有方法保留)
|
||||||
|
// ==========================================
|
||||||
|
|
||||||
// Unity 调用:请求/拉取 IDFA (保留此方法供 Unity 安全拉取数据)
|
// Unity 调用:请求/拉取 IDFA (保留此方法供 Unity 安全拉取数据)
|
||||||
void RequestIDFA() {
|
void RequestIDFA() {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,170 @@
|
||||||
# DeviceIdentifiers.m
|
# DeviceHelper_iOS
|
||||||
|
|
||||||
用于向 iOS (Xcode) 原生层请求获取设备广告标识符 (IDFA) 与供应商标识符 (IDFV) 的 Unity 桥接脚本。
|
Unity iOS 原生桥接插件,提供以下功能:
|
||||||
|
|
||||||
|
1. **IDFA** — 广告标识符(异步获取,含自动弹窗机制)
|
||||||
|
2. **IDFV** — 供应商标识符(同步获取)
|
||||||
|
3. **CAID 设备信息采集** — 采集 iOS 设备信息,以 JSON 字符串形式返回给 Unity C#
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
| 文件 | 路径 |
|
||||||
|
|------|------|
|
||||||
|
| 源文件 | `Assets/Plugins/iOS/DeviceIdentifiers.m` |
|
||||||
|
| C# 桥接 | 项目 `Assets/PhxhSDK/Phxh/Others/DeviceHelper.cs` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Unity C# 调用方式
|
||||||
|
|
||||||
|
### 1. 获取 IDFA(异步)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// IDFA 为异步获取,结果会通过 UnitySendMessage 回调到 GameLauncher.OnIDFAResult
|
||||||
|
// 主动触发请求:
|
||||||
|
// (代码中已实现 App 启动时自动弹窗,无需额外调用)
|
||||||
|
// 若需手动调用:
|
||||||
|
// (在 iOS 原生侧已暴露 RequestIDFA() 函数,可通过 DllImport 调用)
|
||||||
|
```
|
||||||
|
|
||||||
|
> **回调方式**:结果通过 `UnitySendMessage("GameLauncher", "OnIDFAResult", idfaString)` 发送。
|
||||||
|
> 确保 Unity 场景中存在名为 **GameLauncher** 的 GameObject,并挂载了 `OnIDFAResult(string idfa)` 方法。
|
||||||
|
|
||||||
|
### 2. 获取 IDFV(同步)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
public static string GetIDFV()
|
||||||
|
{
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
IntPtr ptr = GetIDFVNative();
|
||||||
|
string result = Marshal.PtrToStringAuto(ptr);
|
||||||
|
Marshal.FreeCoTaskMem(ptr);
|
||||||
|
return result;
|
||||||
|
#else
|
||||||
|
return "";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
[DllImport("__Internal", EntryPoint = "GetIDFV")]
|
||||||
|
private static extern IntPtr GetIDFVNative();
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 获取 CAID 设备信息 JSON(同步)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 直接调用 DeviceHelper.GetCAIDDeviceInfoJSON()
|
||||||
|
// 返回 JSON 字符串,包含 CAID API 所需的全部设备参数
|
||||||
|
string caidJson = DeviceHelper.GetCAIDDeviceInfoJSON();
|
||||||
|
```
|
||||||
|
|
||||||
|
返回的 JSON 示例:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"bootTimeInSec": "1595643553",
|
||||||
|
"countryCode": "CN",
|
||||||
|
"language": "zh-Hans-CN",
|
||||||
|
"deviceName": "e910dddb2748c36b47fcde5dd720eec1",
|
||||||
|
"systemVersion": "14.0",
|
||||||
|
"machine": "iPhone10,3",
|
||||||
|
"carrierInfo": "中国移动",
|
||||||
|
"memory": "3955589120",
|
||||||
|
"disk": "63900340224",
|
||||||
|
"sysFileTime": "1595214620.383940",
|
||||||
|
"model": "D22AP",
|
||||||
|
"timeZone": "28800",
|
||||||
|
"deviceInitTime": "1632467920.301150749"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
各字段说明参见 [CAID API 接入文档](https://caid.china-caa.org) 附录 6.1 章节。
|
||||||
|
|
||||||
|
#### C# 侧方法定义(已内置在 `DeviceHelper.cs` 中)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public static string GetCAIDDeviceInfoJSON()
|
||||||
|
{
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
IntPtr ptr = GetCAIDDeviceInfoJSONNative();
|
||||||
|
if (ptr == IntPtr.Zero)
|
||||||
|
return "{}";
|
||||||
|
string result = Marshal.PtrToStringAuto(ptr);
|
||||||
|
Marshal.FreeCoTaskMem(ptr);
|
||||||
|
return result ?? "{}";
|
||||||
|
#else
|
||||||
|
return "{}";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_IOS && !UNITY_EDITOR
|
||||||
|
[DllImport("__Internal", EntryPoint = "GetCAIDDeviceInfoJSON")]
|
||||||
|
private static extern IntPtr GetCAIDDeviceInfoJSONNative();
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 注意点
|
## 注意点
|
||||||
|
|
||||||
- IDFA (广告标识符) —— 异步获取
|
### IDFA
|
||||||
*授权弹窗:获取 IDFA 必须调起 iOS 系统原生的 AppTrackingTransparency 弹窗,只有在用户主动允许追踪后才可获取真实 IDFA。
|
|
||||||
* 异步回调:由于需要等待用户交互,此过程为异步操作。
|
|
||||||
* 回调目标:获取结果会通过 UnitySendMessage 传递给 Unity。请务必确保 Unity 场景中存在名为 GameLauncher 的 GameObject,并已挂载接收回调的方法。
|
|
||||||
|
|
||||||
- IDFV (供应商标识符) —— 同步获取
|
- **授权弹窗**:获取 IDFA 必须调起 iOS 系统原生的 AppTrackingTransparency 弹窗,只有在用户主动允许追踪后才可获取真实 IDFA。
|
||||||
* 直接获取:IDFV 为设备厂商 ID,无需经过用户授权弹窗,可直接在调用时同步获取并返回结果。
|
- **异步回调**:由于需要等待用户交互,此过程为异步操作。
|
||||||
|
- **回调目标**:获取结果会通过 `UnitySendMessage` 传递给 Unity。请务必确保 Unity 场景中存在名为 `GameLauncher` 的 GameObject,并已挂载接收回调的方法。
|
||||||
|
|
||||||
- 自动触发机制 (App 启动即弹窗)
|
### IDFV
|
||||||
* 默认行为:代码内实现了 onAppDidBecomeActive 监听。App 每次启动并进入活跃状态时,将自动调起 IDFA 请求弹窗。
|
|
||||||
* 修改建议:若项目业务逻辑不需要在 App 刚启动时就弹出授权框(例如需要延迟到特定关卡或场景再弹),请在源码中手动删除或注释掉 onAppDidBecomeActive 相关的监听与函数体。
|
|
||||||
|
|
||||||
## Xcode 工程配置须知 (重要)
|
- **直接获取**:IDFV 为设备厂商 ID,无需经过用户授权弹窗,可直接在调用时同步获取并返回结果。
|
||||||
- 添加系统库:
|
|
||||||
在 Xcode -> Target -> Build Phases -> Link Binary With Libraries 中,添加以下框架:
|
|
||||||
* AdSupport.framework
|
|
||||||
* AppTrackingTransparency.framework
|
|
||||||
|
|
||||||
- 配置隐私描述:
|
### CAID 设备信息
|
||||||
在 Info.plist 中必须添加跟踪权限的用途描述,否则弹窗无法显示且会导致应用崩溃或拒审:
|
|
||||||
* Key: Privacy - Tracking Usage Description (NSUserTrackingUsageDescription)
|
- **同步接口**:所有设备参数的采集均在当前线程同步完成,返回 JSON 字符串。
|
||||||
* Value: (请填写向用户解释为何需要追踪的文案)
|
- **注意内存释放**:C# 侧在 `Marshal.PtrToStringAuto` 后调用 `Marshal.FreeCoTaskMem` 释放原生侧 `malloc` 分配的内存。
|
||||||
|
|
||||||
|
### 自动触发机制(IDFA 弹窗)
|
||||||
|
|
||||||
|
- **默认行为**:代码内实现了 `onAppDidBecomeActive` 监听。App 每次启动并进入活跃状态时,将自动调起 IDFA 请求弹窗。
|
||||||
|
- **修改建议**:若项目业务逻辑不需要在 App 刚启动时就弹出授权框(例如需要延迟到特定关卡或场景再弹),请在源码中手动删除或注释掉 `AutoIDFATrigger` 相关的 `+load` 与 `onAppDidBecomeActive` 实现。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Xcode 工程配置须知(重要)
|
||||||
|
|
||||||
|
- **添加系统库**:
|
||||||
|
在 Xcode -> Target -> Build Phases -> Link Binary With Libraries 中,添加以下框架:
|
||||||
|
|
||||||
|
| 框架 | 用途 |
|
||||||
|
|------|------|
|
||||||
|
| `AdSupport.framework` | IDFA 获取 |
|
||||||
|
| `AppTrackingTransparency.framework` | 跟踪授权弹窗 |
|
||||||
|
| `CoreTelephony.framework` | 运营商信息采集(CAID) |
|
||||||
|
| `libz.tbd` | MD5 计算(CAID) |
|
||||||
|
|
||||||
|
- **配置隐私描述**:
|
||||||
|
在 `Info.plist` 中必须添加跟踪权限的用途描述,否则弹窗无法显示且会导致应用崩溃或拒审:
|
||||||
|
|
||||||
|
- **Key**: `Privacy - Tracking Usage Description` (`NSUserTrackingUsageDescription`)
|
||||||
|
- **Value**: 请填写向用户解释为何需要追踪的文案(如"用于提供个性化广告服务")
|
||||||
|
|
||||||
|
- **Unity 构建时自动添加框架**:
|
||||||
|
若使用 Unity 2020.3+,可在 `Player Settings -> iOS -> Other Settings` 中勾选:
|
||||||
|
|
||||||
|
- `AdSupport.framework` → **Required**
|
||||||
|
- `AppTrackingTransparency.framework` → **Required**
|
||||||
|
- `CoreTelephony.framework` → **Required**
|
||||||
|
|
||||||
|
或在项目中添加 `TDS-Info.plist` 等相关配置(`Assets/Plugins/iOS/TDS-Info.plist`)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本历史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 1.0.0 | - | 初始版本:IDFA / IDFV 获取 + 自动弹窗 + CAID 设备信息采集 |
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue