//
// TPSSAppContext.mm
// SurveillanceCore
//
// Created by ChenYongan on 6/13/16.
// Copyright © 2016 TP-LINK. All rights reserved.
//
#import "TPSSAppContext.h"
#import "TPSSNotification.h"
// FIXME
//#import "TPSSLocalizationConstants.h"
#include "IPCAppContext.h"
#include "tpwlog.h"
#include <set>
#include "TPECCommonMethods.h"
#import "TPSSAppContext+Private.h"
#import <TPFoundation/TPLocalizationUtils.h>
#define APPCONTEXT_LOG_TAG "TPSSAppContext:: "
static BasicToken getBasicToken(const char *pcToken) {
BasicToken token = BasicToken();
if (pcToken != NULL){
strlcpy(token.pcToken, [[TPECCommonMethods AES256DencryptWithTokenString:[NSString stringWithUTF8String: pcToken]] UTF8String], TPWCOMM_MAX_TOKEN_LENGTH);
}
return token;
}
TPBasicTokenCallMethod getBasicTokenMethod = { getBasicToken };
static struct tm LocalTime(long long time) {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:time];
NSDateComponents *dateComponents = [NSCalendar.currentCalendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond fromDate:date];
struct tm tm = { 0 };
tm.tm_year = (int)dateComponents.year - 1900;
tm.tm_mon = (int)dateComponents.month - 1;
tm.tm_mday = (int)dateComponents.day;
tm.tm_hour = (int)dateComponents.hour;
tm.tm_min = (int)dateComponents.minute;
tm.tm_sec = (int)dateComponents.second;
time_t t = mktime(&tm);
return *localtime(&t);
}
@interface TPSSAppContext ()
@property (nonatomic, assign, readwrite) BOOL didRequestLogout;
@property (nonatomic, assign, readwrite) TPAPPTargetType curTarget;
@end
@implementation TPSSAppContext
+ (instancetype)sharedContext {
static TPSSAppContext *appContext = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
appContext = [[TPSSAppContext alloc] init];
});
return appContext;
}
- (instancetype)init {
self = [super init];
if (self) {
pLocalTimeFunction = LocalTime;
pDecryptBasicsToken = getBasicTokenMethod.getBasicToken;
_pAppContext = (IPCAPPCONTEXT *)[self createLowLevelAppContext];
// 加载target type,加载对应target的target config
#ifdef APP_VIGI
self.curTarget = TPAPPTargetTypeSurveillanceHome;
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"targetConfig" ofType:@"plist"];
self.targetConfig = [[NSDictionary alloc] initWithContentsOfFile:bundlePath];
#else
self.curTarget = TPAPPTargetTypeOmadaSurveillance;
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:@"OmadaSurveillance-targetConfig" ofType:@"plist"];
self.targetConfig = [[NSDictionary alloc] initWithContentsOfFile:bundlePath];
#endif
_pAppContext->SetCurTarget((APPTargetType)self.curTarget);
_pAppContext->SetTimeDifference((int)NSTimeZone.localTimeZone.secondsFromGMT * 1000);
_pAppContext->RegisterEventFlingerCallback(EventCallback, ExitCallback, (__bridge void *)self);
//因要设置图片、视频存储路径,该path已通过fishhook修改为library path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *configPath = [paths objectAtIndex:0];
configPath = [configPath stringByAppendingPathComponent:@"AppConfig"];
if (![[NSFileManager defaultManager] fileExistsAtPath:configPath]) {
[[NSFileManager defaultManager] createDirectoryAtPath:configPath
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
_pAppContext->SetAppDataPath([configPath UTF8String]);
NSString *filePath = [paths objectAtIndex:0];
filePath = [filePath stringByAppendingPathComponent:@"AppFiles"];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
[[NSFileManager defaultManager] createDirectoryAtPath:filePath
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
_pAppContext->SetExternalDataPath([filePath UTF8String]);
#if !TARGET_OS_IOS
NSString *picturePath = [TPSSAppContext innerDownloadPath:TPGuardDownloadPathTypePicture context:_pAppContext];
NSString *videoPath = [TPSSAppContext innerDownloadPath:TPGuardDownloadPathTypeVideo context:_pAppContext];
_pAppContext->SetAlbumPath(picturePath.length > 0 ? picturePath.UTF8String : filePath.UTF8String, TP_LOCALALBUM_PATH_TYPE_PICTURE);
_pAppContext->SetAlbumPath(videoPath.length > 0 ? videoPath.UTF8String : filePath.UTF8String, TP_LOCALALBUM_PATH_TYPE_VIDEO);
#endif
NSString *cachePath = [paths objectAtIndex:0];
cachePath = [cachePath stringByAppendingPathComponent:@"AppCache"];
if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath]) {
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath
withIntermediateDirectories:NO
attributes:nil
error:nil];
}
_pAppContext->SetCachePath([cachePath UTF8String]);
#ifdef DEBUG
TPWLogInit();
TPWLogEnableModule(1, TPWLOG_MODULE_COMM);
TPWLogEnableModule(1, TPWLOG_MODULE_PLAYER);
TPWLogEnableModule(1, TPWLOG_MODULE_IPCAPP);
TPWLogEnableModule(0, TPWLOG_MODULE_NET_CLIENT);
TPWLogEnableModule(1, TPWLOG_MODULE_STATISTICS);
TPWLogSetLevel(TPWLOG_LEVEL_VERBOSE);
TPWLogEnableTimestamp(1);
TPWLogSetTimestampFormat(TPWLOG_TIMESTAMP_FORMAT_DATETIME_MIL);
#endif
self.didRequestLogout = NO;
[self setupPresetDSTMap];
[TPSSAppContext setPresetTimeZone:_pAppContext];
_currentSiteDevicelist = [NSArray array];
_localDeviceMac = [NSMutableSet set];
}
return self;
}
- (void)dealloc {
// wait unit stop finish, and then delete
_pAppContext->AppReqStop(&_uiRequestID, 1);
delete _pAppContext;
}
#pragma mark - low level context
- (void *)createLowLevelAppContext {
return new IPCAPPCONTEXT();
}
- (void *)lowLevelAppContext {
return _pAppContext;
}
- (BOOL)allowCelluar {
if (_pAppContext) {
return _pAppContext->GetWindowControllerAllowCelluar();
}
return NO;
}
#pragma mark - getter
- (TPSSAppContextStatus)status {
switch (_pAppContext->GetAppContextStatus()) {
case IPCAPP_STARTED:
return TPSSAppContextStatusStarted;
case IPCAPP_STOPPED:
return TPSSAppContextStatusStopped;
}
}
- (TPSSAppContextConfig)config
{
#ifdef BETA_EXPORT_SALE_CLOUD
return TPSSAppContextConfigTestBeta;
#endif
return TPSSAppContextConfigNormal;
}
- (NSArray <TPSSDeviceForDeviceList *> *)searchSiteList {
if (_searchSiteList == nil) {
_searchSiteList = [NSArray new];
}
return _searchSiteList;
}
#pragma mark - START/STOP
- (TPSSCode)start {
unsigned int uiRequestID;
int iRet = _pAppContext->AppReqStart(&uiRequestID, 0);
return REQUEST_RESULT(uiRequestID, iRet);
}
- (TPSSCode)syncStart {
unsigned int uiRequestID;
int iRet = _pAppContext->AppReqStart(&uiRequestID, 1);
return REQUEST_RESULT(uiRequestID, iRet);
}
- (TPSSCode)stop {
unsigned int uiRequestID;
int iRet = _pAppContext->AppReqStop(&uiRequestID, 0);
return REQUEST_RESULT(uiRequestID, iRet);
}
- (TPSSCode)syncStop {
unsigned int uiRequestID;
int iRet = _pAppContext->AppReqStop(&uiRequestID, 1);
return REQUEST_RESULT(uiRequestID, iRet);
}
- (void)setupPresetDSTMap {
static NSString *dstStartTimeKey = @"start_time";
static NSString *dstEndTimeKey = @"end_time";
static NSString *dstOffsetKey = @"dst_saving";
NSString *path = [[NSBundle mainBundle] pathForResource:@"daylight_saving" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:path];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:nil];
map<string, map<string, TPWDSTInfo>> *pDSTMap = _pAppContext->GetDSTMap();
for (NSString *dstName in dict.allKeys) {
NSDictionary *dstDict = dict[dstName];
map<string, TPWDSTInfo> DSTInfoMap;
for (NSString *year in dstDict.allKeys) {
NSDictionary *dstInfoDict = dstDict[year];
TPWDSTInfo DSTInfo = { 0 };
DSTInfo.llStartTime = [dstInfoDict[dstStartTimeKey] longLongValue];
DSTInfo.llEndTime = [dstInfoDict[dstEndTimeKey] longLongValue];
DSTInfo.iDSTOffset = [dstInfoDict[dstOffsetKey] intValue];
DSTInfoMap[year.UTF8String] = DSTInfo;
}
(*pDSTMap)[dstName.UTF8String] = DSTInfoMap;
}
}
#pragma mark - Notification handler
static void EventCallback(int iQueueID, TPMESSAGE * pMessage, void * pArgs)
{
@autoreleasepool {
TPSSAppContext *ac = (__bridge TPSSAppContext *)pArgs;
[ac _handleEventWithQueueId:iQueueID andMessage:pMessage];
}
}
- (void)_handleEventWithQueueId:(int)iQueueId andMessage:(TPMESSAGE *)pMessage {
NSNotification *notification;
TPSSEventType eventType;
if (TPSequenceNumberGetPrefix(pMessage->iID) == IPC_BROADCAST_SEQ_PREFIX) {
eventType = TPSSEventTypeBroadcast;
} else {
eventType = TPSSEventTypeResponse;
}
notification = [NSNotification notificationWithMessage:pMessage
eventType:eventType];
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotification:notification];
});
}
static void ExitCallback(void * pArgs)
{
@autoreleasepool {
TPSSAppContext *ac = (__bridge TPSSAppContext *)pArgs;
[ac _handleExit];
}
}
- (void)_handleExit {
}
#pragma mark - network
- (void)setNetworkType:(NSInteger)networkType provider:(NSString *)provider {
_pAppContext->SetNetworkType((int)networkType, [provider UTF8String]);
}
- (void)connectivityChanged {
_pAppContext->AppConnectivityChanged();
}
- (void)pipeManagerOptimize {
_pAppContext->GetNetworkPipeManager()->Optimize();
_pAppContext->GetNetworkPipeManager()->ReconnectAllPreconn();
}
- (void)pipeManagerChangeMaxPunchingNum:(NSInteger)playWindowNum deviceIDArray:(NSArray<NSNumber *> *)deviceIDArray {
//后续可根据需求进行定制
if (!deviceIDArray || playWindowNum == 0)
{
return;
}
std::set<long long> *llDeviceIDSet = new std::set<long long>();
for (int i = 0; i < deviceIDArray.count; i++)
{
llDeviceIDSet->insert([deviceIDArray[i] longLongValue]);
}
_pAppContext->GetNetworkPipeManager()->ChangeMaxPunchingNum((int)playWindowNum, llDeviceIDSet);
}
- (void)resetDeviceLocalValid {
_pAppContext->AppResetDeviceLocalValid();
}
#pragma mark - error message
- (NSString *)legacyErrorMessageForIndex:(SInt32)errorIndex {
char pcMessage[IPC_ERROR_MSG_STASH_ENTRY_BUFF_SIZE] = { 0 };
_pAppContext->GetErrorMessage(pcMessage, NULL, NULL, (int)errorIndex);
NSString *errorMessage = [NSString stringWithUTF8String:pcMessage];
return [TPSSAppContext errorMsgForLocalizedKey:errorMessage];
}
- (TPSSError *)legacyErrorForIndex:(SInt32)errorIndex {
int iRval = 0;
int iErrorCode = 0;
int iCode = 0;
char pcMessage[IPC_ERROR_MSG_STASH_ENTRY_BUFF_SIZE] = { 0 };
_pAppContext->GetErrorMessage(pcMessage, &iErrorCode, &iRval, (int)errorIndex);
if (iErrorCode != IPC_EC_RVAL) {
iCode = iErrorCode;
} else {
iCode = iRval;
}
return [TPSSError errorWithCode:iCode andMessage:[NSString stringWithUTF8String:pcMessage]];
}
+ (NSString *)errorMsgForLocalizedKey:(NSString *)localizedKey
{
NSString *localizedString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"IPCAppStringResourceDefines"];
if (![localizedString isEqualToString:localizedKey])
{
return localizedString;
}
NSString *localizedErrorString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"commonErrormsg"];
if (![localizedErrorString isEqualToString:localizedKey])
{
return localizedErrorString;
}
else if ([localizedErrorString isEqualToString:localizedKey])
{
NSString *diffLocalizedString = [TPLocalizationUtils localizedStringForKey:localizedKey andTableName:@"Localizable_diff"];
if (![diffLocalizedString isEqualToString:localizedKey])
{
return diffLocalizedString;
}
}
return localizedKey;
}
#pragma mark - task
- (TPSSCode)cancelTask:(TPSSCode)requestID {
int iRet = _pAppContext->AppCancelTask(requestID);
return iRet;
}
- (TPSSTaskInfo *)taskInfoByID:(NSUInteger)requestID {
TASKINFO *pTaskinfo = new TASKINFO;
_pAppContext->GetTaskInfo((unsigned int)requestID, pTaskinfo);
TPSSTaskInfo *taskinfo = [[TPSSTaskInfo alloc] initWithTaskinfo:pTaskinfo];
delete pTaskinfo;
return taskinfo;
}
#pragma mark - tool method
- (void)updateDidRequestLogout:(BOOL)didRequestLogout
{
self.didRequestLogout = didRequestLogout;
}
#pragma mark - device type
//桥接层的DeviceType值转化成C层的DeviceType值
- (int)determainDeviceType:(TPSSDeviceType)deviceType {
int deviceTypeC;
switch (deviceType) {
case TPSSDeviceTypeIPC:
deviceTypeC = TPW_DEVICE_TYPE_IPC;
break;
case TPSSDeviceTypeNVR:
deviceTypeC = TPW_DEVICE_TYPE_NVR;
break;
case TPSSDeviceTypeSolar:
deviceTypeC = TPW_DEVICE_TYPE_SOLAR;
break;
default:
deviceTypeC = TPW_DEVICE_TYPE_IPC;
break;
}
return deviceTypeC;
}
//桥接层的DeviceSubType值转化成C层的DeviceSubType值
- (int)determainDeviceSubType:(TPSSDeviceSubType)subType {
int deviceSubType;
switch (subType) {
case TPSSDeviceSubTypeNVR:
deviceSubType = TPW_DEVICE_TYPE_NVR;
break;
case TPSSDeviceSubTypeCameraDisplay:
deviceSubType = TPW_DEVICE_TYPE_CAMERA_DISPLAY;
break;
case TPSSDeviceSubTypeDoorBellCamera:
deviceSubType = TPW_DEVICE_TYPE_DOORBELL_CAMERA;
break;
case TPSSDeviceSubTypeSolar:
deviceSubType = TPW_DEVICE_TYPE_SOLAR;
break;
default:
deviceSubType = TPW_DEVICE_TYPE_IPC;
break;
}
return deviceSubType;
}
+ (NSString *)downloadPathKey:(TPGuardDownloadPathType)type {
return [NSString stringWithFormat:@"download_path_%@", @(type)];
}
+ (void)setDownloadPath:(NSString *)path type:(TPGuardDownloadPathType)type {
if (path.length == 0 || type == TPGuardDownloadPathTypeCount) {
return;
}
NSURL *fileURL = [NSURL fileURLWithPath:path];
if (fileURL) {
NSData *bookmarkData = [self createBookmarkForURL:fileURL];
[[NSUserDefaults standardUserDefaults] setObject:bookmarkData forKey:[self downloadPathKey:type]];
} else {
return;
}
auto &context = [TPSSAppContext sharedContext]->_pAppContext;
if (type == TPGuardDownloadPathTypePicture) {
context->SetAlbumPath(path.UTF8String, TP_LOCALALBUM_PATH_TYPE_PICTURE);
} else if (type == TPGuardDownloadPathTypeVideo) {
context->SetAlbumPath(path.UTF8String, TP_LOCALALBUM_PATH_TYPE_VIDEO);
}
}
+ (NSString *)innerDownloadPath:(TPGuardDownloadPathType)type context:(IPCAPPCONTEXT *)pContext {
NSString *path = [[NSUserDefaults standardUserDefaults] valueForKey:[self downloadPathKey:type]];
NSData *boomarkData = [[NSUserDefaults standardUserDefaults] dataForKey:[self downloadPathKey:type]];
if (boomarkData) {
NSURL *fileURL = [self resolveBookmarkData:boomarkData];
if (fileURL.path.length > 0) {
return fileURL.path;
}
}
if (path.length == 0 && pContext != NULL) {
path = [NSString stringWithUTF8String: pContext->GetExternalDataPath()];
}
return path.length > 0 ? path : @"";
}
+ (NSString *)downloadPath:(TPGuardDownloadPathType)type {
return [self innerDownloadPath:type context:[TPSSAppContext sharedContext]->_pAppContext];
}
+ (void)setPresetTimeZone:(IPCAPPCONTEXT *)pContext {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"timezone" ofType:@"json"];
NSData *data = [NSData dataWithContentsOfFile:filePath];
if (!data || pContext == NULL) {
return;
}
NSError *error;
NSDictionary *jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
NSArray *timeZones = jsonObject[@"timezones"];
if (![timeZones isKindOfClass:[NSArray class]]) {
return;
}
map<string, TPWTimeZoneInfo> *map = pContext->GetTimeZoneMap();
for (NSDictionary *item in timeZones) {
if (![item isKindOfClass:[NSDictionary class]]) {
continue;
}
NSString *name = item[@"name"];
NSString *zone = item[@"timezone"];
int offset = [item[@"offset"] intValue];
if (name.length == 0 || zone.length == 0) {
continue;
}
TPWTimeZoneInfo info = TPWTimeZoneInfo();
info.iTimeOffset = offset / 1000;
strlcpy(info.pcTimezone, name.cString, MIN(name.cStringLength, TPW_URL_MAX_LENGTH));
strlcpy(info.pcZoneId, zone.cString, MIN(zone.cStringLength, TPW_URL_MAX_LENGTH));
map->insert(make_pair(zone.UTF8String, info));
}
}
+ (NSURL *)resolveBookmarkData:(NSData *)data {
NSError *error;
BOOL isStale = NO;
NSURL *url = [NSURL URLByResolvingBookmarkData:data
options:NSURLBookmarkResolutionWithSecurityScope
relativeToURL:nil
bookmarkDataIsStale:&isStale
error:&error];
if (error) {
NSLog(@"解析书签失败: %@", error);
return nil;
}
if (isStale) {
NSLog(@"书签已过期,重新生成");
NSData *newBookmark = [self createBookmarkForURL:url];
if (newBookmark) {
[[NSUserDefaults standardUserDefaults] setObject:newBookmark forKey:@"savedBookmark"];
}
}
if ([url startAccessingSecurityScopedResource]) {
return url;
}
return nil;
}
+ (nullable NSData *)createBookmarkForURL:(NSURL *)url {
NSError *error;
NSData *bookmarkData = [url bookmarkDataWithOptions:NSURLBookmarkCreationWithSecurityScope
includingResourceValuesForKeys:nil
relativeToURL:nil
error:&error];
if (error) {
NSLog(@"创建书签失败: %@", error);
}
return bookmarkData;
}
@end
TPSSAppContext类在这里了