#include "XUDPServer.h" #import #import "IPhoneHertbeat.h" #import "XSHackIos.h" #import "XSHelper.h" #import "XSPhoneInfo.h" #import "XSPhoneConfig.h" #import "MyAdServer.h" #import "MyAdTask2.h" #import "MyEventBus.h" BOOL myadTaskManualStop = NO; @interface MyAdTask2Mangger() { @private BOOL running; dispatch_source_t _timer; NSDate *lastRun; NSDate *lastGetCountry; NSString *_lastIdfa; NSString *taskId; NSString *taskAppId; int workType; int screen_w; int screen_h; int adAfter; NSString *linkId; NSString *dataId; NSString *remoteIp; NSString *country; dispatch_source_t _appendCheckTimer; // 新增定时器 } @property (nonatomic, assign) NSTimeInterval lastAppSwitchTime; // 新增属性 @end @implementation MyAdTask2Mangger +(instancetype)sharedInstance { static MyAdTask2Mangger* _sharedInstance = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _sharedInstance = [[MyAdTask2Mangger alloc] init]; }); return _sharedInstance; } -(instancetype)init { if (self = [super init]) { self->running = NO; self->_lastIdfa = @""; self.manQueue = dispatch_queue_create("com.xyzshell.myadtask", DISPATCH_QUEUE_SERIAL); CGFloat screen_scale = [[UIScreen mainScreen] scale]; CGFloat device_screen_width = [UIScreen mainScreen].bounds.size.width * screen_scale; CGFloat device_screen_height = [UIScreen mainScreen].bounds.size.height * screen_scale; self->screen_w = device_screen_width; self->screen_h = device_screen_height; self->adAfter = 1000; self.lastAppSwitchTime = 0; // 初始化为0 return self; } return nil; } - (int)onShow:(NSDictionary *)dic { if(dic == nil || [dic isEqual:[NSNull null]] || [dic count] <= 0) { return 4000; } NSString *t_appid = [self getStr:@"appid" dic:dic]; NSString *t_idfa = [self getStr:@"idfa" dic:dic]; NSString *t_adId = [self getStr:@"id" dic:dic]; NSNumber *t_ecpm = [self getNum:@"ecpm" dic:dic]; BOOL ad = dic[@"ad"]; NSString *iphoneId = [[XSPhoneConfig sharedInstance] IPhoneName]; if(!ad) { NSLog(@"XS- no ad show"); return 0; } int close = 4000; AdLoadInfo adloadInfo = getAdLoadInfo(@{ @"idfa": t_idfa ?: @"", @"adId": t_adId ?: @"", @"ecpm": [NSString stringWithFormat:@"%@", t_ecpm], @"appId": t_appid ?: @"", @"deviceId":iphoneId ?: @"" }); close = [adloadInfo.adTime intValue]; int before = [adloadInfo.adTouchBeforeMs intValue]; if (before >= close) { before = 1000; } int after = [adloadInfo.adTouchAfterMs intValue]; if(after > 1000) { self->adAfter = after; } else { self->adAfter = 1000; } NSInteger random1 = arc4random_uniform(101); NSInteger tr = [adloadInfo.touchRate intValue]; if (tr > random1) { double beforeSec = before / 1000.0; __weak typeof(self) weakSelf = self; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(beforeSec * NSEC_PER_SEC)), self.manQueue, ^{ }); } return close; } - (void)showStatus:(NSString *)data { [[MyEventBus sharedInstance] postEvent:@"UpdateStatus" withObject:data]; } - (void)setRemoteInfo { if (self->country != nil && ![self->country isEqual:@""]) { NSLog(@"Country info already set, skipping remote info update."); return; // 如果国家信息已存在,则跳过更新 } NSString *url = @"https://ipapi.co/json/"; if (!url) { NSLog(@"Invalid URL for remote info"); return; } XSHttpHelper *http = [[XSHttpHelper alloc] init]; __weak typeof(self) weakSelf = self; [http doGET:url withCallback:^(NSData *data) { if(!data) { NSLog(@"!setRemoteInfo 1data"); [weakSelf showStatus:@"!setRemoteInfo 1data"]; return; } NSDictionary *dic = [XSHelper jsonData2Dictionary:data]; if (!dic) { NSLog(@"!setRemoteInfo dic"); [weakSelf showStatus:@"!setRemoteInfo dic"]; return; } NSString *isoCode = dic[@"country_code"]; NSString *ip = dic[@"ip"]; if (!isoCode || !ip) { NSLog(@"Invalid data received for remote info"); return; } __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; strongSelf->remoteIp = [ip copy]; strongSelf->country = [isoCode copy]; [XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip]; [weakSelf showStatus:[XSPhoneInfo sharedInstance].remoteIp]; } withError:^(NSError *err) { NSLog(@"!setRemoteInfo err:%@",err); [weakSelf showStatus:[NSString stringWithFormat:@"%@", err.description]]; }]; } - (void)setRemoteInfo1 { NSString *url = [[XSPhoneConfig sharedInstance] GetRemoteIPURL]; [self showStatus:url]; if (!url) { NSLog(@"!setRemoteInfo url"); return; } __weak typeof(self) weakSelf = self; XSHttpHelper *http = [[XSHttpHelper alloc] init]; [http doGET:url withCallback:^(NSData *data) { if(!data) { NSLog(@"!setRemoteInfo 1data"); [weakSelf showStatus:@"!setRemoteInfo 1data"]; return; } NSDictionary *dic = [XSHelper jsonData2Dictionary:data]; if (!dic) { NSLog(@"!setRemoteInfo dic"); [weakSelf showStatus:@"!setRemoteInfo dic"]; return; } NSDictionary *_data = dic[@"data"]; if(!_data) { NSLog(@"!setRemoteInfo data"); [weakSelf showStatus:@"!setRemoteInfo data"]; return; } NSString *isoCode = _data[@"isoCode"]; NSString *ip = _data[@"ip"]; NSLog(@"setRemoteInfo:%@", _data); __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; strongSelf->remoteIp = [ip copy]; strongSelf->country = [isoCode copy]; [XSPhoneInfo sharedInstance].remoteIp = [NSString stringWithFormat:@"%@:%@", isoCode, ip]; [weakSelf showStatus:[XSPhoneInfo sharedInstance].remoteIp]; } withError:^(NSError *err) { NSLog(@"!setRemoteInfo err:%@",err); [weakSelf showStatus:[NSString stringWithFormat:@"%@", err.description]]; }]; } // 新增方法:检查并处理 append 文件 - (void)checkAppendFile { NSString *appendDirPath = @"/var/mobile/Documents/ad/append"; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir = NO; if ([fileManager fileExistsAtPath:appendDirPath isDirectory:&isDir] && isDir) { NSArray *files = [fileManager contentsOfDirectoryAtPath:appendDirPath error:nil]; if (files.count > 0) { BOOL allDeleted = YES; for (NSString *file in files) { NSString *filePath = [appendDirPath stringByAppendingPathComponent:file]; NSError *error = nil; [fileManager removeItemAtPath:filePath error:&error]; if (error) { NSLog(@"Failed to delete file: %@, error: %@", filePath, error); allDeleted = NO; } } if (allDeleted) { NSLog(@"append directory has files, deleted all, calling onEnd"); [self onEnd:nil]; } } } } - (BOOL)onEnd:(NSDictionary *)dic { @autoreleasepool { if (self->taskAppId) { [self appKill:self->taskAppId]; } [self startApp]; } return NO; } - (void)startApp { @autoreleasepool { if (!self->running) { return; } self->lastRun = [NSDate date]; NSDate *startTime = [NSDate date]; NSLog(@"getChangeInfo started at: %@", startTime); __weak typeof(self) weakSelf = self; getChangeInfo(self->_lastIdfa ?: @"", ^(NSDictionary *dic) { if (!dic) { NSLog(@"getChangeInfo returned nil dictionary"); return; } NSDate *endTime = [NSDate date]; NSTimeInterval duration = [endTime timeIntervalSinceDate:startTime]; NSLog(@"getChangeInfo completed at: %@, duration: %.2f seconds", endTime, duration); dispatch_async(weakSelf.manQueue, ^{ [weakSelf onChangeInfo:dic]; }); }, ^(NSError *err) { NSDate *endTime = [NSDate date]; NSTimeInterval duration = [endTime timeIntervalSinceDate:startTime]; NSLog(@"getChangeInfo completed at: %@, duration: %.2f seconds", endTime, duration); NSLog(@"XS- startTask NSError is null, %@", err); [weakSelf showStatus:[NSString stringWithFormat:@"getChangeInfo error after 5s restart: %@", err.description]]; // 延迟重新启动App dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), weakSelf.manQueue, ^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf && strongSelf->running) { // 确保任务未被停止 [strongSelf startApp]; } }); }); } } - (void)start { self->running = YES; myadTaskManualStop = NO; [[IPhoneHertbeat sharedInstance] setStatus:@"运行中"]; [self startApp]; __weak typeof(self) weakSelf = self; // 确保Timer在重新启动前被取消 if (_timer) { dispatch_source_cancel(_timer); _timer = nil; } // 使用 dispatch_source 创建timer _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.manQueue); dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), 10 * NSEC_PER_SEC, 1 * NSEC_PER_SEC); dispatch_source_set_event_handler(_timer, ^{ @autoreleasepool { [weakSelf proc]; // 检查心跳状态 if (![[IPhoneHertbeat sharedInstance] isHeartbeatActive]) { NSLog(@"Heartbeat inactive, restarting..."); [[IPhoneHertbeat sharedInstance] restartHeartbeat]; } } }); dispatch_resume(_timer); // 新增定时器,每秒检查 append 文件 if (_appendCheckTimer) { dispatch_source_cancel(_appendCheckTimer); _appendCheckTimer = nil; } _appendCheckTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.manQueue); dispatch_source_set_timer(_appendCheckTimer, dispatch_time(DISPATCH_TIME_NOW, 0), 1 * NSEC_PER_SEC, 0); dispatch_source_set_event_handler(_appendCheckTimer, ^{ [weakSelf checkAppendFile]; }); dispatch_resume(_appendCheckTimer); [[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(YES)]; } - (void)proc { if (!self->running) { return; } NSLog(@"XS- MyAdTaskManager proc"); NSDate* curRunTime = [NSDate date]; NSTimeInterval elapsed = [curRunTime timeIntervalSinceDate:self->lastRun]; BOOL washParam = [[XSPhoneConfig sharedInstance] WashParam]; if (elapsed >= 80) { //[self onStop]; [self startApp]; } if (!washParam) { NSString* appId = [ self frontMostAppId]; if (![appId isEqual: self->taskAppId]) { // 引入冷却时间,避免频繁切换应用 [self appKill:appId]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), self.manQueue, ^{ [self appRun:self->taskAppId]; self.lastAppSwitchTime = [NSDate timeIntervalSinceReferenceDate]; // 更新切换时间 }); } } } - (NSString *)toggle { if(self->running) { [self stop]; } else { [self start]; } return [IPhoneHertbeat sharedInstance].status; } - (void)stop { myadTaskManualStop = YES; self->running = NO; [IPhoneHertbeat sharedInstance].status = @"已停止"; if (_timer) { dispatch_source_cancel(_timer); _timer = nil; } // 停止 append 文件定时器 if (_appendCheckTimer) { dispatch_source_cancel(_appendCheckTimer); _appendCheckTimer = nil; } [[IPhoneHertbeat sharedInstance] stopHeartbeat]; // 确保心跳停止 [[MyEventBus sharedInstance] postEvent:@"UpdateRunStatus" withObject:@(NO)]; } - (NSString *)getSystemInfo { void *buffer = NULL; NSString *result = nil; // 设置 sysctl 的 mib 参数 // 0x100000006LL 拆分为两个 int: {6, 1} // 高32位是1 (0x1), 低32位是6 (0x6) int mib[2]; mib[0] = 6; // CTL_HW mib[1] = 1; // HW_MACHINE size_t size = 100; // 第一次调用获取实际需要的缓冲区大小 // #import sysctl(mib, 2, NULL, &size, NULL, 0); // 分配内存 buffer = malloc(size); if (buffer != NULL) { // 第二次调用获取实际数据 sysctl(mib, 2, buffer, &size, NULL, 0); // 转换为 NSString result = [NSString stringWithUTF8String:(const char *)buffer]; // 释放内存 free(buffer); } return result; } - (void)onChangeInfo:(NSDictionary *)dic { NSString* t_idfa = [self getStr:@"idfa" dic:dic]; if (![XSHelper strIsEmpty:t_idfa]) { self->_lastIdfa = [t_idfa copy]; } else { NSLog(@"XS- startTask t_idfa is null"); return; } NSString *_linkId = [self getStr:@"linkId" dic:dic]; if (![XSHelper strIsEmpty:_linkId]) { self->linkId = [_linkId copy]; } NSString *_dataId = [self getStr:@"dataId" dic:dic]; if (![XSHelper strIsEmpty:_dataId]) { self->dataId = [_dataId copy]; } NSString* t_taskId = [self getStr:@"id" dic:dic]; if (![XSHelper strIsEmpty:t_taskId]) { self->taskId = [t_taskId copy]; } else { NSLog(@"XS- startTask t_taskId is null"); return; } NSString* t_taskAppId = [self getStr:@"appId" dic:dic]; if (![XSHelper strIsEmpty:t_taskAppId]) { self->taskAppId = [t_taskAppId copy]; [IPhoneHertbeat sharedInstance].appId = self->taskAppId; } else { NSLog(@"XS- startTask t_taskAppId is null"); return; } __weak typeof(self) weakSelf = self; [self restart:^{ __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; void (^saveBlock)(void) = ^{ if (saveChangeDataFile(dic)) { // 成功,执行后续流程 strongSelf->running = YES; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), strongSelf.manQueue, ^{ __strong typeof(weakSelf) strongSelf2 = weakSelf; if (!strongSelf2) return; [strongSelf2 appRun:strongSelf2->taskAppId]; }); } else { NSLog(@"XS- saveChangeDataFile failed"); } }; saveBlock(); }]; } - (void)appKill:(NSString*)app { NSLog(@"XS- appKill"); XSKillAppByName(app); } - (void)unlock { NSLog(@"XS- unlock"); dispatch_async(dispatch_get_main_queue(), ^{ XSRemoteUnlock(); }); } - (void)resetApp:(NSString*)appId callback:(OnEndCallback)callback { NSLog(@"XS- JSApi resetApp"); NSString *appInfoPath = XSGetAppInfoPath(appId); NSDictionary *appInfo = XSGetAppInfo(appInfoPath); id bfaceDictKey = appInfo[@"bfaceDictKey"]; NSMutableDictionary *tempBfaceDictKey = nil; if(bfaceDictKey) { tempBfaceDictKey = [NSMutableDictionary dictionaryWithDictionary:bfaceDictKey]; } else { tempBfaceDictKey = [[NSMutableDictionary alloc] init]; } NSString *serverUrl = [[XSPhoneConfig sharedInstance] ServerURL]; BOOL washParam = [[XSPhoneConfig sharedInstance] WashParam]; [tempBfaceDictKey setValue:serverUrl forKey:@"adbrush_base_url"]; [tempBfaceDictKey setValue:@(washParam) forKey:@"washParam"]; // [tempBfaceDictKey setValue:lowEcpm forKey:@"adbrush_ecpm"]; if(self->linkId) { [tempBfaceDictKey setValue:self->linkId forKey:@"linkId"]; } else { NSString *linkId = [[NSUUID UUID] UUIDString]; [tempBfaceDictKey setValue:linkId forKey:@"linkId"]; } if(self->dataId) { [tempBfaceDictKey setValue:self->dataId forKey:@"dataId"]; } else { [tempBfaceDictKey setValue:@"0" forKey:@"dataId"]; } if(self->remoteIp) { [tempBfaceDictKey setValue:self->remoteIp forKey:@"remoteIp"]; } uint16_t udp_port = [XUDPServer sharedInstance].udp_port; [tempBfaceDictKey setValue:@(udp_port) forKey:@"udp_port"]; [tempBfaceDictKey setValue:[self getSystemInfo] forKey:@"device_model"]; NSLog(@"XS- resetApp 1"); NSMutableDictionary *tempAppInfo = [[NSMutableDictionary alloc] init]; [tempAppInfo setValue:tempBfaceDictKey forKey:@"bfaceDictKey"]; XSClearAll(appId); XSSaveAppInfo(tempAppInfo, appInfoPath); callback(); } - (NSString *)frontMostAppId { return XSFrontMostAppId(); } - (void)appRun:(NSString *)appId { if ([XSHelper strIsEmpty:appId]) return; dispatch_async(dispatch_get_main_queue(), ^{ @try { bringAppToForeground(appId); } @catch (NSException *exception) { NSLog(@"Error bringing app to foreground: %@", exception); } }); } - (void)restart:(OnEndCallback)callback { @synchronized (self) { __weak typeof(self) weakSelf = self; dispatch_async(self.manQueue, ^{ @try { __strong typeof(weakSelf) strongSelf = weakSelf; if (!strongSelf) return; [weakSelf appKill:@"com.apple.AppStore"]; [weakSelf appKill:@"com.apple.mobilesafari"]; if (strongSelf->taskAppId && ![strongSelf->taskAppId isEqual:[NSNull null]] && [strongSelf->taskAppId length] > 0) { [weakSelf appKill:strongSelf->taskAppId]; [weakSelf unlock]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), strongSelf.manQueue, ^{ [weakSelf unlock]; [weakSelf resetApp:strongSelf->taskAppId callback:callback]; }); } } @catch (NSException *exception) { NSLog(@"Error in restart: %@", exception); } }); } } - (void)rndTouchApp { int times = [XSHelper random:2 and:4]; } - (NSString*)getStr:(NSString *)key dic:(NSDictionary*)dic { NSLog(@"getStr: %@", key); id t_idfa = dic[key]; if (t_idfa && ![t_idfa isEqual:[NSNull null]] && ![t_idfa isEqual:@""]) { return [NSString stringWithFormat:@"%@", t_idfa]; } else { NSLog(@"XS- dic %@ is null", key); return @""; } } - (NSNumber*)getNum:(NSString *)key dic:(NSDictionary*)dic { NSLog(@"getNum: %@", key); id t_idfa = dic[key]; NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa]; NSNumber *ecpm = @0.0; if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) { ecpm = [XSHelper str2num:ecpm_str]; } return ecpm; } - (int)getInt:(NSString *)key dic:(NSDictionary*)dic { NSLog(@"getInt: %@", key); id t_idfa = dic[key]; NSString *ecpm_str = [NSString stringWithFormat:@"%@", t_idfa]; NSNumber *ecpm = @(-99); if (ecpm_str && ![ecpm_str isEqual:[NSNull null]] && ![ecpm_str isEqual:@""]) { ecpm = [XSHelper str2num:ecpm_str]; } return [ecpm intValue]; } - (void)dealloc { [self stop]; } @end