diff --git a/SensorsAnalyticsSDK.podspec b/SensorsAnalyticsSDK.podspec index de9dcd31..2ebfc908 100644 --- a/SensorsAnalyticsSDK.podspec +++ b/SensorsAnalyticsSDK.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SensorsAnalyticsSDK" - s.version = "3.0.3" + s.version = "3.1.0" s.summary = "The official iOS SDK of Sensors Analytics." s.homepage = "http://www.sensorsdata.cn" s.source = { :git => 'https://github.com/sensorsdata/sa-sdk-ios.git', :tag => "v#{s.version}" } @@ -18,7 +18,7 @@ Pod::Spec.new do |s| c.source_files = core_dir + "**/*.{h,m}" c.public_header_files = core_dir + "SensorsAnalyticsSDK.h", core_dir + "SensorsAnalyticsSDK+Public.h", core_dir + "SAAppExtensionDataManager.h", core_dir + "SASecurityPolicy.h", core_dir + "SAConfigOptions.h", core_dir + "SAConstants.h" c.ios.source_files = "SensorsAnalyticsSDK/RemoteConfig/**/*.{h,m}", "SensorsAnalyticsSDK/ChannelMatch/**/*.{h,m}", "SensorsAnalyticsSDK/Encrypt/**/*.{h,m}", "SensorsAnalyticsSDK/Deeplink/**/*.{h,m}", "SensorsAnalyticsSDK/DebugMode/**/*.{h,m}" - c.ios.public_header_files = "SensorsAnalyticsSDK/Encrypt/SASecretKey.h", "SensorsAnalyticsSDK/ChannelMatch/SensorsAnalyticsSDK+SAChannelMatch.h" + c.ios.public_header_files = "SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.h", "SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h", "SensorsAnalyticsSDK/Encrypt/SASecretKey.h", "SensorsAnalyticsSDK/ChannelMatch/SensorsAnalyticsSDK+SAChannelMatch.h" c.ios.resource = 'SensorsAnalyticsSDK/SensorsAnalyticsSDK.bundle' c.ios.frameworks = 'CoreTelephony' end diff --git a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj index 4abf6094..b76ba22e 100644 --- a/SensorsAnalyticsSDK.xcodeproj/project.pbxproj +++ b/SensorsAnalyticsSDK.xcodeproj/project.pbxproj @@ -184,7 +184,7 @@ A82E894E267D918100475757 /* SASecretKeyFactory.m in Sources */ = {isa = PBXBuildFile; fileRef = A82E893D267D918100475757 /* SASecretKeyFactory.m */; }; A82E894F267D918100475757 /* SAEncryptManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A82E893E267D918100475757 /* SAEncryptManager.m */; }; A82E8950267D918100475757 /* SAAESEncryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A82E893F267D918100475757 /* SAAESEncryptor.h */; }; - A82E8951267D918100475757 /* SAEncryptProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = A82E8940267D918100475757 /* SAEncryptProtocol.h */; }; + A82E8951267D918100475757 /* SAEncryptProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = A82E8940267D918100475757 /* SAEncryptProtocol.h */; settings = {ATTRIBUTES = (Public, ); }; }; A82E8952267D918100475757 /* SAECCPluginEncryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A82E8941267D918100475757 /* SAECCPluginEncryptor.h */; }; A82E8953267D918100475757 /* SAECCEncryptor.h in Headers */ = {isa = PBXBuildFile; fileRef = A82E8942267D918100475757 /* SAECCEncryptor.h */; }; A82E8954267D918100475757 /* SARSAEncryptor.m in Sources */ = {isa = PBXBuildFile; fileRef = A82E8943267D918100475757 /* SARSAEncryptor.m */; }; @@ -284,7 +284,7 @@ F277F5E425CFCE56009B5CE6 /* NSObject+DelegateProxy.h in Headers */ = {isa = PBXBuildFile; fileRef = F277F5E225CFCE56009B5CE6 /* NSObject+DelegateProxy.h */; }; F2E9723125E637820009A2B9 /* SAAppPushManager.h in Headers */ = {isa = PBXBuildFile; fileRef = F2E9722F25E637820009A2B9 /* SAAppPushManager.h */; }; F2E9723225E637820009A2B9 /* SAAppPushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = F2E9723025E637820009A2B9 /* SAAppPushManager.m */; }; - FC002920262C189E00A18FE3 /* SAConfigOptions+Encrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0028EF2629756400A18FE3 /* SAConfigOptions+Encrypt.h */; }; + FC002920262C189E00A18FE3 /* SAConfigOptions+Encrypt.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0028EF2629756400A18FE3 /* SAConfigOptions+Encrypt.h */; settings = {ATTRIBUTES = (Public, ); }; }; FC23719523D0519A00DDD708 /* SALinkHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC23719423D0519A00DDD708 /* SALinkHandlerTests.m */; }; FC3C58DA23BEEFB000D83BF4 /* SATrackTimerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC3C58D923BEEFB000D83BF4 /* SATrackTimerTests.m */; }; FC80AB7C242CB17A006D1D25 /* SAIdentifierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FC80AB7B242CB17A006D1D25 /* SAIdentifierTests.m */; }; diff --git a/SensorsAnalyticsSDK/AutoTrack/SAAutoTrackManager.m b/SensorsAnalyticsSDK/AutoTrack/SAAutoTrackManager.m index 3c58fcfc..7508431f 100644 --- a/SensorsAnalyticsSDK/AutoTrack/SAAutoTrackManager.m +++ b/SensorsAnalyticsSDK/AutoTrack/SAAutoTrackManager.m @@ -156,7 +156,7 @@ - (void)remoteConfigModelChanged:(NSNotification *)sender { - (BOOL)isAutoTrackEnabled { if (self.isDisableSDK) { - SALogDebug(@"【remote config】SDK is disabled"); + SALogDebug(@"SDK is disabled"); return NO; } @@ -176,7 +176,7 @@ - (BOOL)isAutoTrackEnabled { - (BOOL)isAutoTrackEventTypeIgnored:(SensorsAnalyticsAutoTrackEventType)eventType { if (self.isDisableSDK) { - SALogDebug(@"【remote config】SDK is disabled"); + SALogDebug(@"SDK is disabled"); return YES; } diff --git a/SensorsAnalyticsSDK/Core/SAConfigOptions.h b/SensorsAnalyticsSDK/Core/SAConfigOptions.h index 591634e2..2c07c017 100644 --- a/SensorsAnalyticsSDK/Core/SAConfigOptions.h +++ b/SensorsAnalyticsSDK/Core/SAConfigOptions.h @@ -78,6 +78,15 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, strong) SASecurityPolicy *securityPolicy API_UNAVAILABLE(macos); +/** + * @abstract + * 设置 flush 时网络发送策略 + * + * @discussion + * 默认 3G、4G、WI-FI 环境下都会尝试 flush + */ +@property (nonatomic) SensorsAnalyticsNetworkType flushNetworkPolicy; + /** * @property * @@ -119,6 +128,11 @@ NS_ASSUME_NONNULL_BEGIN /// 开启 log 打印 @property (nonatomic, assign) BOOL enableLog; +/// 禁用 SDK,默认为 NO +/// +/// 禁用后,SDK 将不会触发事件,也不会发送网络请求 +@property (nonatomic, assign) BOOL disableSDK; + /// 开启点击图 @property (nonatomic, assign) BOOL enableHeatMap API_UNAVAILABLE(macos); diff --git a/SensorsAnalyticsSDK/Core/SAConfigOptions.m b/SensorsAnalyticsSDK/Core/SAConfigOptions.m index 40547bac..0123966f 100644 --- a/SensorsAnalyticsSDK/Core/SAConfigOptions.m +++ b/SensorsAnalyticsSDK/Core/SAConfigOptions.m @@ -27,7 +27,7 @@ @interface SAConfigOptions () -@property (nonatomic, strong, readwrite) NSMutableArray *encryptors; +@property (atomic, strong, readwrite) NSMutableArray *encryptors; @end @@ -53,6 +53,16 @@ - (instancetype)initWithServerURL:(NSString *)serverURL launchOptions:(id)launch #ifdef SENSORS_ANALYTICS_ENABLE_AUTOTRACK_CHILD_VIEWSCREEN _enableAutoTrackChildViewScreen = YES; #endif + + _flushNetworkPolicy = +#if TARGET_OS_IOS + SensorsAnalyticsNetworkType3G | + SensorsAnalyticsNetworkType4G | +#ifdef __IPHONE_14_1 + SensorsAnalyticsNetworkType5G | +#endif +#endif + SensorsAnalyticsNetworkTypeWIFI; } return self; } @@ -68,6 +78,8 @@ - (id)copyWithZone:(nullable NSZone *)zone { options.maxCacheSize = self.maxCacheSize; options.enableLog = self.enableLog; options.flushBeforeEnterBackground = self.flushBeforeEnterBackground; + options.flushNetworkPolicy = self.flushNetworkPolicy; + options.disableSDK = self.disableSDK; #if TARGET_OS_IOS // 支持 https 自签证书 diff --git a/SensorsAnalyticsSDK/Core/SAModuleManager.h b/SensorsAnalyticsSDK/Core/SAModuleManager.h index 98101d24..b35e3332 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleManager.h +++ b/SensorsAnalyticsSDK/Core/SAModuleManager.h @@ -41,6 +41,11 @@ typedef NS_ENUM(NSUInteger, SAModuleType) { + (instancetype)sharedInstance; +- (BOOL)isDisableSDK; + +/// 关闭所有的模块功能 +- (void)disableAllModules; + /// 当前 SDK 中是否包含特定类型的模块 /// @param type 需要判断的模块类型 /// @return 是否包含 @@ -56,6 +61,9 @@ typedef NS_ENUM(NSUInteger, SAModuleType) { /// @param type 模块类型 - (void)setEnable:(BOOL)enable forModuleType:(SAModuleType)type; +/// 更新数据接收地址 +/// @param serverURL 新的数据接收地址 +- (void)updateServerURL:(NSString *)serverURL; @end #pragma mark - diff --git a/SensorsAnalyticsSDK/Core/SAModuleManager.m b/SensorsAnalyticsSDK/Core/SAModuleManager.m index 62a85c26..1bb28bf9 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleManager.m +++ b/SensorsAnalyticsSDK/Core/SAModuleManager.m @@ -58,6 +58,10 @@ @implementation SAModuleManager + (void)startWithConfigOptions:(SAConfigOptions *)configOptions debugMode:(SensorsAnalyticsDebugMode)debugMode { SAModuleManager.sharedInstance.configOptions = configOptions; + // 禁止 SDK 时,不开启其他模块 + if (configOptions.disableSDK) { + return; + } // H5 打通模块 if (configOptions.enableJavaScriptBridge) { @@ -172,6 +176,27 @@ - (void)setEnable:(BOOL)enable forModule:(NSString *)moduleName { #pragma mark - Public +- (BOOL)isDisableSDK { + if (self.configOptions.disableSDK) { + return YES; + } + id manager = (id)self.modules[kSARemoteConfigModuleName]; + return manager.isEnable ? manager.isDisableSDK : NO; +} + +- (void)disableAllModules { + NSArray *allKeys = self.modules.allKeys; + for (NSString *key in allKeys) { + // 这两个模块是使用接口开启,所以在 SAConfigOptions 中不存在标记,无法重新开启 + // 当定位弹窗出现时,如果关闭了定位模块,会导致弹窗消失 + if (![key isEqualToString:kSALocationModuleName] && + ![key isEqualToString:kSADeviceOrientationModuleName] && + ![key isEqualToString:kSADebugModeModuleName]) { + [self.modules removeObjectForKey:key]; + } + } +} + - (BOOL)contains:(SAModuleType)type { NSString *moduleName = [self moduleNameForType:type]; NSString *className = [self classNameForModule:moduleName]; @@ -188,6 +213,15 @@ - (void)setEnable:(BOOL)enable forModuleType:(SAModuleType)type { [self setEnable:enable forModule:name]; } +- (void)updateServerURL:(NSString *)serverURL { + [self.modules enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { + if (!([obj conformsToProtocol:@protocol(SAModuleProtocol)] && [obj respondsToSelector:@selector(updateServerURL:)]) || !obj.isEnable) { + return; + } + [obj updateServerURL:serverURL]; + }]; +} + #pragma mark - Open URL - (BOOL)canHandleURL:(NSURL *)url { @@ -409,8 +443,4 @@ - (BOOL)isIgnoreEventObject:(SABaseEventObject *)obj { return [self.remoteConfigManager isIgnoreEventObject:obj]; } -- (BOOL)isDisableSDK { - return [self.remoteConfigManager isDisableSDK]; -} - @end diff --git a/SensorsAnalyticsSDK/Core/SAModuleProtocol.h b/SensorsAnalyticsSDK/Core/SAModuleProtocol.h index 3c762afb..bcb23574 100644 --- a/SensorsAnalyticsSDK/Core/SAModuleProtocol.h +++ b/SensorsAnalyticsSDK/Core/SAModuleProtocol.h @@ -37,6 +37,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, strong) SAConfigOptions *configOptions; +- (void)updateServerURL:(NSString *)serverURL; + @end #pragma mark - diff --git a/SensorsAnalyticsSDK/Core/SAObject+SAConfigOptions.m b/SensorsAnalyticsSDK/Core/SAObject+SAConfigOptions.m index 877ffbb8..cca1b5aa 100644 --- a/SensorsAnalyticsSDK/Core/SAObject+SAConfigOptions.m +++ b/SensorsAnalyticsSDK/Core/SAObject+SAConfigOptions.m @@ -81,7 +81,7 @@ - (BOOL)isDebugMode { } - (SensorsAnalyticsNetworkType)networkTypePolicy { - return [[SensorsAnalyticsSDK.sdkInstance valueForKey:@"networkTypePolicy"] integerValue]; + return SensorsAnalyticsSDK.sdkInstance.configOptions.flushNetworkPolicy; } - (NSInteger)flushBulkSize { diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h index b073999f..599751f7 100644 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK+Public.h @@ -88,6 +88,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (SensorsAnalyticsSDK * _Nullable)sharedInstance; +/// 禁用 SDK。调用后,SDK 将不采集事件,不发送网络请求 ++ (void)disableSDK; + +/// 开启 SDK。如果之前 SDK 是禁止状态,调用后将恢复数据采集功能 ++ (void)enableSDK; + /** * @abstract * 返回预置的属性 @@ -125,28 +131,6 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark--cache and flush -/** - * @abstract - * 设置本地缓存最多事件条数 - * - * @discussion - * 默认为 10000 条事件 - * - */ -@property (nonatomic, getter = getMaxCacheSize) UInt64 maxCacheSize __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 maxCacheSize"))); -- (UInt64)getMaxCacheSize __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 maxCacheSize"))); - -/** - * @abstract - * 设置 flush 时网络发送策略 - * - * @discussion - * 默认 3G、4G、WI-FI 环境下都会尝试 flush - * - * @param networkType SensorsAnalyticsNetworkType - */ -- (void)setFlushNetworkPolicy:(SensorsAnalyticsNetworkType)networkType; - /** * @abstract * 登录,设置当前用户的 loginId @@ -187,12 +171,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (void)resetAnonymousId; -/** - * @abstract - * 自动收集 App Crash 日志,该功能默认是关闭的 - */ -- (void)trackAppCrash __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 enableTrackAppCrash"))) API_UNAVAILABLE(macos); - /** * @abstract * 设置是否显示 debugInfoView,对于 iOS,是 UIAlertView/UIAlertController @@ -868,6 +846,28 @@ DeepLink 回调函数 */ @property (atomic) BOOL flushBeforeEnterBackground __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 flushBeforeEnterBackground"))); +/** + * @abstract + * 设置本地缓存最多事件条数 + * + * @discussion + * 默认为 10000 条事件 + * + */ +@property (nonatomic, getter = getMaxCacheSize) UInt64 maxCacheSize __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 maxCacheSize"))); +- (UInt64)getMaxCacheSize __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 maxCacheSize"))); + +/** + * @abstract + * 设置 flush 时网络发送策略 + * + * @discussion + * 默认 3G、4G、WI-FI 环境下都会尝试 flush + * + * @param networkType SensorsAnalyticsNetworkType + */ +- (void)setFlushNetworkPolicy:(SensorsAnalyticsNetworkType)networkType __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 flushNetworkPolicy"))); + /** * @abstract * 根据传入的配置,初始化并返回一个 SensorsAnalyticsSDK 的单例 @@ -921,6 +921,12 @@ DeepLink 回调函数 */ - (void)setDebugMode:(SensorsAnalyticsDebugMode)debugMode __attribute__((deprecated("已过时,建议动态开启调试模式"))) API_UNAVAILABLE(macos); +/** + * @abstract + * 自动收集 App Crash 日志,该功能默认是关闭的 + */ +- (void)trackAppCrash __attribute__((deprecated("已过时,请参考 SAConfigOptions 类的 enableTrackAppCrash"))) API_UNAVAILABLE(macos); + /** * @abstract * 提供一个接口,用来在用户注册的时候,用注册ID来替换用户以前的匿名ID diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h index 7c2e6386..e3d92da5 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.h @@ -47,3 +47,7 @@ #if __has_include("SASecretKey.h") #import "SASecretKey.h" #endif + +#if __has_include("SAConfigOptions+Encrypt.h") +#import "SAConfigOptions+Encrypt.h" +#endif diff --git a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m index 6e74d3d9..2561cf77 100755 --- a/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m +++ b/SensorsAnalyticsSDK/Core/SensorsAnalyticsSDK.m @@ -41,7 +41,7 @@ #import "SAProfileEventObject.h" #import "SAJSONUtil.h" -#define VERSION @"3.0.3" +#define VERSION @"3.1.0" void *SensorsAnalyticsQueueTag = &SensorsAnalyticsQueueTag; @@ -62,8 +62,6 @@ @interface SensorsAnalyticsSDK() @property (nonatomic, strong) SATrackTimer *trackTimer; -@property (nonatomic, strong) NSRegularExpression *propertiesRegex; - @property (nonatomic, strong) NSTimer *timer; // 兼容 UA 值打通逻辑,后续废弃 UA 值打通逻辑时可以全部移除 @@ -86,9 +84,7 @@ @interface SensorsAnalyticsSDK() @end -@implementation SensorsAnalyticsSDK { - SensorsAnalyticsNetworkType _networkTypePolicy; -} +@implementation SensorsAnalyticsSDK #pragma mark - Initialization + (void)startWithConfigOptions:(SAConfigOptions *)configOptions { @@ -103,14 +99,14 @@ + (void)startWithConfigOptions:(SAConfigOptions *)configOptions { dispatch_once(&sdkInitializeOnceToken, ^{ sharedInstance = [[SensorsAnalyticsSDK alloc] initWithConfigOptions:configOptions debugMode:SensorsAnalyticsDebugOff]; [SAModuleManager startWithConfigOptions:sharedInstance.configOptions debugMode:SensorsAnalyticsDebugOff]; - [sharedInstance startAppLifecycle]; + [sharedInstance addAppLifecycleObservers]; }); } + (SensorsAnalyticsSDK *_Nullable)sharedInstance { NSAssert(sharedInstance, @"请先使用 startWithConfigOptions: 初始化 SDK"); if ([SAModuleManager.sharedInstance isDisableSDK]) { - SALogDebug(@"【remote config】SDK is disabled"); + SALogDebug(@"SDK is disabled"); return nil; } return sharedInstance; @@ -121,6 +117,57 @@ + (SensorsAnalyticsSDK *)sdkInstance { return sharedInstance; } ++ (void)disableSDK { + SensorsAnalyticsSDK *instance = SensorsAnalyticsSDK.sdkInstance; + if (instance.configOptions.disableSDK) { + return; + } + [instance track:@"$AppDataTrackingClose"]; + [instance flush]; + + [instance clearTrackTimer]; + [instance stopFlushTimer]; + [instance removeObservers]; + [instance removeWebViewUserAgent]; + + [SAReachability.sharedInstance stopMonitoring]; + + [SAModuleManager.sharedInstance disableAllModules]; + + instance.configOptions.disableSDK = YES; + + SALogWarn(@"SensorsAnalyticsSDK disabled"); + [SALog sharedLog].enableLog = NO; +} + ++ (void)enableSDK { + SensorsAnalyticsSDK *instance = SensorsAnalyticsSDK.sdkInstance; + if (!instance.configOptions.disableSDK) { + return; + } + instance.configOptions.disableSDK = NO; + // 部分模块和监听依赖网络状态,所以需要优先开启 + [SAReachability.sharedInstance startMonitoring]; + + // 优先添加远程控制监听,防止热启动时关闭 SDK 的情况下 + [instance addRemoteConfigObservers]; + + if (instance.configOptions.enableLog) { + [instance enableLog:YES]; + } + + // Debug 模块做了特殊处理,未移除,可以使用 Debug 模块获取 DebugMode + SensorsAnalyticsDebugMode mode = SAModuleManager.sharedInstance.debugMode; + [SAModuleManager startWithConfigOptions:instance.configOptions debugMode:mode]; + + // 需要在模块加载完成之后添加监听,如果过早会导致退到后台后,$AppEnd 事件无法立即上报 + [instance addAppLifecycleObservers]; + + [instance appendWebViewUserAgent]; + [instance startFlushTimer]; + SALogInfo(@"SensorsAnalyticsSDK enabled"); +} + - (instancetype)initWithServerURL:(NSString *)serverURL andLaunchOptions:(NSDictionary *)launchOptions andDebugMode:(SensorsAnalyticsDebugMode)debugMode { @@ -139,17 +186,7 @@ - (instancetype)initWithConfigOptions:(nonnull SAConfigOptions *)configOptions d self = [super init]; if (self) { _configOptions = [configOptions copy]; - - _networkTypePolicy = -#if TARGET_OS_IOS - SensorsAnalyticsNetworkType3G | - SensorsAnalyticsNetworkType4G | - -#ifdef __IPHONE_14_1 - SensorsAnalyticsNetworkType5G | -#endif -#endif - SensorsAnalyticsNetworkTypeWIFI; + _appLifecycle = [[SAAppLifecycle alloc] init]; _people = [[SensorsAnalyticsPeople alloc] init]; @@ -160,24 +197,23 @@ - (instancetype)initWithConfigOptions:(nonnull SAConfigOptions *)configOptions d NSString *readWriteQueueLabel = [NSString stringWithFormat:@"com.sensorsdata.readWriteQueue.%p", self]; _readWriteQueue = dispatch_queue_create([readWriteQueueLabel UTF8String], DISPATCH_QUEUE_SERIAL); - [[SAReachability sharedInstance] startMonitoring]; - _network = [[SANetwork alloc] init]; _eventTracker = [[SAEventTracker alloc] initWithQueue:_serialQueue]; _trackTimer = [[SATrackTimer alloc] init]; - NSString *namePattern = @"^([a-zA-Z_$][a-zA-Z\\d_$]{0,99})$"; - _propertiesRegex = [NSRegularExpression regularExpressionWithPattern:namePattern options:NSRegularExpressionCaseInsensitive error:nil]; - _identifier = [[SAIdentifier alloc] initWithQueue:_readWriteQueue]; _presetProperty = [[SAPresetProperty alloc] initWithQueue:_readWriteQueue libVersion:[self libVersion]]; _superProperty = [[SASuperProperty alloc] init]; - - if (_configOptions.enableLog) { - [self enableLog:YES]; + + if (!_configOptions.disableSDK) { + if (_configOptions.enableLog) { + [self enableLog:_configOptions.enableLog]; + } + [[SAReachability sharedInstance] startMonitoring]; + [self addRemoteConfigObservers]; } #if TARGET_OS_IOS @@ -185,8 +221,6 @@ - (instancetype)initWithConfigOptions:(nonnull SAConfigOptions *)configOptions d [SAReferrerManager sharedInstance].serialQueue = _serialQueue; [SAReferrerManager sharedInstance].enableReferrerTitle = configOptions.enableReferrerTitle; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteConfigManagerModelChanged:) name:SA_REMOTE_CONFIG_MODEL_CHANGED_NOTIFICATION object:nil]; #endif } @@ -201,6 +235,10 @@ - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } +- (void)removeObservers { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + #if TARGET_OS_IOS - (void)setupSecurityPolicyWithConfigOptions:(SAConfigOptions *)options { SASecurityPolicy *securityPolicy = options.securityPolicy; @@ -278,6 +316,9 @@ - (void)setServerUrl:(NSString *)serverUrl isRequestRemoteConfig:(BOOL)isRequest } dispatch_async(self.serialQueue, ^{ + // 更新数据接收地址 + [SAModuleManager.sharedInstance updateServerURL:serverUrl]; + self.configOptions.serverURL = serverUrl; if (isRequestRemoteConfig) { [SAModuleManager.sharedInstance retryRequestRemoteConfigWithForceUpdateFlag:YES]; @@ -285,26 +326,6 @@ - (void)setServerUrl:(NSString *)serverUrl isRequestRemoteConfig:(BOOL)isRequest }); } -- (void)setFlushNetworkPolicy:(SensorsAnalyticsNetworkType)networkType { - @synchronized (self) { - _networkTypePolicy = networkType; - } -} - -- (void)setMaxCacheSize:(UInt64)maxCacheSize { - @synchronized(self) { - //防止设置的值太小导致事件丢失 - UInt64 temMaxCacheSize = maxCacheSize > 10000 ? maxCacheSize : 10000; - self.configOptions.maxCacheSize = (NSInteger)temMaxCacheSize; - }; -} - -- (UInt64)getMaxCacheSize { - @synchronized(self) { - return (UInt64)self.configOptions.maxCacheSize; - }; -} - - (void)login:(NSString *)loginId { [self login:loginId withProperties:nil]; } @@ -356,12 +377,6 @@ - (void)resetAnonymousId { }); } -- (void)trackAppCrash { - _configOptions.enableTrackAppCrash = YES; - // Install uncaught exception handlers first - [SAModuleManager.sharedInstance setEnable:YES forModuleType:SAModuleTypeException]; -} - - (void)showDebugInfoView:(BOOL)show { [SAModuleManager.sharedInstance setShowDebugAlertView:show]; } @@ -380,11 +395,14 @@ - (void)deleteAll { #pragma mark - AppLifecycle -- (void)startAppLifecycle { +/// 在所有模块加载完成之后调用,添加通知 +/// 注意⚠️:不要随意调整通知添加顺序 +- (void)addAppLifecycleObservers { + if (self.configOptions.disableSDK) { + return; + } [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appLifecycleStateWillChange:) name:kSAAppLifecycleStateWillChangeNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appLifecycleStateDidChange:) name:kSAAppLifecycleStateDidChangeNotification object:nil]; - - _appLifecycle = [[SAAppLifecycle alloc] init]; } // 处理事件触发之前的逻辑 @@ -986,6 +1004,17 @@ - (void)clearKeychainData { #pragma mark - RemoteConfig +/// 远程控制通知回调需要在所有其他通知之前调用 +/// 注意⚠️:不要随意调整通知添加顺序 +- (void)addRemoteConfigObservers { + if (self.configOptions.disableSDK) { + return; + } +#if TARGET_OS_IOS + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(remoteConfigManagerModelChanged:) name:SA_REMOTE_CONFIG_MODEL_CHANGED_NOTIFICATION object:nil]; +#endif +} + - (void)remoteConfigManagerModelChanged:(NSNotification *)sender { @try { BOOL isDisableDebugMode = [[sender.object valueForKey:@"disableDebugMode"] boolValue]; @@ -1029,7 +1058,11 @@ - (void)appendWebViewUserAgent { // 没有开启老版打通 return; } - + + if ([SAModuleManager.sharedInstance isDisableSDK]) { + return; + } + NSString *currentUserAgent = [SACommonUtility currentUserAgent]; if ([currentUserAgent containsString:self.addWebViewUserAgent]) { return; @@ -1304,7 +1337,7 @@ + (SensorsAnalyticsSDK *)sharedInstanceWithServerURL:(NSString *)serverURL andLaunchOptions:launchOptions andDebugMode:debugMode]; [SAModuleManager startWithConfigOptions:sharedInstance.configOptions debugMode:debugMode]; - [sharedInstance startAppLifecycle]; + [sharedInstance addAppLifecycleObservers]; }); return sharedInstance; } @@ -1317,7 +1350,7 @@ + (SensorsAnalyticsSDK *)sharedInstanceWithServerURL:(nonnull NSString *)serverU andLaunchOptions:launchOptions andDebugMode:SensorsAnalyticsDebugOff]; [SAModuleManager startWithConfigOptions:sharedInstance.configOptions debugMode:SensorsAnalyticsDebugOff]; - [sharedInstance startAppLifecycle]; + [sharedInstance addAppLifecycleObservers]; }); return sharedInstance; } @@ -1366,6 +1399,32 @@ - (void)setFlushBeforeEnterBackground:(BOOL)flushBeforeEnterBackground { } } +- (void)setFlushNetworkPolicy:(SensorsAnalyticsNetworkType)networkType { + @synchronized (self) { + self.configOptions.flushNetworkPolicy = networkType; + } +} + +- (void)setMaxCacheSize:(UInt64)maxCacheSize { + @synchronized(self) { + //防止设置的值太小导致事件丢失 + UInt64 temMaxCacheSize = maxCacheSize > 10000 ? maxCacheSize : 10000; + self.configOptions.maxCacheSize = (NSInteger)temMaxCacheSize; + }; +} + +- (UInt64)getMaxCacheSize { + @synchronized(self) { + return (UInt64)self.configOptions.maxCacheSize; + }; +} + +- (void)trackAppCrash { + _configOptions.enableTrackAppCrash = YES; + // Install uncaught exception handlers first + [SAModuleManager.sharedInstance setEnable:YES forModuleType:SAModuleTypeException]; +} + - (void)setDebugMode:(SensorsAnalyticsDebugMode)debugMode { SAModuleManager.sharedInstance.debugMode = debugMode; } diff --git a/SensorsAnalyticsSDK/DebugMode/SADebugModeManager.m b/SensorsAnalyticsSDK/DebugMode/SADebugModeManager.m index a9b58c08..06caf45e 100644 --- a/SensorsAnalyticsSDK/DebugMode/SADebugModeManager.m +++ b/SensorsAnalyticsSDK/DebugMode/SADebugModeManager.m @@ -70,6 +70,9 @@ - (BOOL)handleURL:(nonnull NSURL *)url { #pragma mark - SADebugModeModuleProtocol - (void)handleDebugMode:(SensorsAnalyticsDebugMode)mode { + if (_debugMode == mode) { + return; + } _debugMode = mode; if (_debugMode == SensorsAnalyticsDebugOff) { diff --git a/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.h b/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.h index 19671c4e..118562a3 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.h +++ b/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.h @@ -25,7 +25,6 @@ NS_ASSUME_NONNULL_BEGIN extern NSString * const kSAAlgorithmTypeAES; extern NSString * const kSAAlgorithmTypeRSA; extern NSString * const kSAAlgorithmTypeECC; -extern NSString * const kSAEncryptECCClassName; @protocol SAAlgorithmProtocol diff --git a/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.m b/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.m index c869953c..766a450a 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.m +++ b/SensorsAnalyticsSDK/Encrypt/SAAlgorithmProtocol.m @@ -27,4 +27,3 @@ NSString * const kSAAlgorithmTypeAES = @"AES"; NSString * const kSAAlgorithmTypeRSA = @"RSA"; NSString * const kSAAlgorithmTypeECC = @"EC"; -NSString * const kSAEncryptECCClassName = @"SACryptoppECC"; diff --git a/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.h b/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.h index 1c7744bc..86474b58 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.h +++ b/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.h @@ -18,24 +18,15 @@ // limitations under the License. // -#import "SAConfigOptions.h" #import "SAEncryptProtocol.h" -#import "SASecretKey.h" +#import "SAConfigOptions.h" -@interface SAConfigOptions (Encrypt) +NS_ASSUME_NONNULL_BEGIN -@property (nonatomic, copy, readonly) NSArray *encryptors; +@interface SAConfigOptions (Encrypt) -- (void)registerEncryptor:(id)encryptor; +- (void)registerEncryptor:(id)encryptor API_UNAVAILABLE(macos); @end -@interface SASecretKey (Private) - -/// 对称加密类型 -@property (nonatomic, copy) NSString *symmetricEncryptType; - -/// 非对称加密类型 -@property (nonatomic, copy) NSString *asymmetricEncryptType; - -@end +NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.m b/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.m index e8b8eab1..7dfaca7c 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.m +++ b/SensorsAnalyticsSDK/Encrypt/SAConfigOptions+Encrypt.m @@ -26,7 +26,7 @@ @interface SAConfigOptions () -@property (nonatomic, strong, readwrite) NSMutableArray *encryptors; +@property (atomic, strong, readwrite) NSMutableArray *encryptors; @end diff --git a/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.h b/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.h index 767d4b0a..6ae3f2e8 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.h +++ b/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.h @@ -23,6 +23,9 @@ NS_ASSUME_NONNULL_BEGIN +extern NSString * const kSAEncryptECCClassName; +extern NSString * const kSAEncryptECCPrefix; + @interface SAECCEncryptor : NSObject @property (nonatomic, copy) NSString *key; diff --git a/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.m b/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.m index 68f64f62..ca9f802e 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.m +++ b/SensorsAnalyticsSDK/Encrypt/SAECCEncryptor.m @@ -26,6 +26,7 @@ #import "SAValidator.h" #import "SALog.h" +NSString * const kSAEncryptECCClassName = @"SACryptoppECC"; NSString * const kSAEncryptECCPrefix = @"EC:"; typedef NSString* (*SAEEncryptImplementation)(Class, SEL, NSString *, NSString *); @@ -38,11 +39,12 @@ - (void)setKey:(NSString *)key { return; } - if (![key hasPrefix:kSAEncryptECCPrefix]) { - SALogError(@"Enable ECC encryption but the secret key is not ECC key!"); - return; + // 兼容老版本逻辑,当前缀包含 EC: 时删除前缀信息 + if ([key hasPrefix:kSAEncryptECCPrefix]) { + _key = [key substringFromIndex:[kSAEncryptECCPrefix length]]; + } else { + _key = key; } - _key = [key substringFromIndex:[kSAEncryptECCPrefix length]]; } #pragma mark - Public Methods diff --git a/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.h b/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.h index 525baed9..668bfa13 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.h +++ b/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.h @@ -26,6 +26,8 @@ NS_ASSUME_NONNULL_BEGIN @interface SAECCPluginEncryptor : NSObject ++ (BOOL)isAvaliable; + @end NS_ASSUME_NONNULL_END diff --git a/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.m b/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.m index 8cf41aef..407d8a33 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.m +++ b/SensorsAnalyticsSDK/Encrypt/SAECCPluginEncryptor.m @@ -35,6 +35,10 @@ @interface SAECCPluginEncryptor () @implementation SAECCPluginEncryptor ++ (BOOL)isAvaliable { + return NSClassFromString(kSAEncryptECCClassName) != nil; +} + - (instancetype)init { self = [super init]; if (self) { @@ -57,7 +61,9 @@ - (NSString *)encryptEvent:(NSData *)event { } - (NSString *)encryptSymmetricKeyWithPublicKey:(NSString *)publicKey { - _eccEncryptor.key = publicKey; + if (![_eccEncryptor.key isEqualToString:publicKey]) { + _eccEncryptor.key = publicKey; + } return [_eccEncryptor encryptData:_aesEncryptor.key]; } diff --git a/SensorsAnalyticsSDK/Encrypt/SAEncryptManager.m b/SensorsAnalyticsSDK/Encrypt/SAEncryptManager.m index 536b6355..c3588dc4 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAEncryptManager.m +++ b/SensorsAnalyticsSDK/Encrypt/SAEncryptManager.m @@ -33,10 +33,17 @@ #import "SARSAPluginEncryptor.h" #import "SAECCPluginEncryptor.h" #import "SAConfigOptions+Encrypt.h" +#import "SASecretKey.h" #import "SASecretKeyFactory.h" static NSString * const kSAEncryptSecretKey = @"SAEncryptSecretKey"; +@interface SAConfigOptions (Private) + +@property (atomic, strong, readonly) NSMutableArray *encryptors; + +@end + @interface SAEncryptManager () /// 当前使用的加密插件 @@ -45,9 +52,10 @@ @interface SAEncryptManager () /// 当前支持的加密插件列表 @property (nonatomic, copy) NSArray> *encryptors; +/// 已加密过的对称秘钥内容 @property (nonatomic, copy) NSString *encryptedSymmetricKey; -/// 非对称密钥加密器的公钥(RSA/ECC 的公钥) +/// 非对称加密器的公钥(RSA/ECC 的公钥) @property (nonatomic, strong) SASecretKey *secretKey; @end @@ -68,7 +76,11 @@ - (void)setConfigOptions:(SAConfigOptions *)configOptions { _configOptions = configOptions; NSMutableArray *encryptors = [NSMutableArray array]; - [encryptors addObject:[[SAECCPluginEncryptor alloc] init]]; + + // 当 ECC 加密库未集成时,不注册 ECC 加密插件 + if ([SAECCPluginEncryptor isAvaliable]) { + [encryptors addObject:[[SAECCPluginEncryptor alloc] init]]; + } [encryptors addObject:[[SARSAPluginEncryptor alloc] init]]; [encryptors addObjectsFromArray:configOptions.encryptors]; self.encryptors = encryptors; @@ -86,17 +98,21 @@ - (BOOL)handleURL:(nonnull NSURL *)url { if (self.enable) { NSDictionary *paramDic = [SAURLUtils queryItemsWithURL:url]; NSString *urlVersion = paramDic[@"v"]; - NSString *urlKey = paramDic[@"key"]; + + // url 中的 key 为 encode 之后的,这里做 decode + NSString *urlKey = [paramDic[@"key"] stringByRemovingPercentEncoding]; if ([SAValidator isValidString:urlVersion] && [SAValidator isValidString:urlKey]) { SASecretKey *secretKey = [self loadCurrentSecretKey]; NSString *loadVersion = [@(secretKey.version) stringValue]; - // url 中的 key 为 encode 之后的 - NSString *loadKey = [secretKey.key stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet alphanumericCharacterSet]]; - if ([loadVersion isEqualToString:urlVersion] && [loadKey isEqualToString:urlKey]) { + // 这里为了兼容新老版本下发的 EC 秘钥中 URL key 前缀和本地保存的 EC 秘钥前缀不一致的问题,都统一删除 EC 前缀后比较内容 + NSString *currentKey = [secretKey.key hasPrefix:kSAEncryptECCPrefix] ? [secretKey.key substringFromIndex:kSAEncryptECCPrefix.length] : secretKey.key; + NSString *decodeKey = [urlKey hasPrefix:kSAEncryptECCPrefix] ? [urlKey substringFromIndex:kSAEncryptECCPrefix.length] : urlKey; + + if ([loadVersion isEqualToString:urlVersion] && [currentKey isEqualToString:decodeKey]) { message = @"密钥验证通过,所选密钥与 App 端密钥相同"; - } else if (![SAValidator isValidString:loadKey]) { + } else if (![SAValidator isValidString:currentKey]) { message = @"密钥验证不通过,App 端密钥为空"; } else { message = [NSString stringWithFormat:@"密钥验证不通过,所选密钥与 App 端密钥不相同。所选密钥版本:%@,App 端密钥版本:%@", urlVersion, loadVersion]; @@ -172,9 +188,16 @@ - (void)handleEncryptWithConfig:(NSDictionary *)encryptConfig { if (!encryptConfig) { return; } - SASecretKey *secretKey = [SASecretKeyFactory generateSecretKeyWithRemoteConfig:encryptConfig]; + + // 加密插件化 2.0 新增字段,下发秘钥信息不可用时,继续走 1.0 逻辑 + SASecretKey *secretKey = [SASecretKeyFactory createSecretKeyByVersion2:encryptConfig[@"key_v2"]]; + if (![self encryptorWithSecretKey:secretKey]) { + // 加密插件化 1.0 秘钥信息 + secretKey = [SASecretKeyFactory createSecretKeyByVersion1:encryptConfig[@"key"]]; + } + + //当前秘钥没有对应的加密器 if (![self encryptorWithSecretKey:secretKey]) { - //当前秘钥没有对应的加密器 return; } // 存储请求的公钥 @@ -236,9 +259,11 @@ - (BOOL)isSameSecretKey:(SASecretKey *)currentSecretKey newSecretKey:(SASecretKe - (id)filterEncrptor:(SASecretKey *)secretKey { id encryptor = [self encryptorWithSecretKey:secretKey]; - // 特殊处理,当秘钥类型为 ECC 且未集成 ECC 加密库时,进行断言提示 - if ((!NSClassFromString(kSAEncryptECCClassName) && [encryptor isKindOfClass:SAECCPluginEncryptor.class])) { - NSAssert(NO, @"\n您使用了 ECC 密钥,但是并没有集成 ECC 加密库。\n • 如果使用源码集成 ECC 加密库,请检查是否包含名为 SAECCEncrypt 的文件? \n • 如果使用 CocoaPods 集成 SDK,请修改 Podfile 文件并增加 ECC 模块,例如:pod 'SensorsAnalyticsEncrypt'。\n"); + if (!encryptor) { + NSString *format = @"\n您使用了 [%@] 密钥,但是并没有注册对应加密插件。\n • 若您使用的是 EC+AES 或 SM2+SM4 加密方式,请检查是否正确集成 'SensorsAnalyticsEncrypt' 模块,且已注册对应加密插件。\n"; + NSString *type = [NSString stringWithFormat:@"%@+%@", secretKey.asymmetricEncryptType, secretKey.symmetricEncryptType]; + NSString *message = [NSString stringWithFormat:format, type]; + NSAssert(NO, message); return nil; } return encryptor; @@ -250,7 +275,10 @@ - (BOOL)isSameSecretKey:(SASecretKey *)currentSecretKey newSecretKey:(SASecretKe } __block id encryptor; [self.encryptors enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop) { - if ([self checkEncryptType:obj secretKey:secretKey]) { + BOOL isSameAsymmetricType = [[obj asymmetricEncryptType] isEqualToString:secretKey.asymmetricEncryptType]; + BOOL isSameSymmetricType = [[obj symmetricEncryptType] isEqualToString:secretKey.symmetricEncryptType]; + // 当非对称加密类型和对称加密类型都匹配一致时,返回对应加密器 + if (isSameAsymmetricType && isSameSymmetricType) { encryptor = obj; *stop = YES; } @@ -258,16 +286,6 @@ - (BOOL)isSameSecretKey:(SASecretKey *)currentSecretKey newSecretKey:(SASecretKe return encryptor; } -- (BOOL)checkEncryptType:(id)encryptor secretKey:(SASecretKey *)secretKey { - if (![[encryptor symmetricEncryptType] isEqualToString:secretKey.symmetricEncryptType]) { - return NO; - } - if (![[encryptor asymmetricEncryptType] isEqualToString:secretKey.asymmetricEncryptType]) { - return NO; - } - return YES; -} - #pragma mark - archive/unarchive secretKey - (void)saveRequestSecretKey:(SASecretKey *)secretKey { if (!secretKey) { @@ -317,15 +335,6 @@ - (SASecretKey *)loadCurrentSecretKey { SALogDebug(@"Load secret key from localSecretKey failed!"); } } - - // 兼容老版本保存的秘钥 - if (!secretKey.symmetricEncryptType) { - secretKey.symmetricEncryptType = kSAAlgorithmTypeAES; - } - if (!secretKey.asymmetricEncryptType) { - BOOL isECC = [secretKey.key hasPrefix:kSAAlgorithmTypeECC]; - secretKey.asymmetricEncryptType = isECC ? kSAAlgorithmTypeECC : kSAAlgorithmTypeRSA; - } return secretKey; } diff --git a/SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h b/SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h index 0980afd8..ce3e8c63 100644 --- a/SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h +++ b/SensorsAnalyticsSDK/Encrypt/SAEncryptProtocol.h @@ -18,6 +18,8 @@ // limitations under the License. // +#import + @protocol SAEncryptProtocol /// 返回对称加密的类型,例如 AES diff --git a/SensorsAnalyticsSDK/Encrypt/SARSAPluginEncryptor.m b/SensorsAnalyticsSDK/Encrypt/SARSAPluginEncryptor.m index ddd604c3..fd4663ca 100644 --- a/SensorsAnalyticsSDK/Encrypt/SARSAPluginEncryptor.m +++ b/SensorsAnalyticsSDK/Encrypt/SARSAPluginEncryptor.m @@ -63,7 +63,9 @@ - (NSString *)encryptEvent:(NSData *)event { /// 返回加密后的对称密钥数据 /// @param publicKey 非对称加密算法的公钥,用于加密对称密钥 - (NSString *)encryptSymmetricKeyWithPublicKey:(NSString *)publicKey { - _rsaEncryptor.key = publicKey; + if (![_rsaEncryptor.key isEqualToString:publicKey]) { + _rsaEncryptor.key = publicKey; + } return [_rsaEncryptor encryptData:_aesEncryptor.key]; } diff --git a/SensorsAnalyticsSDK/Encrypt/SASecretKey.h b/SensorsAnalyticsSDK/Encrypt/SASecretKey.h index 259175b5..0929a4fe 100644 --- a/SensorsAnalyticsSDK/Encrypt/SASecretKey.h +++ b/SensorsAnalyticsSDK/Encrypt/SASecretKey.h @@ -25,11 +25,31 @@ NS_ASSUME_NONNULL_BEGIN /// 密钥信息 @interface SASecretKey : NSObject +/// 指定构造器,初始化时必须传入四个参数 +/// @param key 非对称加密时使用的公钥值 +/// @param version 非对称加密时使用的公钥值对应版本 +/// @param asymmetricEncryptType 非对称加密类型 +/// @param symmetricEncryptType 对称加密类型 +/// @return 返回秘钥实例 +- (instancetype)initWithKey:(NSString *)key + version:(NSInteger)version + asymmetricEncryptType:(NSString *)asymmetricEncryptType + symmetricEncryptType:(NSString *)symmetricEncryptType; + +/// 禁用 init 初始化方法 +- (instancetype)init NS_UNAVAILABLE; + /// 密钥版本 -@property (nonatomic, assign) NSInteger version; +@property (nonatomic, assign, readonly) NSInteger version; /// 密钥值 -@property (nonatomic, copy) NSString *key; +@property (nonatomic, copy, readonly) NSString *key; + +/// 对称加密类型 +@property (nonatomic, copy, readonly) NSString *symmetricEncryptType; + +/// 非对称加密类型 +@property (nonatomic, copy, readonly) NSString *asymmetricEncryptType; @end diff --git a/SensorsAnalyticsSDK/Encrypt/SASecretKey.m b/SensorsAnalyticsSDK/Encrypt/SASecretKey.m index aae5d526..a9bd3f29 100644 --- a/SensorsAnalyticsSDK/Encrypt/SASecretKey.m +++ b/SensorsAnalyticsSDK/Encrypt/SASecretKey.m @@ -23,9 +23,16 @@ #endif #import "SASecretKey.h" +#import "SAAlgorithmProtocol.h" @interface SASecretKey () +/// 密钥版本 +@property (nonatomic, assign) NSInteger version; + +/// 密钥值 +@property (nonatomic, copy) NSString *key; + /// 对称加密类型 @property (nonatomic, copy) NSString *symmetricEncryptType; @@ -36,6 +43,20 @@ @interface SASecretKey () @implementation SASecretKey +- (instancetype)initWithKey:(NSString *)key + version:(NSInteger)version + asymmetricEncryptType:(NSString *)asymmetricEncryptType + symmetricEncryptType:(NSString *)symmetricEncryptType { + self = [super init]; + if (self) { + self.version = version; + self.key = key; + [self updateAsymmetricType:asymmetricEncryptType symmetricType:symmetricEncryptType]; + + } + return self; +} + - (void)encodeWithCoder:(NSCoder *)coder { [coder encodeInteger:self.version forKey:@"version"]; [coder encodeObject:self.key forKey:@"key"]; @@ -48,10 +69,29 @@ - (instancetype)initWithCoder:(NSCoder *)coder { if (self) { self.version = [coder decodeIntegerForKey:@"version"]; self.key = [coder decodeObjectForKey:@"key"]; - self.symmetricEncryptType = [coder decodeObjectForKey:@"symmetricEncryptType"]; - self.asymmetricEncryptType = [coder decodeObjectForKey:@"asymmetricEncryptType"]; + + NSString *symmetricType = [coder decodeObjectForKey:@"symmetricEncryptType"]; + NSString *asymmetricType = [coder decodeObjectForKey:@"asymmetricEncryptType"]; + [self updateAsymmetricType:asymmetricType symmetricType:symmetricType]; } return self; } +- (void)updateAsymmetricType:(NSString *)asymmetricType symmetricType:(NSString *)symmetricType { + // 兼容老版本保存的秘钥 + if (!symmetricType) { + self.symmetricEncryptType = kSAAlgorithmTypeAES; + } else { + self.symmetricEncryptType = symmetricType; + } + + // 兼容老版本保存的秘钥 + if (!asymmetricType) { + BOOL isECC = [self.key hasPrefix:kSAAlgorithmTypeECC]; + self.asymmetricEncryptType = isECC ? kSAAlgorithmTypeECC : kSAAlgorithmTypeRSA; + } else { + self.asymmetricEncryptType = asymmetricType; + } +} + @end diff --git a/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.h b/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.h index 09b1451b..c46b9b1a 100644 --- a/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.h +++ b/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.h @@ -26,7 +26,20 @@ NS_ASSUME_NONNULL_BEGIN @interface SASecretKeyFactory : NSObject -+ (SASecretKey *)generateSecretKeyWithRemoteConfig:(NSDictionary *)remoteConfig; +typedef BOOL(^EncryptorChecker)(SASecretKey *secretKey); + +/// { "key_v2": { "pkv": 27, "public_key": "<公钥>", "type": "SM2+SM4"} , +/// "key ": { " pkv": 23, "public_key": "<公钥>", "key_ec": "{ \"pkv\":26,\"type\":\"EC\",\"public_key\":\<公钥>\" }" } } + +/// 根据 key_v2 秘钥信息生成对应的秘钥实例对象,加密插件化 2.0 逻辑 +/// @param version2 key_v2 秘钥信息 +/// @return 返回可用秘钥对象 ++ (SASecretKey *)createSecretKeyByVersion2:(NSDictionary *)version2; + +/// 根据 key 秘钥信息生成对应的秘钥实例对象,加密插件化 1.0 逻辑 +/// @param version1 key 版本秘钥信息 +/// @return 返回可用秘钥对象 ++ (SASecretKey *)createSecretKeyByVersion1:(NSDictionary *)version1; @end diff --git a/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.m b/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.m index ae6c1965..26b4ffee 100644 --- a/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.m +++ b/SensorsAnalyticsSDK/Encrypt/SASecretKeyFactory.m @@ -24,83 +24,95 @@ #import "SASecretKeyFactory.h" #import "SAConfigOptions.h" -#import "SAConfigOptions+Encrypt.h" +#import "SASecretKey.h" #import "SAValidator.h" #import "SAJSONUtil.h" -#import "SAECCEncryptor.h" -#import "SAAESEncryptor.h" -#import "SARSAEncryptor.h" +#import "SAAlgorithmProtocol.h" +#import "SAECCPluginEncryptor.h" + +static NSString *const kSAEncryptVersion = @"pkv"; +static NSString *const kSAEncryptPublicKey = @"public_key"; +static NSString *const kSAEncryptType = @"type"; +static NSString *const kSAEncryptTypeSeparate = @"+"; @implementation SASecretKeyFactory -+ (SASecretKey *)generateSecretKeyWithRemoteConfig:(NSDictionary *)remoteConfig { - if (!remoteConfig) { +#pragma mark - Encryptor Plugin 2.0 ++ (SASecretKey *)createSecretKeyByVersion2:(NSDictionary *)version2 { + // key_v2 不存在时直接跳过 2.0 逻辑 + if (!version2) { return nil; } - NSString *customContent = remoteConfig[@"key_custom_placeholder"]; - if ([SAValidator isValidString:customContent]) { - // 当自定义插件秘钥字段存在时,不再使用其他加密插件 - // 不论秘钥是否创建成功,都不再切换使用其他加密插件 - NSDictionary *config = [SAJSONUtil JSONObjectWithString:customContent]; - SASecretKey *secretKey = [self createCustomSecretKey:config]; - return secretKey; + + NSNumber *pkv = version2[kSAEncryptVersion]; + NSString *type = version2[kSAEncryptType]; + NSString *publicKey = version2[kSAEncryptPublicKey]; + + // 检查相关参数是否有效 + if (!pkv || ![SAValidator isValidString:type] || ![SAValidator isValidString:publicKey]) { + return nil; } - NSString *eccContent = remoteConfig[@"key_ec"]; - if (eccContent && NSClassFromString(kSAEncryptECCClassName)) { - // 当 key_ec 存在且加密库存在时,使用 ECC 加密插件 - // 不论秘钥是否创建成功,都不再切换使用其他加密插件 - NSDictionary *config = [SAJSONUtil JSONObjectWithString:eccContent]; - SASecretKey *secretKey = [self createECCSecretKey:config]; - return secretKey; + NSArray *types = [type componentsSeparatedByString:kSAEncryptTypeSeparate]; + // 当 type 分隔数组个数小于 2 时 type 不合法,不处理秘钥信息 + if (types.count < 2) { + return nil; } - // 当远程配置不包含自定义秘钥且 ECC 不可用时,使用 RSA 秘钥 - return [self createRSASecretKey:remoteConfig]; + // 非对称加密类型,例如: SM2 + NSString *asymmetricType = types[0]; + + // 对称加密类型,例如: SM4 + NSString *symmetricType = types[1]; + + return [[SASecretKey alloc] initWithKey:publicKey version:[pkv integerValue] asymmetricEncryptType:asymmetricType symmetricEncryptType:symmetricType]; } -+ (SASecretKey *)createCustomSecretKey:(NSDictionary *)config { - if (![SAValidator isValidDictionary:config]) { ++ (SASecretKey *)createSecretKeyByVersion1:(NSDictionary *)version1 { + if (!version1) { return nil; } - // 暂不支持自定义秘钥类型 - SASecretKey *secretKey = [[SASecretKey alloc] init]; - return secretKey; + // 1.0 历史版本逻辑,只处理 key 字段中内容 + NSString *eccContent = version1[@"key_ec"]; + + // 当 key_ec 存在且加密库存在时,使用 EC 加密插件 + // 不论秘钥是否创建成功,都不再切换使用其他加密插件 + + // 这里为了检查 ECC 插件是否存在,手动生成 ECC 模拟秘钥 + if (eccContent && [SAECCPluginEncryptor isAvaliable]) { + NSDictionary *config = [SAJSONUtil JSONObjectWithString:eccContent]; + return [SASecretKeyFactory createECCSecretKey:config]; + } + + // 当远程配置不包含自定义秘钥且 EC 不可用时,使用 RSA 秘钥 + return [SASecretKeyFactory createRSASecretKey:version1]; } +#pragma mark - Encryptor Plugin 1.0 + (SASecretKey *)createECCSecretKey:(NSDictionary *)config { if (![SAValidator isValidDictionary:config]) { return nil; } - NSNumber *pkv = config[@"pkv"]; - NSString *publicKey = config[@"public_key"]; - NSString *type = config[@"type"]; + NSNumber *pkv = config[kSAEncryptVersion]; + NSString *publicKey = config[kSAEncryptPublicKey]; + NSString *type = config[kSAEncryptType]; if (!pkv || ![SAValidator isValidString:type] || ![SAValidator isValidString:publicKey]) { return nil; } - SASecretKey *secretKey = [[SASecretKey alloc] init]; - secretKey.version = [pkv integerValue]; - secretKey.asymmetricEncryptType = type; - secretKey.symmetricEncryptType = kSAAlgorithmTypeAES; - secretKey.key = [NSString stringWithFormat:@"%@:%@", type, publicKey]; - return secretKey; + NSString *key = [NSString stringWithFormat:@"%@:%@", type, publicKey]; + return [[SASecretKey alloc] initWithKey:key version:[pkv integerValue] asymmetricEncryptType:type symmetricEncryptType:kSAAlgorithmTypeAES]; } + (SASecretKey *)createRSASecretKey:(NSDictionary *)config { if (![SAValidator isValidDictionary:config]) { return nil; } - NSNumber *pkv = config[@"pkv"]; - NSString *publicKey = config[@"public_key"]; + NSNumber *pkv = config[kSAEncryptVersion]; + NSString *publicKey = config[kSAEncryptPublicKey]; if (!pkv || ![SAValidator isValidString:publicKey]) { return nil; } - SASecretKey *secretKey = [[SASecretKey alloc] init]; - secretKey.version = [pkv integerValue]; - secretKey.key = publicKey; - secretKey.asymmetricEncryptType = kSAAlgorithmTypeRSA; - secretKey.symmetricEncryptType = kSAAlgorithmTypeAES; - return secretKey; + return [[SASecretKey alloc] initWithKey:publicKey version:[pkv integerValue] asymmetricEncryptType:kSAAlgorithmTypeRSA symmetricEncryptType:kSAAlgorithmTypeAES]; } @end diff --git a/SensorsAnalyticsSDK/RemoteConfig/SARemoteConfigOperator.m b/SensorsAnalyticsSDK/RemoteConfig/SARemoteConfigOperator.m index a2b865fb..795faa14 100644 --- a/SensorsAnalyticsSDK/RemoteConfig/SARemoteConfigOperator.m +++ b/SensorsAnalyticsSDK/RemoteConfig/SARemoteConfigOperator.m @@ -101,10 +101,18 @@ - (void)requestRemoteConfigWithForceUpdate:(BOOL)isForceUpdate completion:(void - (NSDictionary *)extractRemoteConfig:(NSDictionary *)config { @try { - NSMutableDictionary *configs = [NSMutableDictionary dictionaryWithDictionary:config[@"configs"]]; - [configs removeObjectForKey:@"key"]; - - NSMutableDictionary *remoteConfig = [NSMutableDictionary dictionaryWithDictionary:config]; + // 只读取远程配置信息中的开关状态,不处理加密等其他逻辑字段 + NSMutableDictionary *configs = [NSMutableDictionary dictionary]; + configs[@"disableDebugMode"] = config[@"configs"][@"disableDebugMode"]; + configs[@"disableSDK"] = config[@"configs"][@"disableSDK"]; + configs[@"autoTrackMode"] = config[@"configs"][@"autoTrackMode"]; + configs[@"event_blacklist"] = config[@"configs"][@"event_blacklist"]; + configs[@"effect_mode"] = config[@"configs"][@"effect_mode"]; + configs[@"nv"] = config[@"configs"][@"nv"]; + + // 读取远程配置信息中的版本信息 + NSMutableDictionary *remoteConfig = [NSMutableDictionary dictionary]; + remoteConfig[@"v"] = config[@"v"]; remoteConfig[@"configs"] = configs; return remoteConfig; @@ -115,7 +123,8 @@ - (void)requestRemoteConfigWithForceUpdate:(BOOL)isForceUpdate completion:(void } - (NSDictionary *)extractEncryptConfig:(NSDictionary *)config { - return config[@"configs"][@"key"]; + // 远程配置中新增 key_v2,传递给加密模块时不做处理直接传递整个远程配置信息 + return [config[@"configs"] copy]; } - (void)trackAppRemoteConfigChanged:(NSDictionary *)remoteConfig { diff --git a/SensorsAnalyticsSDK/Visualized/SAVisualizedManager.m b/SensorsAnalyticsSDK/Visualized/SAVisualizedManager.m index 13887f35..339766cb 100644 --- a/SensorsAnalyticsSDK/Visualized/SAVisualizedManager.m +++ b/SensorsAnalyticsSDK/Visualized/SAVisualizedManager.m @@ -25,12 +25,8 @@ #import "SAVisualizedManager.h" #import "SAVisualizedConnection.h" #import "SAAlertController.h" -#import "SensorsAnalyticsSDK+Private.h" -#import "SensorsAnalyticsSDK+Visualized.h" #import "UIViewController+SAElementPath.h" -#import "SAVisualPropertiesConfigSources.h" #import "SAConstants+Private.h" -#import "UIView+SAElementPath.h" #import "UIView+AutoTrack.h" #import "SAVisualizedUtils.h" #import "SAModuleManager.h" @@ -40,9 +36,6 @@ #import "SASwizzle.h" #import "SALog.h" -static void * const kSAVisualizeContext = (void*)&kSAVisualizeContext; -static NSString * const kSAVisualizeObserverKeyPath = @"serverURL"; - @interface SAVisualizedManager() @property (nonatomic, strong) SAVisualizedConnection *visualizedConnection; @@ -83,10 +76,6 @@ - (instancetype)init { return self; } -- (void)dealloc { - [self.configOptions removeObserver:self forKeyPath:kSAVisualizeObserverKeyPath context:kSAVisualizeContext]; -} - #pragma mark SAConfigChangesDelegate - (void)configChangedWithValid:(BOOL)valid { if (valid){ @@ -122,11 +111,6 @@ - (void)setEnable:(BOOL)enable { if (error) { SALogError(@"Failed to swizzle on UIViewController. Details: %@", error); } - - // 监听 configOptions 中 serverURL 变化,更新属性配置 - if (self.configOptions.enableVisualizedAutoTrack) { - [self.configOptions addObserver:self forKeyPath:kSAVisualizeObserverKeyPath options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) context:kSAVisualizeContext]; - } }); if (!self.configSources && self.configOptions.enableVisualizedAutoTrack) { @@ -136,6 +120,14 @@ - (void)setEnable:(BOOL)enable { } } +-(void)updateServerURL:(NSString *)serverURL { + if (![SAValidator isValidString:serverURL] || ![self.configOptions.serverURL isEqualToString:serverURL]) { + return; + } + // 刷新自定义属性配置 + [self.configSources loadConfig]; +} + #pragma mark - - (NSString *)javaScriptSource { if (!self.enable) { @@ -262,7 +254,6 @@ - (SensorsAnalyticsVisualizedType)currentVisualizedType { return self.visualizedType; } - #pragma mark - Visualize - (void)addVisualizeWithViewControllers:(NSArray *)controllers { @@ -286,24 +277,6 @@ - (BOOL)isVisualizeWithViewController:(UIViewController *)viewController { } #pragma mark - Property - -- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (context != kSAVisualizeContext) { - return [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; - } - - NSString *newValue = change[NSKeyValueChangeNewKey]; - if (![SAValidator isValidString:newValue]) { - return; - } - if (![keyPath isEqualToString:kSAVisualizeObserverKeyPath] || [newValue isEqualToString:change[NSKeyValueChangeOldKey]]) { - return; - } - // 更新配置信息 - [self.configSources reloadConfig]; -} - - - (nullable NSDictionary *)propertiesWithView:(UIView *)view { UIViewController *viewController = view.sensorsdata_viewController; if (!viewController) { @@ -354,4 +327,9 @@ - (void)enableEventCheck:(BOOL)enable { } } +- (void)dealloc { + // 断开连接,防止 visualizedConnection 内 timer 导致无法释放 + [self.visualizedConnection close]; +} + @end