FIRCoreDiagnostics.m 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  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 <objc/runtime.h>
  17. #include <sys/utsname.h>
  18. #import <GoogleDataTransport/GoogleDataTransport.h>
  19. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  20. #import <GoogleUtilities/GULHeartbeatDateStorage.h>
  21. #import <GoogleUtilities/GULLogger.h>
  22. #import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsData.h"
  23. #import "Interop/CoreDiagnostics/Public/FIRCoreDiagnosticsInterop.h"
  24. #import <nanopb/pb.h>
  25. #import <nanopb/pb_decode.h>
  26. #import <nanopb/pb_encode.h>
  27. #import "Firebase/CoreDiagnostics/FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
  28. /** The logger service string to use when printing to the console. */
  29. static GULLoggerService kFIRCoreDiagnostics = @"[FirebaseCoreDiagnostics/FIRCoreDiagnostics]";
  30. #ifdef FIREBASE_BUILD_ZIP_FILE
  31. static BOOL kUsingZipFile = YES;
  32. #else // FIREBASE_BUILD_ZIP_FILE
  33. static BOOL kUsingZipFile = NO;
  34. #endif // FIREBASE_BUILD_ZIP_FILE
  35. #if SWIFT_PACKAGE
  36. #define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_SPM
  37. #elif FIREBASE_BUILD_CARTHAGE
  38. #define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_CARTHAGE
  39. #elif FIREBASE_BUILD_ZIP_FILE
  40. #define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_ZIP_FILE
  41. #else
  42. #define kDeploymentType logs_proto_mobilesdk_ios_ICoreConfiguration_DeploymentType_COCOAPODS
  43. #endif
  44. static NSString *const kFIRServiceMLModelInterpreter = @"MLModelInterpreter";
  45. static NSString *const kFIRServiceAdMob = @"AdMob";
  46. static NSString *const kFIRServiceAuth = @"Auth";
  47. static NSString *const kFIRServiceAuthUI = @"AuthUI";
  48. static NSString *const kFIRServiceCrash = @"Crash";
  49. static NSString *const kFIRServiceDatabase = @"Database";
  50. static NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
  51. static NSString *const kFIRServiceFirestore = @"Firestore";
  52. static NSString *const kFIRServiceFunctions = @"Functions";
  53. static NSString *const kFIRServiceIAM = @"InAppMessaging";
  54. static NSString *const kFIRServiceInstanceID = @"InstanceID";
  55. static NSString *const kFIRServiceInvites = @"Invites";
  56. static NSString *const kFIRServiceMessaging = @"Messaging";
  57. static NSString *const kFIRServiceMeasurement = @"Measurement";
  58. static NSString *const kFIRServicePerformance = @"Performance";
  59. static NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
  60. static NSString *const kFIRServiceStorage = @"Storage";
  61. static NSString *const kGGLServiceAnalytics = @"Analytics";
  62. static NSString *const kGGLServiceSignIn = @"SignIn";
  63. static NSString *const kFIRAppDiagnosticsConfigurationTypeKey =
  64. @"FIRAppDiagnosticsConfigurationTypeKey";
  65. static NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRAppDiagnosticsFIRAppKey";
  66. static NSString *const kFIRAppDiagnosticsSDKNameKey = @"FIRAppDiagnosticsSDKNameKey";
  67. static NSString *const kFIRAppDiagnosticsSDKVersionKey = @"FIRAppDiagnosticsSDKVersionKey";
  68. static NSString *const kFIRCoreDiagnosticsHeartbeatTag = @"FIRCoreDiagnostics";
  69. /**
  70. * The file name to the recent heartbeat date.
  71. */
  72. NSString *const kFIRCoreDiagnosticsHeartbeatDateFileName = @"FIREBASE_DIAGNOSTICS_HEARTBEAT_DATE";
  73. /**
  74. * @note This should implement the GDTCOREventDataObject protocol, but can't because of
  75. * weak-linking.
  76. */
  77. @interface FIRCoreDiagnosticsLog : NSObject
  78. /** The config that will be converted to proto bytes. */
  79. @property(nonatomic) logs_proto_mobilesdk_ios_ICoreConfiguration config;
  80. @end
  81. @implementation FIRCoreDiagnosticsLog
  82. - (instancetype)initWithConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration)config {
  83. self = [super init];
  84. if (self) {
  85. _config = config;
  86. }
  87. return self;
  88. }
  89. // Provided and required by the GDTCOREventDataObject protocol.
  90. - (NSData *)transportBytes {
  91. pb_ostream_t sizestream = PB_OSTREAM_SIZING;
  92. // Encode 1 time to determine the size.
  93. if (!pb_encode(&sizestream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
  94. GDTCORLogError(GDTCORMCETransportBytesError, @"Error in nanopb encoding for size: %s",
  95. PB_GET_ERROR(&sizestream));
  96. }
  97. // Encode a 2nd time to actually get the bytes from it.
  98. size_t bufferSize = sizestream.bytes_written;
  99. CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
  100. CFDataSetLength(dataRef, bufferSize);
  101. pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
  102. if (!pb_encode(&ostream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
  103. GDTCORLogError(GDTCORMCETransportBytesError, @"Error in nanopb encoding for bytes: %s",
  104. PB_GET_ERROR(&ostream));
  105. }
  106. CFDataSetLength(dataRef, ostream.bytes_written);
  107. return CFBridgingRelease(dataRef);
  108. }
  109. - (void)dealloc {
  110. pb_release(logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config);
  111. }
  112. @end
  113. NS_ASSUME_NONNULL_BEGIN
  114. /** This class produces a protobuf containing diagnostics and usage data to be logged. */
  115. @interface FIRCoreDiagnostics : NSObject <FIRCoreDiagnosticsInterop>
  116. /** The queue on which all diagnostics collection will occur. */
  117. @property(nonatomic, readonly) dispatch_queue_t diagnosticsQueue;
  118. /** The transport object used to send data. */
  119. @property(nonatomic, readonly) GDTCORTransport *transport;
  120. /** The storage to store the date of the last sent heartbeat. */
  121. @property(nonatomic, readonly) GULHeartbeatDateStorage *heartbeatDateStorage;
  122. @end
  123. NS_ASSUME_NONNULL_END
  124. @implementation FIRCoreDiagnostics
  125. + (instancetype)sharedInstance {
  126. static FIRCoreDiagnostics *sharedInstance;
  127. static dispatch_once_t onceToken;
  128. dispatch_once(&onceToken, ^{
  129. sharedInstance = [[FIRCoreDiagnostics alloc] init];
  130. });
  131. return sharedInstance;
  132. }
  133. - (instancetype)init {
  134. GDTCORTransport *transport = [[GDTCORTransport alloc] initWithMappingID:@"137"
  135. transformers:nil
  136. target:kGDTCORTargetFLL];
  137. GULHeartbeatDateStorage *dateStorage =
  138. [[GULHeartbeatDateStorage alloc] initWithFileName:kFIRCoreDiagnosticsHeartbeatDateFileName];
  139. return [self initWithTransport:transport heartbeatDateStorage:dateStorage];
  140. }
  141. /** Initializer for unit tests.
  142. *
  143. * @param transport A `GDTCORTransport` instance which that be used to send event.
  144. * @param heartbeatDateStorage An instanse of date storage to track heartbeat sending.
  145. * @return Returns the initialized `FIRCoreDiagnostics` instance.
  146. */
  147. - (instancetype)initWithTransport:(GDTCORTransport *)transport
  148. heartbeatDateStorage:(GULHeartbeatDateStorage *)heartbeatDateStorage {
  149. self = [super init];
  150. if (self) {
  151. _diagnosticsQueue =
  152. dispatch_queue_create("com.google.FIRCoreDiagnostics", DISPATCH_QUEUE_SERIAL);
  153. _transport = transport;
  154. _heartbeatDateStorage = heartbeatDateStorage;
  155. }
  156. return self;
  157. }
  158. #pragma mark - nanopb helper functions
  159. /** Callocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
  160. *
  161. * @note Memory needs to be free manually, through pb_free or pb_release.
  162. * @param string The string to encode as pb_bytes.
  163. */
  164. pb_bytes_array_t *FIREncodeString(NSString *string) {
  165. NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
  166. return FIREncodeData(stringBytes);
  167. }
  168. /** Callocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
  169. *
  170. * @note Memory needs to be free manually, through pb_free or pb_release.
  171. * @param data The data to copy into the new bytes array.
  172. */
  173. pb_bytes_array_t *FIREncodeData(NSData *data) {
  174. pb_bytes_array_t *pbBytesArray = calloc(1, PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
  175. if (pbBytesArray != NULL) {
  176. [data getBytes:pbBytesArray->bytes length:data.length];
  177. pbBytesArray->size = (pb_size_t)data.length;
  178. }
  179. return pbBytesArray;
  180. }
  181. /** Maps a service string to the representative nanopb enum.
  182. *
  183. * @param serviceString The SDK service string to convert.
  184. * @return The representative nanopb enum.
  185. */
  186. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType FIRMapFromServiceStringToTypeEnum(
  187. NSString *serviceString) {
  188. static NSDictionary<NSString *, NSNumber *> *serviceStringToTypeEnum;
  189. if (serviceStringToTypeEnum == nil) {
  190. serviceStringToTypeEnum = @{
  191. kFIRServiceAdMob : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB),
  192. kFIRServiceMessaging : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING),
  193. kFIRServiceMeasurement :
  194. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT),
  195. kFIRServiceRemoteConfig :
  196. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG),
  197. kFIRServiceDatabase : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE),
  198. kFIRServiceDynamicLinks :
  199. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS),
  200. kFIRServiceAuth : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH),
  201. kFIRServiceAuthUI : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI),
  202. kFIRServiceFirestore : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE),
  203. kFIRServiceFunctions : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS),
  204. kFIRServicePerformance :
  205. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE),
  206. kFIRServiceStorage : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE),
  207. kFIRServiceMLModelInterpreter :
  208. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER),
  209. kGGLServiceAnalytics : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS),
  210. kGGLServiceSignIn : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN),
  211. kFIRServiceIAM : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING),
  212. };
  213. }
  214. if (serviceStringToTypeEnum[serviceString] != nil) {
  215. return (int32_t)serviceStringToTypeEnum[serviceString].longLongValue;
  216. }
  217. return logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE;
  218. }
  219. #pragma mark - Proto population functions
  220. /** Populates the given proto with data related to an SDK logDiagnostics call from the
  221. * diagnosticObjects dictionary.
  222. *
  223. * @param config The proto to populate
  224. * @param diagnosticObjects The dictionary of diagnostics objects.
  225. */
  226. void FIRPopulateProtoWithInfoFromUserInfoParams(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
  227. NSDictionary<NSString *, id> *diagnosticObjects) {
  228. NSNumber *configurationType = diagnosticObjects[kFIRCDConfigurationTypeKey];
  229. if (configurationType != nil) {
  230. switch (configurationType.integerValue) {
  231. case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE:
  232. config->configuration_type =
  233. logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE;
  234. config->has_configuration_type = 1;
  235. break;
  236. case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK:
  237. config->configuration_type =
  238. logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK;
  239. config->has_configuration_type = 1;
  240. break;
  241. default:
  242. break;
  243. }
  244. }
  245. NSString *sdkName = diagnosticObjects[kFIRCDSdkNameKey];
  246. if (sdkName) {
  247. config->sdk_name = FIRMapFromServiceStringToTypeEnum(sdkName);
  248. config->has_sdk_name = 1;
  249. }
  250. NSString *version = diagnosticObjects[kFIRCDSdkVersionKey];
  251. if (version) {
  252. config->sdk_version = FIREncodeString(version);
  253. }
  254. }
  255. /** Populates the given proto with data from the calling FIRApp using the given
  256. * diagnosticObjects dictionary.
  257. *
  258. * @param config The proto to populate
  259. * @param diagnosticObjects The dictionary of diagnostics objects.
  260. */
  261. void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
  262. NSDictionary<NSString *, id> *diagnosticObjects) {
  263. config->pod_name = logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE;
  264. config->has_pod_name = 1;
  265. if (!diagnosticObjects[kFIRCDllAppsCountKey]) {
  266. GDTCORLogError(GDTCORMCEGeneralError, @"%@",
  267. @"App count is a required value in the data dict.");
  268. }
  269. config->app_count = (int32_t)[diagnosticObjects[kFIRCDllAppsCountKey] integerValue];
  270. config->has_app_count = 1;
  271. NSString *googleAppID = diagnosticObjects[kFIRCDGoogleAppIDKey];
  272. if (googleAppID.length) {
  273. config->app_id = FIREncodeString(googleAppID);
  274. }
  275. NSString *bundleID = diagnosticObjects[kFIRCDBundleIDKey];
  276. if (bundleID.length) {
  277. config->bundle_id = FIREncodeString(bundleID);
  278. }
  279. NSString *firebaseUserAgent = diagnosticObjects[kFIRCDFirebaseUserAgentKey];
  280. if (firebaseUserAgent.length) {
  281. config->platform_info = FIREncodeString(firebaseUserAgent);
  282. }
  283. NSNumber *usingOptionsFromDefaultPlist = diagnosticObjects[kFIRCDUsingOptionsFromDefaultPlistKey];
  284. if (usingOptionsFromDefaultPlist != nil) {
  285. config->use_default_app = [usingOptionsFromDefaultPlist boolValue];
  286. config->has_use_default_app = 1;
  287. }
  288. NSString *libraryVersionID = diagnosticObjects[kFIRCDLibraryVersionIDKey];
  289. if (libraryVersionID) {
  290. config->icore_version = FIREncodeString(libraryVersionID);
  291. }
  292. NSString *deviceModel = [GULAppEnvironmentUtil deviceModel];
  293. if (deviceModel.length) {
  294. config->device_model = FIREncodeString(deviceModel);
  295. }
  296. NSString *osVersion = [GULAppEnvironmentUtil systemVersion];
  297. if (osVersion.length) {
  298. config->os_version = FIREncodeString(osVersion);
  299. }
  300. config->using_zip_file = kUsingZipFile;
  301. config->has_using_zip_file = 1;
  302. config->deployment_type = kDeploymentType;
  303. config->has_deployment_type = 1;
  304. config->deployed_in_app_store = [GULAppEnvironmentUtil isFromAppStore];
  305. config->has_deployed_in_app_store = 1;
  306. }
  307. /** Populates the given proto with installed services data.
  308. *
  309. * @param config The proto to populate
  310. */
  311. void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
  312. NSMutableArray<NSNumber *> *sdkServiceInstalledArray = [NSMutableArray array];
  313. // AdMob
  314. if (NSClassFromString(@"GADBannerView") != nil) {
  315. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAdMob))];
  316. }
  317. // CloudMessaging
  318. if (NSClassFromString(@"FIRMessaging") != nil) {
  319. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMessaging))];
  320. }
  321. // RemoteConfig
  322. if (NSClassFromString(@"FIRRemoteConfig") != nil) {
  323. [sdkServiceInstalledArray
  324. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceRemoteConfig))];
  325. }
  326. // Measurement/Analtyics
  327. if (NSClassFromString(@"FIRAnalytics") != nil) {
  328. [sdkServiceInstalledArray
  329. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMeasurement))];
  330. }
  331. // ML Model Interpreter
  332. if (NSClassFromString(@"FIRCustomModelInterpreter") != nil) {
  333. [sdkServiceInstalledArray
  334. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLModelInterpreter))];
  335. }
  336. // Database
  337. if (NSClassFromString(@"FIRDatabase") != nil) {
  338. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDatabase))];
  339. }
  340. // DynamicDeepLink
  341. if (NSClassFromString(@"FIRDynamicLinks") != nil) {
  342. [sdkServiceInstalledArray
  343. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDynamicLinks))];
  344. }
  345. // Auth
  346. if (NSClassFromString(@"FIRAuth") != nil) {
  347. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuth))];
  348. }
  349. // AuthUI
  350. if (NSClassFromString(@"FUIAuth") != nil) {
  351. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuthUI))];
  352. }
  353. // Firestore
  354. if (NSClassFromString(@"FIRFirestore") != nil) {
  355. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFirestore))];
  356. }
  357. // Functions
  358. if (NSClassFromString(@"FIRFunctions") != nil) {
  359. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFunctions))];
  360. }
  361. // Performance
  362. if (NSClassFromString(@"FIRPerformance") != nil) {
  363. [sdkServiceInstalledArray
  364. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServicePerformance))];
  365. }
  366. // Storage
  367. if (NSClassFromString(@"FIRStorage") != nil) {
  368. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceStorage))];
  369. }
  370. // SignIn via Google pod
  371. if (NSClassFromString(@"GIDSignIn") != nil && NSClassFromString(@"GGLContext") != nil) {
  372. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceSignIn))];
  373. }
  374. // Analytics via Google pod
  375. if (NSClassFromString(@"GAI") != nil && NSClassFromString(@"GGLContext") != nil) {
  376. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceAnalytics))];
  377. }
  378. // In-App Messaging
  379. if (NSClassFromString(@"FIRInAppMessaging") != nil) {
  380. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceIAM))];
  381. }
  382. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *servicesInstalled =
  383. calloc(sdkServiceInstalledArray.count,
  384. sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType));
  385. if (servicesInstalled == NULL) {
  386. return;
  387. }
  388. for (NSUInteger i = 0; i < sdkServiceInstalledArray.count; i++) {
  389. NSNumber *typeEnum = sdkServiceInstalledArray[i];
  390. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType serviceType =
  391. (int32_t)typeEnum.integerValue;
  392. servicesInstalled[i] = serviceType;
  393. }
  394. config->sdk_service_installed = servicesInstalled;
  395. config->sdk_service_installed_count = (int32_t)sdkServiceInstalledArray.count;
  396. }
  397. /** Populates the proto with Info.plist values.
  398. *
  399. * @param config The proto to populate.
  400. */
  401. void FIRPopulateProtoWithInfoPlistValues(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
  402. NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
  403. NSString *xcodeVersion = info[@"DTXcodeBuild"] ?: @"";
  404. NSString *sdkVersion = info[@"DTSDKBuild"] ?: @"";
  405. NSString *combinedVersions = [NSString stringWithFormat:@"%@-%@", xcodeVersion, sdkVersion];
  406. config->apple_framework_version = FIREncodeString(combinedVersions);
  407. NSString *minVersion = info[@"MinimumOSVersion"];
  408. if (minVersion) {
  409. config->min_supported_ios_version = FIREncodeString(minVersion);
  410. }
  411. // Apps can turn off swizzling in the Info.plist, check if they've explicitly set the value and
  412. // report it. It's enabled by default.
  413. NSNumber *appDelegateSwizzledNum = info[@"FirebaseAppDelegateProxyEnabled"];
  414. BOOL appDelegateSwizzled = YES;
  415. if ([appDelegateSwizzledNum isKindOfClass:[NSNumber class]]) {
  416. appDelegateSwizzled = [appDelegateSwizzledNum boolValue];
  417. }
  418. config->swizzling_enabled = appDelegateSwizzled;
  419. config->has_swizzling_enabled = 1;
  420. }
  421. #pragma mark - FIRCoreDiagnosticsInterop
  422. + (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
  423. FIRCoreDiagnostics *diagnostics = [FIRCoreDiagnostics sharedInstance];
  424. [diagnostics sendDiagnosticsData:diagnosticsData];
  425. }
  426. - (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
  427. dispatch_async(self.diagnosticsQueue, ^{
  428. NSDictionary<NSString *, id> *diagnosticObjects = diagnosticsData.diagnosticObjects;
  429. NSNumber *isDataCollectionDefaultEnabled =
  430. diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey];
  431. if (isDataCollectionDefaultEnabled && ![isDataCollectionDefaultEnabled boolValue]) {
  432. return;
  433. }
  434. // Create the proto.
  435. logs_proto_mobilesdk_ios_ICoreConfiguration icore_config =
  436. logs_proto_mobilesdk_ios_ICoreConfiguration_init_default;
  437. icore_config.using_gdt = 1;
  438. icore_config.has_using_gdt = 1;
  439. // Populate the proto with information.
  440. FIRPopulateProtoWithInfoFromUserInfoParams(&icore_config, diagnosticObjects);
  441. FIRPopulateProtoWithCommonInfoFromApp(&icore_config, diagnosticObjects);
  442. FIRPopulateProtoWithInstalledServices(&icore_config);
  443. FIRPopulateProtoWithInfoPlistValues(&icore_config);
  444. [self setHeartbeatFlagIfNeededToConfig:&icore_config];
  445. // This log object is capable of converting the proto to bytes.
  446. FIRCoreDiagnosticsLog *log = [[FIRCoreDiagnosticsLog alloc] initWithConfig:icore_config];
  447. // Send the log as a telemetry event.
  448. GDTCOREvent *event = [self.transport eventForTransport];
  449. event.dataObject = (id<GDTCOREventDataObject>)log;
  450. [self.transport sendTelemetryEvent:event];
  451. });
  452. }
  453. #pragma mark - Heartbeat
  454. - (void)setHeartbeatFlagIfNeededToConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration *)config {
  455. // Check if need to send a heartbeat.
  456. NSDate *currentDate = [NSDate date];
  457. NSDate *lastCheckin =
  458. [self.heartbeatDateStorage heartbeatDateForTag:kFIRCoreDiagnosticsHeartbeatTag];
  459. if (lastCheckin) {
  460. // Ensure the previous checkin was on a different date in the past.
  461. if ([self isDate:currentDate inSameDayOrBeforeThan:lastCheckin]) {
  462. return;
  463. }
  464. }
  465. // Update heartbeat sent date.
  466. [self.heartbeatDateStorage setHearbeatDate:currentDate forTag:kFIRCoreDiagnosticsHeartbeatTag];
  467. // Set the flag.
  468. config->sdk_name = logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE;
  469. config->has_sdk_name = 1;
  470. }
  471. - (BOOL)isDate:(NSDate *)date1 inSameDayOrBeforeThan:(NSDate *)date2 {
  472. return [[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2] ||
  473. [date1 compare:date2] == NSOrderedAscending;
  474. }
  475. @end