2019-05-20 17:12:13 -07:00
|
|
|
/*
|
|
|
|
|
* Copyright 2019 Google
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
2020-10-05 14:00:49 -04:00
|
|
|
#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORPlatform.h"
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2020-06-17 09:42:18 -07:00
|
|
|
#import <sys/sysctl.h>
|
|
|
|
|
|
2020-10-01 20:56:26 -04:00
|
|
|
#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORAssert.h"
|
2020-10-05 14:00:49 -04:00
|
|
|
#import "GoogleDataTransport/GDTCORLibrary/Internal/GDTCORReachability.h"
|
2020-07-31 08:12:23 -07:00
|
|
|
#import "GoogleDataTransport/GDTCORLibrary/Public/GoogleDataTransport/GDTCORConsoleLogger.h"
|
2019-11-21 14:55:12 -08:00
|
|
|
|
2020-06-24 17:50:15 -07:00
|
|
|
#import "GoogleDataTransport/GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
|
2019-11-15 01:30:19 -08:00
|
|
|
|
|
|
|
|
#ifdef GDTCOR_VERSION
|
|
|
|
|
#define STR(x) STR_EXPAND(x)
|
|
|
|
|
#define STR_EXPAND(x) #x
|
|
|
|
|
NSString *const kGDTCORVersion = @STR(GDTCOR_VERSION);
|
|
|
|
|
#else
|
|
|
|
|
NSString *const kGDTCORVersion = @"Unknown";
|
|
|
|
|
#endif // GDTCOR_VERSION
|
2019-09-03 20:37:16 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
const GDTCORBackgroundIdentifier GDTCORBackgroundIdentifierInvalid = 0;
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
NSString *const kGDTCORApplicationDidEnterBackgroundNotification =
|
|
|
|
|
@"GDTCORApplicationDidEnterBackgroundNotification";
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
NSString *const kGDTCORApplicationWillEnterForegroundNotification =
|
|
|
|
|
@"GDTCORApplicationWillEnterForegroundNotification";
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
NSString *const kGDTCORApplicationWillTerminateNotification =
|
|
|
|
|
@"GDTCORApplicationWillTerminateNotification";
|
2020-03-18 08:09:18 -07:00
|
|
|
|
|
|
|
|
NSURL *GDTCORRootDirectory(void) {
|
|
|
|
|
static NSURL *GDTPath;
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
NSString *cachePath =
|
|
|
|
|
NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
|
|
|
|
|
GDTPath =
|
|
|
|
|
[NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/google-sdks-events", cachePath]];
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"GDT's state will be saved to: %@", GDTPath);
|
2020-03-18 08:09:18 -07:00
|
|
|
});
|
2020-06-23 16:04:18 -07:00
|
|
|
NSError *error;
|
|
|
|
|
[[NSFileManager defaultManager] createDirectoryAtPath:GDTPath.path
|
|
|
|
|
withIntermediateDirectories:YES
|
|
|
|
|
attributes:nil
|
|
|
|
|
error:&error];
|
|
|
|
|
GDTCORAssert(error == nil, @"There was an error creating GDT's path");
|
2020-03-18 08:09:18 -07:00
|
|
|
return GDTPath;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
BOOL GDTCORReachabilityFlagsReachable(GDTCORNetworkReachabilityFlags flags) {
|
2019-12-23 16:45:19 -08:00
|
|
|
#if !TARGET_OS_WATCH
|
2020-04-24 15:19:43 -07:00
|
|
|
BOOL reachable =
|
|
|
|
|
(flags & kSCNetworkReachabilityFlagsReachable) == kSCNetworkReachabilityFlagsReachable;
|
|
|
|
|
BOOL connectionRequired = (flags & kSCNetworkReachabilityFlagsConnectionRequired) ==
|
|
|
|
|
kSCNetworkReachabilityFlagsConnectionRequired;
|
|
|
|
|
return reachable && !connectionRequired;
|
|
|
|
|
#else
|
|
|
|
|
return (flags & kGDTCORNetworkReachabilityFlagsReachable) ==
|
|
|
|
|
kGDTCORNetworkReachabilityFlagsReachable;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL GDTCORReachabilityFlagsContainWWAN(GDTCORNetworkReachabilityFlags flags) {
|
2019-05-20 17:12:13 -07:00
|
|
|
#if TARGET_OS_IOS
|
|
|
|
|
return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
|
|
|
|
|
#else
|
2020-04-24 15:19:43 -07:00
|
|
|
// Assume network connection not WWAN on macOS, tvOS, watchOS.
|
2019-05-20 17:12:13 -07:00
|
|
|
return NO;
|
2019-05-23 12:31:59 -07:00
|
|
|
#endif // TARGET_OS_IOS
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
2020-02-25 11:14:41 -08:00
|
|
|
GDTCORNetworkType GDTCORNetworkTypeMessage() {
|
|
|
|
|
#if !TARGET_OS_WATCH
|
|
|
|
|
SCNetworkReachabilityFlags reachabilityFlags = [GDTCORReachability currentFlags];
|
|
|
|
|
if ((reachabilityFlags & kSCNetworkReachabilityFlagsReachable) ==
|
|
|
|
|
kSCNetworkReachabilityFlagsReachable) {
|
|
|
|
|
if (GDTCORReachabilityFlagsContainWWAN(reachabilityFlags)) {
|
|
|
|
|
return GDTCORNetworkTypeMobile;
|
|
|
|
|
} else {
|
|
|
|
|
return GDTCORNetworkTypeWIFI;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
return GDTCORNetworkTypeUNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-21 15:48:50 -08:00
|
|
|
GDTCORNetworkMobileSubtype GDTCORNetworkMobileSubTypeMessage() {
|
|
|
|
|
#if TARGET_OS_IOS
|
|
|
|
|
static NSDictionary<NSString *, NSNumber *> *CTRadioAccessTechnologyToNetworkSubTypeMessage;
|
|
|
|
|
static CTTelephonyNetworkInfo *networkInfo;
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
CTRadioAccessTechnologyToNetworkSubTypeMessage = @{
|
|
|
|
|
CTRadioAccessTechnologyGPRS : @(GDTCORNetworkMobileSubtypeGPRS),
|
|
|
|
|
CTRadioAccessTechnologyEdge : @(GDTCORNetworkMobileSubtypeEdge),
|
|
|
|
|
CTRadioAccessTechnologyWCDMA : @(GDTCORNetworkMobileSubtypeWCDMA),
|
|
|
|
|
CTRadioAccessTechnologyHSDPA : @(GDTCORNetworkMobileSubtypeHSDPA),
|
|
|
|
|
CTRadioAccessTechnologyHSUPA : @(GDTCORNetworkMobileSubtypeHSUPA),
|
|
|
|
|
CTRadioAccessTechnologyCDMA1x : @(GDTCORNetworkMobileSubtypeCDMA1x),
|
|
|
|
|
CTRadioAccessTechnologyCDMAEVDORev0 : @(GDTCORNetworkMobileSubtypeCDMAEVDORev0),
|
|
|
|
|
CTRadioAccessTechnologyCDMAEVDORevA : @(GDTCORNetworkMobileSubtypeCDMAEVDORevA),
|
|
|
|
|
CTRadioAccessTechnologyCDMAEVDORevB : @(GDTCORNetworkMobileSubtypeCDMAEVDORevB),
|
|
|
|
|
CTRadioAccessTechnologyeHRPD : @(GDTCORNetworkMobileSubtypeHRPD),
|
|
|
|
|
CTRadioAccessTechnologyLTE : @(GDTCORNetworkMobileSubtypeLTE),
|
|
|
|
|
};
|
|
|
|
|
networkInfo = [[CTTelephonyNetworkInfo alloc] init];
|
|
|
|
|
});
|
2020-02-24 11:42:37 -08:00
|
|
|
NSString *networkCurrentRadioAccessTechnology;
|
2020-02-25 11:14:41 -08:00
|
|
|
#if TARGET_OS_MACCATALYST
|
|
|
|
|
NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict =
|
|
|
|
|
networkInfo.serviceCurrentRadioAccessTechnology;
|
|
|
|
|
if (networkCurrentRadioAccessTechnologyDict.count) {
|
|
|
|
|
networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0];
|
|
|
|
|
}
|
2020-10-08 15:11:00 -04:00
|
|
|
#else // TARGET_OS_MACCATALYST
|
2020-02-25 11:14:41 -08:00
|
|
|
if (@available(iOS 12.0, *)) {
|
2020-02-24 11:42:37 -08:00
|
|
|
NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict =
|
|
|
|
|
networkInfo.serviceCurrentRadioAccessTechnology;
|
|
|
|
|
if (networkCurrentRadioAccessTechnologyDict.count) {
|
|
|
|
|
// In iOS 12, multiple radio technologies can be captured. We prefer not particular radio
|
|
|
|
|
// tech to another, so we'll just return the first value in the dictionary.
|
|
|
|
|
networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-10-08 15:11:00 -04:00
|
|
|
#if TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED < 120000
|
2020-09-17 15:31:44 -04:00
|
|
|
networkCurrentRadioAccessTechnology = networkInfo.currentRadioAccessTechnology;
|
2020-10-08 15:11:00 -04:00
|
|
|
#endif // TARGET_OS_IOS && __IPHONE_OS_VERSION_MIN_REQUIRED < 120000
|
2020-02-24 11:42:37 -08:00
|
|
|
}
|
2020-03-17 19:06:05 -07:00
|
|
|
#endif // TARGET_OS_MACCATALYST
|
2020-02-21 15:48:50 -08:00
|
|
|
if (networkCurrentRadioAccessTechnology) {
|
|
|
|
|
NSNumber *networkMobileSubtype =
|
|
|
|
|
CTRadioAccessTechnologyToNetworkSubTypeMessage[networkCurrentRadioAccessTechnology];
|
|
|
|
|
return networkMobileSubtype.intValue;
|
|
|
|
|
} else {
|
|
|
|
|
return GDTCORNetworkMobileSubtypeUNKNOWN;
|
|
|
|
|
}
|
2020-11-05 12:58:48 -05:00
|
|
|
#else // TARGET_OS_IOS
|
2020-02-21 15:48:50 -08:00
|
|
|
return GDTCORNetworkMobileSubtypeUNKNOWN;
|
2020-10-08 15:11:00 -04:00
|
|
|
#endif // TARGET_OS_IOS
|
2020-02-21 15:48:50 -08:00
|
|
|
}
|
|
|
|
|
|
2020-06-17 09:42:18 -07:00
|
|
|
NSString *_Nonnull GDTCORDeviceModel() {
|
2020-06-18 10:07:18 -07:00
|
|
|
static NSString *deviceModel = @"Unknown";
|
2020-06-17 09:42:18 -07:00
|
|
|
|
|
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
size_t size;
|
|
|
|
|
char *keyToExtract = "hw.machine";
|
|
|
|
|
sysctlbyname(keyToExtract, NULL, &size, NULL, 0);
|
|
|
|
|
if (size > 0) {
|
|
|
|
|
char *machine = calloc(1, size);
|
|
|
|
|
sysctlbyname(keyToExtract, machine, &size, NULL, 0);
|
|
|
|
|
deviceModel = [NSString stringWithCString:machine encoding:NSUTF8StringEncoding];
|
|
|
|
|
free(machine);
|
|
|
|
|
} else {
|
|
|
|
|
deviceModel = [UIDevice currentDevice].model;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return deviceModel;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 19:06:05 -07:00
|
|
|
NSData *_Nullable GDTCOREncodeArchive(id<NSSecureCoding> obj,
|
2020-08-13 11:18:40 -04:00
|
|
|
NSString *filePath,
|
2020-03-17 19:06:05 -07:00
|
|
|
NSError *_Nullable *error) {
|
2020-06-23 16:04:18 -07:00
|
|
|
BOOL result = NO;
|
2020-08-13 11:18:40 -04:00
|
|
|
if (filePath.length > 0) {
|
2020-06-23 16:04:18 -07:00
|
|
|
result = [[NSFileManager defaultManager]
|
2020-08-13 11:18:40 -04:00
|
|
|
createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
2020-06-23 16:04:18 -07:00
|
|
|
withIntermediateDirectories:YES
|
|
|
|
|
attributes:nil
|
|
|
|
|
error:error];
|
|
|
|
|
if (result == NO || *error) {
|
2020-08-13 11:18:40 -04:00
|
|
|
GDTCORLogDebug(@"Attempt to create directory failed: path:%@ error:%@", filePath, *error);
|
2020-06-23 16:04:18 -07:00
|
|
|
return nil;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-17 19:06:05 -07:00
|
|
|
NSData *resultData;
|
|
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4, *)) {
|
|
|
|
|
resultData = [NSKeyedArchiver archivedDataWithRootObject:obj
|
|
|
|
|
requiringSecureCoding:YES
|
|
|
|
|
error:error];
|
2020-06-23 16:04:18 -07:00
|
|
|
if (resultData == nil || (error != NULL && *error != nil)) {
|
2020-03-18 08:09:18 -07:00
|
|
|
GDTCORLogDebug(@"Encoding an object failed: %@", *error);
|
|
|
|
|
return nil;
|
|
|
|
|
}
|
2020-08-13 11:18:40 -04:00
|
|
|
if (filePath.length > 0) {
|
|
|
|
|
result = [resultData writeToFile:filePath options:NSDataWritingAtomic error:error];
|
2020-03-18 08:09:18 -07:00
|
|
|
if (result == NO || *error) {
|
2020-08-13 11:18:40 -04:00
|
|
|
GDTCORLogDebug(@"Attempt to write archive failed: path:%@ error:%@", filePath, *error);
|
2020-03-18 08:09:18 -07:00
|
|
|
} else {
|
2020-08-13 11:18:40 -04:00
|
|
|
GDTCORLogDebug(@"Writing archive succeeded: %@", filePath);
|
2020-03-18 08:09:18 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-03-17 19:06:05 -07:00
|
|
|
} else {
|
|
|
|
|
@try {
|
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
|
resultData = [NSKeyedArchiver archivedDataWithRootObject:obj];
|
|
|
|
|
#pragma clang diagnostic pop
|
2020-08-13 11:18:40 -04:00
|
|
|
if (filePath.length > 0) {
|
|
|
|
|
result = [resultData writeToFile:filePath options:NSDataWritingAtomic error:error];
|
2020-03-18 08:09:18 -07:00
|
|
|
if (result == NO || *error) {
|
2020-08-13 11:18:40 -04:00
|
|
|
GDTCORLogDebug(@"Attempt to write archive failed: URL:%@ error:%@", filePath, *error);
|
2020-03-18 08:09:18 -07:00
|
|
|
} else {
|
2020-08-13 11:18:40 -04:00
|
|
|
GDTCORLogDebug(@"Writing archive succeeded: %@", filePath);
|
2020-03-18 08:09:18 -07:00
|
|
|
}
|
|
|
|
|
}
|
2020-03-17 19:06:05 -07:00
|
|
|
} @catch (NSException *exception) {
|
|
|
|
|
NSString *errorString =
|
|
|
|
|
[NSString stringWithFormat:@"An exception was thrown during encoding: %@", exception];
|
|
|
|
|
*error = [NSError errorWithDomain:NSCocoaErrorDomain
|
|
|
|
|
code:-1
|
|
|
|
|
userInfo:@{NSLocalizedFailureReasonErrorKey : errorString}];
|
|
|
|
|
}
|
2020-03-18 08:09:18 -07:00
|
|
|
GDTCORLogDebug(@"Attempt to write archive. successful:%@ URL:%@ error:%@",
|
2020-08-13 11:18:40 -04:00
|
|
|
result ? @"YES" : @"NO", filePath, *error);
|
2020-03-17 19:06:05 -07:00
|
|
|
}
|
|
|
|
|
return resultData;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
id<NSSecureCoding> _Nullable GDTCORDecodeArchive(Class archiveClass,
|
|
|
|
|
NSString *_Nullable archivePath,
|
|
|
|
|
NSData *_Nullable archiveData,
|
|
|
|
|
NSError *_Nullable *error) {
|
|
|
|
|
id<NSSecureCoding> unarchivedObject = nil;
|
|
|
|
|
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, watchOS 4, *)) {
|
|
|
|
|
NSData *data = archiveData ? archiveData : [NSData dataWithContentsOfFile:archivePath];
|
|
|
|
|
if (data) {
|
|
|
|
|
unarchivedObject = [NSKeyedUnarchiver unarchivedObjectOfClass:archiveClass
|
|
|
|
|
fromData:data
|
|
|
|
|
error:error];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
@try {
|
|
|
|
|
NSData *archivedData =
|
|
|
|
|
archiveData ? archiveData : [NSData dataWithContentsOfFile:archivePath];
|
|
|
|
|
#pragma clang diagnostic push
|
|
|
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
|
unarchivedObject = [NSKeyedUnarchiver unarchiveObjectWithData:archivedData];
|
|
|
|
|
#pragma clang diagnostic pop
|
|
|
|
|
} @catch (NSException *exception) {
|
|
|
|
|
NSString *errorString =
|
|
|
|
|
[NSString stringWithFormat:@"An exception was thrown during encoding: %@", exception];
|
|
|
|
|
*error = [NSError errorWithDomain:NSCocoaErrorDomain
|
|
|
|
|
code:-1
|
|
|
|
|
userInfo:@{NSLocalizedFailureReasonErrorKey : errorString}];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return unarchivedObject;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-11 10:42:30 -04:00
|
|
|
BOOL GDTCORWriteDataToFile(NSData *data, NSString *filePath, NSError *_Nullable *outError) {
|
|
|
|
|
BOOL result = NO;
|
|
|
|
|
if (filePath.length > 0) {
|
|
|
|
|
result = [[NSFileManager defaultManager]
|
|
|
|
|
createDirectoryAtPath:[filePath stringByDeletingLastPathComponent]
|
|
|
|
|
withIntermediateDirectories:YES
|
|
|
|
|
attributes:nil
|
|
|
|
|
error:outError];
|
|
|
|
|
if (result == NO || *outError) {
|
|
|
|
|
GDTCORLogDebug(@"Attempt to create directory failed: path:%@ error:%@", filePath, *outError);
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (filePath.length > 0) {
|
|
|
|
|
result = [data writeToFile:filePath options:NSDataWritingAtomic error:outError];
|
|
|
|
|
if (result == NO || *outError) {
|
|
|
|
|
GDTCORLogDebug(@"Attempt to write archive failed: path:%@ error:%@", filePath, *outError);
|
|
|
|
|
} else {
|
|
|
|
|
GDTCORLogDebug(@"Writing archive succeeded: %@", filePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-02 15:10:28 -04:00
|
|
|
@interface GDTCORApplication ()
|
|
|
|
|
/**
|
|
|
|
|
Private flag to match the existing `readonly` public flag. This will be accurate for all platforms,
|
|
|
|
|
since we handle each platform's lifecycle notifications separately.
|
|
|
|
|
*/
|
|
|
|
|
@property(atomic, readwrite) BOOL isRunningInBackground;
|
|
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
@implementation GDTCORApplication
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
#if TARGET_OS_WATCH
|
|
|
|
|
/** A dispatch queue on which all task semaphores will populate and remove from
|
|
|
|
|
* gBackgroundIdentifierToSemaphoreMap.
|
|
|
|
|
*/
|
|
|
|
|
static dispatch_queue_t gSemaphoreQueue;
|
|
|
|
|
|
|
|
|
|
/** For mapping backgroundIdentifier to task semaphore. */
|
|
|
|
|
static NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *gBackgroundIdentifierToSemaphoreMap;
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-05-20 17:12:13 -07:00
|
|
|
+ (void)load {
|
2020-01-20 14:50:44 -08:00
|
|
|
GDTCORLogDebug(
|
2020-04-14 12:30:29 -07:00
|
|
|
@"%@", @"GDT is initializing. Please note that if you quit the app via the "
|
|
|
|
|
"debugger and not through a lifecycle event, event data will remain on disk but "
|
|
|
|
|
"storage won't have a reference to them since the singleton wasn't saved to disk.");
|
2019-07-18 09:54:06 -07:00
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
2019-08-08 11:34:52 -07:00
|
|
|
// If this asserts, please file a bug at https://github.com/firebase/firebase-ios-sdk/issues.
|
2019-09-09 15:21:29 -07:00
|
|
|
GDTCORFatalAssert(
|
|
|
|
|
GDTCORBackgroundIdentifierInvalid == UIBackgroundTaskInvalid,
|
|
|
|
|
@"GDTCORBackgroundIdentifierInvalid and UIBackgroundTaskInvalid should be the same.");
|
2019-05-20 17:12:13 -07:00
|
|
|
#endif
|
|
|
|
|
[self sharedApplication];
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
+ (void)initialize {
|
|
|
|
|
#if TARGET_OS_WATCH
|
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
|
gSemaphoreQueue = dispatch_queue_create("com.google.GDTCORApplication", DISPATCH_QUEUE_SERIAL);
|
|
|
|
|
GDTCORLogDebug(
|
|
|
|
|
@"%@",
|
|
|
|
|
@"GDTCORApplication is initializing on watchOS, gSemaphoreQueue has been initialized.");
|
|
|
|
|
gBackgroundIdentifierToSemaphoreMap = [[NSMutableDictionary alloc] init];
|
|
|
|
|
GDTCORLogDebug(@"%@", @"GDTCORApplication is initializing on watchOS, "
|
|
|
|
|
@"gBackgroundIdentifierToSemaphoreMap has been initialized.");
|
|
|
|
|
});
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
+ (nullable GDTCORApplication *)sharedApplication {
|
|
|
|
|
static GDTCORApplication *application;
|
2019-05-20 17:12:13 -07:00
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
|
dispatch_once(&onceToken, ^{
|
2019-09-09 15:21:29 -07:00
|
|
|
application = [[GDTCORApplication alloc] init];
|
2019-05-20 17:12:13 -07:00
|
|
|
});
|
|
|
|
|
return application;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (instancetype)init {
|
|
|
|
|
self = [super init];
|
|
|
|
|
if (self) {
|
2019-10-02 15:10:28 -04:00
|
|
|
// This class will be instantiated in the foreground.
|
|
|
|
|
_isRunningInBackground = NO;
|
|
|
|
|
|
2019-07-18 09:54:06 -07:00
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
2019-05-20 17:12:13 -07:00
|
|
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationDidEnterBackground:)
|
|
|
|
|
name:UIApplicationDidEnterBackgroundNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationWillEnterForeground:)
|
|
|
|
|
name:UIApplicationWillEnterForegroundNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
|
|
|
|
|
NSString *name = UIApplicationWillTerminateNotification;
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationWillTerminate:)
|
|
|
|
|
name:name
|
|
|
|
|
object:nil];
|
2019-08-07 19:19:16 -07:00
|
|
|
|
|
|
|
|
#if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
2019-08-12 06:58:33 -07:00
|
|
|
if (@available(iOS 13, tvOS 13.0, *)) {
|
2019-08-07 19:19:16 -07:00
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationWillEnterForeground:)
|
|
|
|
|
name:UISceneWillEnterForegroundNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationDidEnterBackground:)
|
|
|
|
|
name:UISceneWillDeactivateNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
}
|
|
|
|
|
#endif // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
|
|
|
|
|
2019-05-20 17:12:13 -07:00
|
|
|
#elif TARGET_OS_OSX
|
|
|
|
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(macOSApplicationWillTerminate:)
|
|
|
|
|
name:NSApplicationWillTerminateNotification
|
|
|
|
|
object:nil];
|
2020-04-24 15:19:43 -07:00
|
|
|
|
|
|
|
|
#elif TARGET_OS_WATCH
|
|
|
|
|
// TODO: Notification on watchOS platform is currently posted by strings which are frangible.
|
|
|
|
|
// TODO: Needs improvements here.
|
|
|
|
|
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationDidEnterBackground:)
|
|
|
|
|
name:@"UIApplicationDidEnterBackgroundNotification"
|
|
|
|
|
object:nil];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationWillEnterForeground:)
|
|
|
|
|
name:@"UIApplicationWillEnterForegroundNotification"
|
|
|
|
|
object:nil];
|
|
|
|
|
|
|
|
|
|
// Adds observers for app extension on watchOS platform
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationDidEnterBackground:)
|
|
|
|
|
name:NSExtensionHostDidEnterBackgroundNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
[notificationCenter addObserver:self
|
|
|
|
|
selector:@selector(iOSApplicationWillEnterForeground:)
|
|
|
|
|
name:NSExtensionHostWillEnterForegroundNotification
|
|
|
|
|
object:nil];
|
|
|
|
|
#endif
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
|
|
|
|
return self;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
#if TARGET_OS_WATCH
|
|
|
|
|
/** Generates and maps a unique background identifier to the given semaphore.
|
|
|
|
|
*
|
|
|
|
|
* @param semaphore The semaphore to map.
|
|
|
|
|
* @return A unique GDTCORBackgroundIdentifier mapped to the given semaphore.
|
|
|
|
|
*/
|
|
|
|
|
+ (GDTCORBackgroundIdentifier)createAndMapBackgroundIdentifierToSemaphore:
|
|
|
|
|
(dispatch_semaphore_t)semaphore {
|
|
|
|
|
__block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid;
|
|
|
|
|
dispatch_queue_t queue = gSemaphoreQueue;
|
|
|
|
|
NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *map = gBackgroundIdentifierToSemaphoreMap;
|
|
|
|
|
if (queue && map) {
|
|
|
|
|
dispatch_sync(queue, ^{
|
|
|
|
|
bgID = arc4random();
|
|
|
|
|
NSNumber *bgIDNumber = @(bgID);
|
|
|
|
|
while (bgID == GDTCORBackgroundIdentifierInvalid || map[bgIDNumber]) {
|
|
|
|
|
bgID = arc4random();
|
|
|
|
|
bgIDNumber = @(bgID);
|
|
|
|
|
}
|
|
|
|
|
map[bgIDNumber] = semaphore;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return bgID;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Returns the semaphore mapped to given bgID and removes the value from the map.
|
|
|
|
|
*
|
|
|
|
|
* @param bgID The unique NSUInteger as GDTCORBackgroundIdentifier.
|
|
|
|
|
* @return The semaphore mapped by given bgID.
|
|
|
|
|
*/
|
|
|
|
|
+ (dispatch_semaphore_t)semaphoreForBackgroundIdentifier:(GDTCORBackgroundIdentifier)bgID {
|
|
|
|
|
__block dispatch_semaphore_t semaphore;
|
|
|
|
|
dispatch_queue_t queue = gSemaphoreQueue;
|
|
|
|
|
NSMutableDictionary<NSNumber *, dispatch_semaphore_t> *map = gBackgroundIdentifierToSemaphoreMap;
|
|
|
|
|
NSNumber *bgIDNumber = @(bgID);
|
|
|
|
|
if (queue && map) {
|
|
|
|
|
dispatch_sync(queue, ^{
|
|
|
|
|
semaphore = map[bgIDNumber];
|
|
|
|
|
[map removeObjectForKey:bgIDNumber];
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
return semaphore;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-10-02 10:40:45 -04:00
|
|
|
- (GDTCORBackgroundIdentifier)beginBackgroundTaskWithName:(NSString *)name
|
|
|
|
|
expirationHandler:(void (^)(void))handler {
|
2020-04-24 15:19:43 -07:00
|
|
|
__block GDTCORBackgroundIdentifier bgID = GDTCORBackgroundIdentifierInvalid;
|
|
|
|
|
#if !TARGET_OS_WATCH
|
|
|
|
|
bgID = [[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithName:name
|
|
|
|
|
expirationHandler:handler];
|
2020-01-20 14:50:44 -08:00
|
|
|
#if !NDEBUG
|
|
|
|
|
if (bgID != GDTCORBackgroundIdentifierInvalid) {
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"Creating background task with name:%@ bgID:%ld", name, (long)bgID);
|
2020-01-20 14:50:44 -08:00
|
|
|
}
|
|
|
|
|
#endif // !NDEBUG
|
2020-04-24 15:19:43 -07:00
|
|
|
#elif TARGET_OS_WATCH
|
|
|
|
|
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
|
|
|
|
|
bgID = [GDTCORApplication createAndMapBackgroundIdentifierToSemaphore:semaphore];
|
|
|
|
|
if (bgID != GDTCORBackgroundIdentifierInvalid) {
|
|
|
|
|
GDTCORLogDebug(@"Creating activity with name:%@ bgID:%ld on watchOS.", name, (long)bgID);
|
|
|
|
|
}
|
|
|
|
|
[[self sharedNSProcessInfoForBackgroundTask]
|
|
|
|
|
performExpiringActivityWithReason:name
|
|
|
|
|
usingBlock:^(BOOL expired) {
|
|
|
|
|
if (expired) {
|
|
|
|
|
if (handler) {
|
|
|
|
|
handler();
|
|
|
|
|
}
|
|
|
|
|
dispatch_semaphore_signal(semaphore);
|
|
|
|
|
GDTCORLogDebug(
|
|
|
|
|
@"Activity with name:%@ bgID:%ld on watchOS is expiring.",
|
|
|
|
|
name, (long)bgID);
|
|
|
|
|
} else {
|
|
|
|
|
dispatch_semaphore_wait(
|
|
|
|
|
semaphore,
|
|
|
|
|
dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC));
|
|
|
|
|
}
|
|
|
|
|
}];
|
|
|
|
|
#endif
|
2020-01-20 14:50:44 -08:00
|
|
|
return bgID;
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
2019-09-09 15:21:29 -07:00
|
|
|
- (void)endBackgroundTask:(GDTCORBackgroundIdentifier)bgID {
|
2020-04-24 15:19:43 -07:00
|
|
|
#if !TARGET_OS_WATCH
|
2019-09-09 15:21:29 -07:00
|
|
|
if (bgID != GDTCORBackgroundIdentifierInvalid) {
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"Ending background task with ID:%ld was successful", (long)bgID);
|
2019-08-19 21:00:53 -07:00
|
|
|
[[self sharedApplicationForBackgroundTask] endBackgroundTask:bgID];
|
2020-01-20 14:50:44 -08:00
|
|
|
return;
|
2019-08-19 21:00:53 -07:00
|
|
|
}
|
2020-04-24 15:19:43 -07:00
|
|
|
#elif TARGET_OS_WATCH
|
|
|
|
|
if (bgID != GDTCORBackgroundIdentifierInvalid) {
|
|
|
|
|
dispatch_semaphore_t semaphore = [GDTCORApplication semaphoreForBackgroundIdentifier:bgID];
|
|
|
|
|
GDTCORLogDebug(@"Ending activity with bgID:%ld on watchOS.", (long)bgID);
|
|
|
|
|
if (semaphore) {
|
|
|
|
|
dispatch_semaphore_signal(semaphore);
|
|
|
|
|
GDTCORLogDebug(@"Signaling semaphore with bgID:%ld on watchOS.", (long)bgID);
|
|
|
|
|
} else {
|
|
|
|
|
GDTCORLogDebug(@"Semaphore with bgID:%ld is nil on watchOS.", (long)bgID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // !TARGET_OS_WATCH
|
2019-07-18 09:54:06 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#pragma mark - App environment helpers
|
|
|
|
|
|
|
|
|
|
- (BOOL)isAppExtension {
|
|
|
|
|
BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
|
|
|
|
|
return appExtension;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
/** Returns a UIApplication or NSProcessInfo instance if on the appropriate platform.
|
2019-07-18 09:54:06 -07:00
|
|
|
*
|
2020-04-24 15:19:43 -07:00
|
|
|
* @return The shared UIApplication or NSProcessInfo if on the appropriate platform.
|
2019-07-18 09:54:06 -07:00
|
|
|
*/
|
|
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
|
|
|
|
- (nullable UIApplication *)sharedApplicationForBackgroundTask {
|
2020-04-24 15:19:43 -07:00
|
|
|
#elif TARGET_OS_WATCH
|
|
|
|
|
- (nullable NSProcessInfo *)sharedNSProcessInfoForBackgroundTask {
|
2019-07-18 09:54:06 -07:00
|
|
|
#else
|
|
|
|
|
- (nullable id)sharedApplicationForBackgroundTask {
|
|
|
|
|
#endif
|
2020-04-24 15:19:43 -07:00
|
|
|
id sharedInstance = nil;
|
|
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
|
|
|
|
if (![self isAppExtension]) {
|
|
|
|
|
Class uiApplicationClass = NSClassFromString(@"UIApplication");
|
|
|
|
|
if (uiApplicationClass &&
|
|
|
|
|
[uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
|
|
|
|
|
sharedInstance = [uiApplicationClass sharedApplication];
|
|
|
|
|
}
|
2019-07-18 09:54:06 -07:00
|
|
|
}
|
2020-04-24 15:19:43 -07:00
|
|
|
#elif TARGET_OS_WATCH
|
|
|
|
|
sharedInstance = [NSProcessInfo processInfo];
|
|
|
|
|
#endif
|
|
|
|
|
return sharedInstance;
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
#pragma mark - UIApplicationDelegate and WKExtensionDelegate
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
2019-05-20 17:12:13 -07:00
|
|
|
- (void)iOSApplicationDidEnterBackground:(NSNotification *)notif {
|
2019-10-02 15:10:28 -04:00
|
|
|
_isRunningInBackground = YES;
|
|
|
|
|
|
2019-05-20 17:12:13 -07:00
|
|
|
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is backgrounding.");
|
2019-09-09 15:21:29 -07:00
|
|
|
[notifCenter postNotificationName:kGDTCORApplicationDidEnterBackgroundNotification object:nil];
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (void)iOSApplicationWillEnterForeground:(NSNotification *)notif {
|
2019-10-02 15:10:28 -04:00
|
|
|
_isRunningInBackground = NO;
|
|
|
|
|
|
2019-05-20 17:12:13 -07:00
|
|
|
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is foregrounding.");
|
2019-09-09 15:21:29 -07:00
|
|
|
[notifCenter postNotificationName:kGDTCORApplicationWillEnterForegroundNotification object:nil];
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
2020-04-24 15:19:43 -07:00
|
|
|
#endif // TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
|
2019-05-20 17:12:13 -07:00
|
|
|
|
2020-04-24 15:19:43 -07:00
|
|
|
#pragma mark - UIApplicationDelegate
|
|
|
|
|
|
|
|
|
|
#if TARGET_OS_IOS || TARGET_OS_TV
|
2019-05-20 17:12:13 -07:00
|
|
|
- (void)iOSApplicationWillTerminate:(NSNotification *)notif {
|
|
|
|
|
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is terminating.");
|
2019-09-09 15:21:29 -07:00
|
|
|
[notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
2019-07-18 09:54:06 -07:00
|
|
|
#endif // TARGET_OS_IOS || TARGET_OS_TV
|
2019-05-20 17:12:13 -07:00
|
|
|
|
|
|
|
|
#pragma mark - NSApplicationDelegate
|
|
|
|
|
|
|
|
|
|
#if TARGET_OS_OSX
|
|
|
|
|
- (void)macOSApplicationWillTerminate:(NSNotification *)notif {
|
|
|
|
|
NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
|
2020-04-14 12:30:29 -07:00
|
|
|
GDTCORLogDebug(@"%@", @"GDTCORPlatform is sending a notif that the app is terminating.");
|
2019-09-09 15:21:29 -07:00
|
|
|
[notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
|
2019-05-20 17:12:13 -07:00
|
|
|
}
|
2019-05-23 12:31:59 -07:00
|
|
|
#endif // TARGET_OS_OSX
|
2019-05-20 17:12:13 -07:00
|
|
|
|
|
|
|
|
@end
|