GDTCORPlatform.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /*
  2. * Copyright 2019 Google
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #import <GoogleDataTransport/GDTCORPlatform.h>
  17. #import <GoogleDataTransport/GDTCORAssert.h>
  18. #import <GoogleDataTransport/GDTCORConsoleLogger.h>
  19. #import <GoogleDataTransport/GDTCORReachability.h>
  20. #import "GDTCORLibrary/Private/GDTCORRegistrar_Private.h"
  21. #ifdef GDTCOR_VERSION
  22. #define STR(x) STR_EXPAND(x)
  23. #define STR_EXPAND(x) #x
  24. NSString *const kGDTCORVersion = @STR(GDTCOR_VERSION);
  25. #else
  26. NSString *const kGDTCORVersion = @"Unknown";
  27. #endif // GDTCOR_VERSION
  28. const GDTCORBackgroundIdentifier GDTCORBackgroundIdentifierInvalid = 0;
  29. NSString *const kGDTCORApplicationDidEnterBackgroundNotification =
  30. @"GDTCORApplicationDidEnterBackgroundNotification";
  31. NSString *const kGDTCORApplicationWillEnterForegroundNotification =
  32. @"GDTCORApplicationWillEnterForegroundNotification";
  33. NSString *const kGDTCORApplicationWillTerminateNotification =
  34. @"GDTCORApplicationWillTerminateNotification";
  35. #if !TARGET_OS_WATCH
  36. BOOL GDTCORReachabilityFlagsContainWWAN(SCNetworkReachabilityFlags flags) {
  37. #if TARGET_OS_IOS
  38. return (flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN;
  39. #else
  40. return NO;
  41. #endif // TARGET_OS_IOS
  42. }
  43. #endif // !TARGET_OS_WATCH
  44. GDTCORNetworkType GDTCORNetworkTypeMessage() {
  45. #if !TARGET_OS_WATCH
  46. SCNetworkReachabilityFlags reachabilityFlags = [GDTCORReachability currentFlags];
  47. if ((reachabilityFlags & kSCNetworkReachabilityFlagsReachable) ==
  48. kSCNetworkReachabilityFlagsReachable) {
  49. if (GDTCORReachabilityFlagsContainWWAN(reachabilityFlags)) {
  50. return GDTCORNetworkTypeMobile;
  51. } else {
  52. return GDTCORNetworkTypeWIFI;
  53. }
  54. }
  55. #endif
  56. return GDTCORNetworkTypeUNKNOWN;
  57. }
  58. GDTCORNetworkMobileSubtype GDTCORNetworkMobileSubTypeMessage() {
  59. #if TARGET_OS_IOS
  60. static NSDictionary<NSString *, NSNumber *> *CTRadioAccessTechnologyToNetworkSubTypeMessage;
  61. static CTTelephonyNetworkInfo *networkInfo;
  62. static dispatch_once_t onceToken;
  63. dispatch_once(&onceToken, ^{
  64. CTRadioAccessTechnologyToNetworkSubTypeMessage = @{
  65. CTRadioAccessTechnologyGPRS : @(GDTCORNetworkMobileSubtypeGPRS),
  66. CTRadioAccessTechnologyEdge : @(GDTCORNetworkMobileSubtypeEdge),
  67. CTRadioAccessTechnologyWCDMA : @(GDTCORNetworkMobileSubtypeWCDMA),
  68. CTRadioAccessTechnologyHSDPA : @(GDTCORNetworkMobileSubtypeHSDPA),
  69. CTRadioAccessTechnologyHSUPA : @(GDTCORNetworkMobileSubtypeHSUPA),
  70. CTRadioAccessTechnologyCDMA1x : @(GDTCORNetworkMobileSubtypeCDMA1x),
  71. CTRadioAccessTechnologyCDMAEVDORev0 : @(GDTCORNetworkMobileSubtypeCDMAEVDORev0),
  72. CTRadioAccessTechnologyCDMAEVDORevA : @(GDTCORNetworkMobileSubtypeCDMAEVDORevA),
  73. CTRadioAccessTechnologyCDMAEVDORevB : @(GDTCORNetworkMobileSubtypeCDMAEVDORevB),
  74. CTRadioAccessTechnologyeHRPD : @(GDTCORNetworkMobileSubtypeHRPD),
  75. CTRadioAccessTechnologyLTE : @(GDTCORNetworkMobileSubtypeLTE),
  76. };
  77. networkInfo = [[CTTelephonyNetworkInfo alloc] init];
  78. });
  79. NSString *networkCurrentRadioAccessTechnology;
  80. #if TARGET_OS_MACCATALYST
  81. NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict =
  82. networkInfo.serviceCurrentRadioAccessTechnology;
  83. if (networkCurrentRadioAccessTechnologyDict.count) {
  84. networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0];
  85. }
  86. #else
  87. if (@available(iOS 12.0, *)) {
  88. NSDictionary<NSString *, NSString *> *networkCurrentRadioAccessTechnologyDict =
  89. networkInfo.serviceCurrentRadioAccessTechnology;
  90. if (networkCurrentRadioAccessTechnologyDict.count) {
  91. // In iOS 12, multiple radio technologies can be captured. We prefer not particular radio
  92. // tech to another, so we'll just return the first value in the dictionary.
  93. networkCurrentRadioAccessTechnology = networkCurrentRadioAccessTechnologyDict.allValues[0];
  94. }
  95. } else {
  96. networkCurrentRadioAccessTechnology = networkInfo.currentRadioAccessTechnology;
  97. }
  98. #endif
  99. if (networkCurrentRadioAccessTechnology) {
  100. NSNumber *networkMobileSubtype =
  101. CTRadioAccessTechnologyToNetworkSubTypeMessage[networkCurrentRadioAccessTechnology];
  102. return networkMobileSubtype.intValue;
  103. } else {
  104. return GDTCORNetworkMobileSubtypeUNKNOWN;
  105. }
  106. #else
  107. return GDTCORNetworkMobileSubtypeUNKNOWN;
  108. #endif
  109. }
  110. @interface GDTCORApplication ()
  111. /**
  112. Private flag to match the existing `readonly` public flag. This will be accurate for all platforms,
  113. since we handle each platform's lifecycle notifications separately.
  114. */
  115. @property(atomic, readwrite) BOOL isRunningInBackground;
  116. @end
  117. @implementation GDTCORApplication
  118. + (void)load {
  119. GDTCORLogDebug(
  120. "%@", @"GDT is initializing. Please note that if you quit the app via the "
  121. "debugger and not through a lifecycle event, event data will remain on disk but "
  122. "storage won't have a reference to them since the singleton wasn't saved to disk.");
  123. #if TARGET_OS_IOS || TARGET_OS_TV
  124. // If this asserts, please file a bug at https://github.com/firebase/firebase-ios-sdk/issues.
  125. GDTCORFatalAssert(
  126. GDTCORBackgroundIdentifierInvalid == UIBackgroundTaskInvalid,
  127. @"GDTCORBackgroundIdentifierInvalid and UIBackgroundTaskInvalid should be the same.");
  128. #endif
  129. [self sharedApplication];
  130. }
  131. + (nullable GDTCORApplication *)sharedApplication {
  132. static GDTCORApplication *application;
  133. static dispatch_once_t onceToken;
  134. dispatch_once(&onceToken, ^{
  135. application = [[GDTCORApplication alloc] init];
  136. });
  137. return application;
  138. }
  139. - (instancetype)init {
  140. self = [super init];
  141. if (self) {
  142. // This class will be instantiated in the foreground.
  143. _isRunningInBackground = NO;
  144. #if TARGET_OS_IOS || TARGET_OS_TV
  145. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  146. [notificationCenter addObserver:self
  147. selector:@selector(iOSApplicationDidEnterBackground:)
  148. name:UIApplicationDidEnterBackgroundNotification
  149. object:nil];
  150. [notificationCenter addObserver:self
  151. selector:@selector(iOSApplicationWillEnterForeground:)
  152. name:UIApplicationWillEnterForegroundNotification
  153. object:nil];
  154. NSString *name = UIApplicationWillTerminateNotification;
  155. [notificationCenter addObserver:self
  156. selector:@selector(iOSApplicationWillTerminate:)
  157. name:name
  158. object:nil];
  159. #if defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  160. if (@available(iOS 13, tvOS 13.0, *)) {
  161. [notificationCenter addObserver:self
  162. selector:@selector(iOSApplicationWillEnterForeground:)
  163. name:UISceneWillEnterForegroundNotification
  164. object:nil];
  165. [notificationCenter addObserver:self
  166. selector:@selector(iOSApplicationDidEnterBackground:)
  167. name:UISceneWillDeactivateNotification
  168. object:nil];
  169. }
  170. #endif // defined(__IPHONE_13_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  171. #elif TARGET_OS_OSX
  172. NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
  173. [notificationCenter addObserver:self
  174. selector:@selector(macOSApplicationWillTerminate:)
  175. name:NSApplicationWillTerminateNotification
  176. object:nil];
  177. #endif // TARGET_OS_IOS || TARGET_OS_TV
  178. }
  179. return self;
  180. }
  181. - (GDTCORBackgroundIdentifier)beginBackgroundTaskWithName:(NSString *)name
  182. expirationHandler:(void (^)(void))handler {
  183. GDTCORBackgroundIdentifier bgID =
  184. [[self sharedApplicationForBackgroundTask] beginBackgroundTaskWithName:name
  185. expirationHandler:handler];
  186. #if !NDEBUG
  187. if (bgID != GDTCORBackgroundIdentifierInvalid) {
  188. GDTCORLogDebug("Creating background task with name:%@ bgID:%ld", name, (long)bgID);
  189. }
  190. #endif // !NDEBUG
  191. return bgID;
  192. }
  193. - (void)endBackgroundTask:(GDTCORBackgroundIdentifier)bgID {
  194. if (bgID != GDTCORBackgroundIdentifierInvalid) {
  195. GDTCORLogDebug("Ending background task with ID:%ld was successful", (long)bgID);
  196. [[self sharedApplicationForBackgroundTask] endBackgroundTask:bgID];
  197. return;
  198. }
  199. }
  200. #pragma mark - App environment helpers
  201. - (BOOL)isAppExtension {
  202. #if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_WATCH
  203. BOOL appExtension = [[[NSBundle mainBundle] bundlePath] hasSuffix:@".appex"];
  204. return appExtension;
  205. #elif TARGET_OS_OSX
  206. return NO;
  207. #endif
  208. }
  209. /** Returns a UIApplication instance if on the appropriate platform.
  210. *
  211. * @return The shared UIApplication if on the appropriate platform.
  212. */
  213. #if TARGET_OS_IOS || TARGET_OS_TV
  214. - (nullable UIApplication *)sharedApplicationForBackgroundTask {
  215. #else
  216. - (nullable id)sharedApplicationForBackgroundTask {
  217. #endif
  218. if ([self isAppExtension]) {
  219. return nil;
  220. }
  221. id sharedApplication = nil;
  222. Class uiApplicationClass = NSClassFromString(@"UIApplication");
  223. if (uiApplicationClass &&
  224. [uiApplicationClass respondsToSelector:(NSSelectorFromString(@"sharedApplication"))]) {
  225. sharedApplication = [uiApplicationClass sharedApplication];
  226. }
  227. return sharedApplication;
  228. }
  229. #pragma mark - UIApplicationDelegate
  230. #if TARGET_OS_IOS || TARGET_OS_TV
  231. - (void)iOSApplicationDidEnterBackground:(NSNotification *)notif {
  232. _isRunningInBackground = YES;
  233. NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
  234. GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is backgrounding.");
  235. [notifCenter postNotificationName:kGDTCORApplicationDidEnterBackgroundNotification object:nil];
  236. }
  237. - (void)iOSApplicationWillEnterForeground:(NSNotification *)notif {
  238. _isRunningInBackground = NO;
  239. NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
  240. GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is foregrounding.");
  241. [notifCenter postNotificationName:kGDTCORApplicationWillEnterForegroundNotification object:nil];
  242. }
  243. - (void)iOSApplicationWillTerminate:(NSNotification *)notif {
  244. NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
  245. GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is terminating.");
  246. [notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
  247. }
  248. #endif // TARGET_OS_IOS || TARGET_OS_TV
  249. #pragma mark - NSApplicationDelegate
  250. #if TARGET_OS_OSX
  251. - (void)macOSApplicationWillTerminate:(NSNotification *)notif {
  252. NSNotificationCenter *notifCenter = [NSNotificationCenter defaultCenter];
  253. GDTCORLogDebug("%@", @"GDTCORPlatform is sending a notif that the app is terminating.");
  254. [notifCenter postNotificationName:kGDTCORApplicationWillTerminateNotification object:nil];
  255. }
  256. #endif // TARGET_OS_OSX
  257. @end