FIRCoreDiagnostics.m 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  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/GDTEvent.h>
  19. #import <GoogleDataTransport/GDTTargets.h>
  20. #import <GoogleDataTransport/GDTTransport.h>
  21. #import <GoogleUtilities/GULAppEnvironmentUtil.h>
  22. #import <GoogleUtilities/GULLogger.h>
  23. #import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsData.h>
  24. #import <FirebaseCoreDiagnosticsInterop/FIRCoreDiagnosticsInterop.h>
  25. #import <nanopb/pb.h>
  26. #import <nanopb/pb_decode.h>
  27. #import <nanopb/pb_encode.h>
  28. #import "FIRCDLibrary/Protogen/nanopb/firebasecore.nanopb.h"
  29. #import "FIRCDLibrary/FIRCoreDiagnosticsDateFileStorage.h"
  30. /** The logger service string to use when printing to the console. */
  31. static GULLoggerService kFIRCoreDiagnostics = @"[FirebaseCoreDiagnostics/FIRCoreDiagnostics]";
  32. #ifdef FIREBASE_BUILD_ZIP_FILE
  33. static BOOL kUsingZipFile = YES;
  34. #else // FIREBASE_BUILD_ZIP_FILE
  35. static BOOL kUsingZipFile = NO;
  36. #endif // FIREBASE_BUILD_ZIP_FILE
  37. #ifdef 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 kFIRServiceMLVisionOnDeviceAutoML = @"MLVisionOnDeviceAutoML";
  45. static NSString *const kFIRServiceMLVisionOnDeviceFace = @"MLVisionOnDeviceFace";
  46. static NSString *const kFIRServiceMLVisionOnDeviceBarcode = @"MLVisionOnDeviceBarcode";
  47. static NSString *const kFIRServiceMLVisionOnDeviceText = @"MLVisionOnDeviceText";
  48. static NSString *const kFIRServiceMLVisionOnDeviceLabel = @"MLVisionOnDeviceLabel";
  49. static NSString *const kFIRServiceMLVisionOnDeviceObjectDetection =
  50. @"MLVisionOnDeviceObjectDetection";
  51. static NSString *const kFIRServiceMLModelInterpreter = @"MLModelInterpreter";
  52. static NSString *const kFIRServiceAdMob = @"AdMob";
  53. static NSString *const kFIRServiceAuth = @"Auth";
  54. static NSString *const kFIRServiceAuthUI = @"AuthUI";
  55. static NSString *const kFIRServiceCrash = @"Crash";
  56. static NSString *const kFIRServiceDatabase = @"Database";
  57. static NSString *const kFIRServiceDynamicLinks = @"DynamicLinks";
  58. static NSString *const kFIRServiceFirestore = @"Firestore";
  59. static NSString *const kFIRServiceFunctions = @"Functions";
  60. static NSString *const kFIRServiceIAM = @"InAppMessaging";
  61. static NSString *const kFIRServiceInstanceID = @"InstanceID";
  62. static NSString *const kFIRServiceInvites = @"Invites";
  63. static NSString *const kFIRServiceMessaging = @"Messaging";
  64. static NSString *const kFIRServiceMeasurement = @"Measurement";
  65. static NSString *const kFIRServicePerformance = @"Performance";
  66. static NSString *const kFIRServiceRemoteConfig = @"RemoteConfig";
  67. static NSString *const kFIRServiceStorage = @"Storage";
  68. static NSString *const kGGLServiceAnalytics = @"Analytics";
  69. static NSString *const kGGLServiceSignIn = @"SignIn";
  70. static NSString *const kFIRAppDiagnosticsConfigurationTypeKey =
  71. @"FIRAppDiagnosticsConfigurationTypeKey";
  72. static NSString *const kFIRAppDiagnosticsFIRAppKey = @"FIRAppDiagnosticsFIRAppKey";
  73. static NSString *const kFIRAppDiagnosticsSDKNameKey = @"FIRAppDiagnosticsSDKNameKey";
  74. static NSString *const kFIRAppDiagnosticsSDKVersionKey = @"FIRAppDiagnosticsSDKVersionKey";
  75. /**
  76. * The file name to the recent heartbeat date.
  77. */
  78. NSString *const kFIRCoreDiagnosticsHeartbeatDateFileName = @"FIREBASE_DIAGNOSTICS_HEARTBEAT_DATE";
  79. /**
  80. * @note This should implement the GDTEventDataObject protocol, but can't because of weak-linking.
  81. */
  82. @interface FIRCoreDiagnosticsLog : NSObject
  83. /** The config that will be converted to proto bytes. */
  84. @property(nonatomic) logs_proto_mobilesdk_ios_ICoreConfiguration config;
  85. @end
  86. @implementation FIRCoreDiagnosticsLog
  87. - (instancetype)initWithConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration)config {
  88. self = [super init];
  89. if (self) {
  90. _config = config;
  91. }
  92. return self;
  93. }
  94. // Provided and required by the GDTEventDataObject protocol.
  95. - (NSData *)transportBytes {
  96. pb_ostream_t sizestream = PB_OSTREAM_SIZING;
  97. // Encode 1 time to determine the size.
  98. if (!pb_encode(&sizestream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
  99. NSCAssert(NO, @"Error in nanopb encoding for size: %s", PB_GET_ERROR(&sizestream));
  100. }
  101. // Encode a 2nd time to actually get the bytes from it.
  102. size_t bufferSize = sizestream.bytes_written;
  103. CFMutableDataRef dataRef = CFDataCreateMutable(CFAllocatorGetDefault(), bufferSize);
  104. pb_ostream_t ostream = pb_ostream_from_buffer((void *)CFDataGetBytePtr(dataRef), bufferSize);
  105. if (!pb_encode(&ostream, logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config)) {
  106. NSCAssert(NO, @"Error in nanopb encoding for bytes: %s", PB_GET_ERROR(&ostream));
  107. }
  108. CFDataSetLength(dataRef, ostream.bytes_written);
  109. return CFBridgingRelease(dataRef);
  110. }
  111. - (void)dealloc {
  112. pb_release(logs_proto_mobilesdk_ios_ICoreConfiguration_fields, &_config);
  113. }
  114. @end
  115. NS_ASSUME_NONNULL_BEGIN
  116. /** This class produces a protobuf containing diagnostics and usage data to be logged. */
  117. @interface FIRCoreDiagnostics : NSObject <FIRCoreDiagnosticsInterop>
  118. /** The queue on which all diagnostics collection will occur. */
  119. @property(nonatomic, readonly) dispatch_queue_t diagnosticsQueue;
  120. /** The transport object used to send data. */
  121. @property(nonatomic, readonly) GDTTransport *transport;
  122. /** The storage to store the date of the last sent heartbeat. */
  123. @property(nonatomic, readonly) FIRCoreDiagnosticsDateFileStorage *heartbeatDateStorage;
  124. @end
  125. NS_ASSUME_NONNULL_END
  126. @implementation FIRCoreDiagnostics
  127. + (instancetype)sharedInstance {
  128. static FIRCoreDiagnostics *sharedInstance;
  129. static dispatch_once_t onceToken;
  130. dispatch_once(&onceToken, ^{
  131. sharedInstance = [[FIRCoreDiagnostics alloc] init];
  132. });
  133. return sharedInstance;
  134. }
  135. - (instancetype)init {
  136. GDTTransport *transport = [[GDTTransport alloc] initWithMappingID:@"137"
  137. transformers:nil
  138. target:kGDTTargetCCT];
  139. FIRCoreDiagnosticsDateFileStorage *dateStorage = [[FIRCoreDiagnosticsDateFileStorage alloc]
  140. initWithFileURL:[[self class] filePathURLWithName:kFIRCoreDiagnosticsHeartbeatDateFileName]];
  141. return [self initWithTransport:transport heartbeatDateStorage:dateStorage];
  142. }
  143. /** Initializer for unit tests.
  144. *
  145. * @param transport A `GDTTransport` instance which that be used to send event.
  146. * @param heartbeatDateStorage An instanse of date storage to track heartbeat sending.
  147. * @return Returns the initialized `FIRCoreDiagnostics` instance.
  148. */
  149. - (instancetype)initWithTransport:(GDTTransport *)transport
  150. heartbeatDateStorage:(FIRCoreDiagnosticsDateFileStorage *)heartbeatDateStorage {
  151. self = [super init];
  152. if (self) {
  153. _diagnosticsQueue =
  154. dispatch_queue_create("com.google.FIRCoreDiagnostics", DISPATCH_QUEUE_SERIAL);
  155. _transport = transport;
  156. _heartbeatDateStorage = heartbeatDateStorage;
  157. }
  158. return self;
  159. }
  160. #pragma mark - File path helpers
  161. /** Returns the URL path of the file with name fileName under the Application Support folder for
  162. * local logging. Creates the Application Support folder if the folder doesn't exist.
  163. *
  164. * @return the URL path of the file with the name fileName in Application Support.
  165. */
  166. + (NSURL *)filePathURLWithName:(NSString *)fileName {
  167. @synchronized(self) {
  168. NSArray<NSString *> *paths =
  169. NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
  170. NSArray<NSString *> *components = @[ paths.lastObject, @"Google/FIRApp" ];
  171. NSString *directoryString = [NSString pathWithComponents:components];
  172. NSURL *directoryURL = [NSURL fileURLWithPath:directoryString];
  173. NSError *error;
  174. if (![directoryURL checkResourceIsReachableAndReturnError:&error]) {
  175. // If fail creating the Application Support directory, return nil.
  176. if (![[NSFileManager defaultManager] createDirectoryAtURL:directoryURL
  177. withIntermediateDirectories:YES
  178. attributes:nil
  179. error:&error]) {
  180. GULLogWarning(kFIRCoreDiagnostics, YES, @"I-COR100001",
  181. @"Unable to create internal state storage: %@", error);
  182. return nil;
  183. }
  184. }
  185. return [directoryURL URLByAppendingPathComponent:fileName];
  186. }
  187. }
  188. #pragma mark - Metadata helpers
  189. /** Returns the model of iOS device. Sample platform strings are @"iPhone7,1" for iPhone 6 Plus,
  190. * @"iPhone7,2" for iPhone 6, etc. Refer to the Hardware strings at
  191. * https://en.wikipedia.org/wiki/List_of_iOS_devices
  192. *
  193. * @return The device model as an NSString.
  194. */
  195. + (NSString *)deviceModel {
  196. static NSString *deviceModel = nil;
  197. if (deviceModel == nil) {
  198. struct utsname systemInfo;
  199. uname(&systemInfo);
  200. deviceModel = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding];
  201. }
  202. return deviceModel;
  203. }
  204. #pragma mark - nanopb helper functions
  205. /** Mallocs a pb_bytes_array and copies the given NSString's bytes into the bytes array.
  206. *
  207. * @note Memory needs to be free manually, through pb_free or pb_release.
  208. * @param string The string to encode as pb_bytes.
  209. */
  210. pb_bytes_array_t *FIREncodeString(NSString *string) {
  211. NSData *stringBytes = [string dataUsingEncoding:NSUTF8StringEncoding];
  212. return FIREncodeData(stringBytes);
  213. }
  214. /** Mallocs a pb_bytes_array and copies the given NSData bytes into the bytes array.
  215. *
  216. * @note Memory needs to be free manually, through pb_free or pb_release.
  217. * @param data The data to copy into the new bytes array.
  218. */
  219. pb_bytes_array_t *FIREncodeData(NSData *data) {
  220. pb_bytes_array_t *pbBytes = malloc(PB_BYTES_ARRAY_T_ALLOCSIZE(data.length));
  221. memcpy(pbBytes->bytes, [data bytes], data.length);
  222. pbBytes->size = (pb_size_t)data.length;
  223. return pbBytes;
  224. }
  225. /** Maps a service string to the representative nanopb enum.
  226. *
  227. * @param serviceString The SDK service string to convert.
  228. * @return The representative nanopb enum.
  229. */
  230. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType FIRMapFromServiceStringToTypeEnum(
  231. NSString *serviceString) {
  232. static NSDictionary<NSString *, NSNumber *> *serviceStringToTypeEnum;
  233. if (serviceStringToTypeEnum == nil) {
  234. serviceStringToTypeEnum = @{
  235. kFIRServiceAdMob : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ADMOB),
  236. kFIRServiceMessaging : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MESSAGING),
  237. kFIRServiceMeasurement :
  238. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_MEASUREMENT),
  239. kFIRServiceRemoteConfig :
  240. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_REMOTE_CONFIG),
  241. kFIRServiceDatabase : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DATABASE),
  242. kFIRServiceDynamicLinks :
  243. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_DYNAMIC_LINKS),
  244. kFIRServiceAuth : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH),
  245. kFIRServiceAuthUI : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_AUTH_UI),
  246. kFIRServiceFirestore : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FIRESTORE),
  247. kFIRServiceFunctions : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_FUNCTIONS),
  248. kFIRServicePerformance :
  249. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_PERFORMANCE),
  250. kFIRServiceStorage : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_STORAGE),
  251. kFIRServiceMLVisionOnDeviceAutoML :
  252. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_AUTOML),
  253. kFIRServiceMLVisionOnDeviceFace :
  254. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_FACE),
  255. kFIRServiceMLVisionOnDeviceBarcode :
  256. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_BARCODE),
  257. kFIRServiceMLVisionOnDeviceText :
  258. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_TEXT),
  259. kFIRServiceMLVisionOnDeviceLabel :
  260. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_LABEL),
  261. kFIRServiceMLVisionOnDeviceObjectDetection : @(
  262. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_VISION_ON_DEVICE_OBJECT_DETECTION),
  263. kFIRServiceMLModelInterpreter :
  264. @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ML_MODEL_INTERPRETER),
  265. kGGLServiceAnalytics : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ANALYTICS),
  266. kGGLServiceSignIn : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_SIGN_IN),
  267. kFIRServiceIAM : @(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_IN_APP_MESSAGING),
  268. };
  269. }
  270. if (serviceStringToTypeEnum[serviceString] != nil) {
  271. return (int32_t)serviceStringToTypeEnum[serviceString].longLongValue;
  272. }
  273. return logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_UNKNOWN_SDK_SERVICE;
  274. }
  275. #pragma mark - Proto population functions
  276. /** Populates the given proto with data related to an SDK logDiagnostics call from the
  277. * diagnosticObjects dictionary.
  278. *
  279. * @param config The proto to populate
  280. * @param diagnosticObjects The dictionary of diagnostics objects.
  281. */
  282. void FIRPopulateProtoWithInfoFromUserInfoParams(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
  283. NSDictionary<NSString *, id> *diagnosticObjects) {
  284. NSNumber *configurationType = diagnosticObjects[kFIRCDConfigurationTypeKey];
  285. if (configurationType != nil) {
  286. switch (configurationType.integerValue) {
  287. case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE:
  288. config->configuration_type =
  289. logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_CORE;
  290. config->has_configuration_type = 1;
  291. break;
  292. case logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK:
  293. config->configuration_type =
  294. logs_proto_mobilesdk_ios_ICoreConfiguration_ConfigurationType_SDK;
  295. config->has_configuration_type = 1;
  296. break;
  297. default:
  298. break;
  299. }
  300. }
  301. NSString *sdkName = diagnosticObjects[kFIRCDSdkNameKey];
  302. if (sdkName) {
  303. config->sdk_name = FIRMapFromServiceStringToTypeEnum(sdkName);
  304. config->has_sdk_name = 1;
  305. }
  306. NSString *version = diagnosticObjects[kFIRCDSdkVersionKey];
  307. if (version) {
  308. config->sdk_version = FIREncodeString(version);
  309. }
  310. }
  311. /** Populates the given proto with data from the calling FIRApp using the given
  312. * diagnosticObjects dictionary.
  313. *
  314. * @param config The proto to populate
  315. * @param diagnosticObjects The dictionary of diagnostics objects.
  316. */
  317. void FIRPopulateProtoWithCommonInfoFromApp(logs_proto_mobilesdk_ios_ICoreConfiguration *config,
  318. NSDictionary<NSString *, id> *diagnosticObjects) {
  319. config->pod_name = logs_proto_mobilesdk_ios_ICoreConfiguration_PodName_FIREBASE;
  320. config->has_pod_name = 1;
  321. NSCAssert(diagnosticObjects[kFIRCDllAppsCountKey],
  322. @"App count is a required value in the data dict.");
  323. config->app_count = (int32_t)[diagnosticObjects[kFIRCDllAppsCountKey] integerValue];
  324. config->has_app_count = 1;
  325. NSString *googleAppID = diagnosticObjects[kFIRCDGoogleAppIDKey];
  326. if (googleAppID.length) {
  327. config->app_id = FIREncodeString(googleAppID);
  328. }
  329. NSString *bundleID = diagnosticObjects[kFIRCDBundleIDKey];
  330. if (bundleID.length) {
  331. config->bundle_id = FIREncodeString(bundleID);
  332. }
  333. NSString *firebaseUserAgent = diagnosticObjects[kFIRCDFirebaseUserAgentKey];
  334. if (firebaseUserAgent.length) {
  335. config->platform_info = FIREncodeString(firebaseUserAgent);
  336. }
  337. NSNumber *usingOptionsFromDefaultPlist = diagnosticObjects[kFIRCDUsingOptionsFromDefaultPlistKey];
  338. if (usingOptionsFromDefaultPlist != nil) {
  339. config->use_default_app = [usingOptionsFromDefaultPlist boolValue];
  340. config->has_use_default_app = 1;
  341. }
  342. NSString *libraryVersionID = diagnosticObjects[kFIRCDLibraryVersionIDKey];
  343. if (libraryVersionID) {
  344. config->icore_version = FIREncodeString(libraryVersionID);
  345. }
  346. NSString *deviceModel = [FIRCoreDiagnostics deviceModel];
  347. if (deviceModel.length) {
  348. config->device_model = FIREncodeString(deviceModel);
  349. }
  350. NSString *osVersion = [GULAppEnvironmentUtil systemVersion];
  351. if (osVersion.length) {
  352. config->os_version = FIREncodeString(osVersion);
  353. }
  354. config->using_zip_file = kUsingZipFile;
  355. config->has_using_zip_file = 1;
  356. config->deployment_type = kDeploymentType;
  357. config->has_deployment_type = 1;
  358. config->deployed_in_app_store = [GULAppEnvironmentUtil isFromAppStore];
  359. config->has_deployed_in_app_store = 1;
  360. }
  361. /** Populates the given proto with installed services data.
  362. *
  363. * @param config The proto to populate
  364. */
  365. void FIRPopulateProtoWithInstalledServices(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
  366. NSMutableArray<NSNumber *> *sdkServiceInstalledArray = [NSMutableArray array];
  367. // AdMob
  368. if (NSClassFromString(@"GADBannerView") != nil) {
  369. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAdMob))];
  370. }
  371. // CloudMessaging
  372. if (NSClassFromString(@"FIRMessaging") != nil) {
  373. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMessaging))];
  374. }
  375. // RemoteConfig
  376. if (NSClassFromString(@"FIRRemoteConfig") != nil) {
  377. [sdkServiceInstalledArray
  378. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceRemoteConfig))];
  379. }
  380. // Measurement/Analtyics
  381. if (NSClassFromString(@"FIRAnalytics") != nil) {
  382. [sdkServiceInstalledArray
  383. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMeasurement))];
  384. }
  385. // ML Vision On Device AutoML.
  386. if (NSClassFromString(@"FIRVisionOnDeviceAutoMLImageLabelerOptions") != nil) {
  387. [sdkServiceInstalledArray
  388. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceAutoML))];
  389. }
  390. // ML Vision On Device Face.
  391. if (NSClassFromString(@"FIRVisionFaceDetector") != nil &&
  392. NSClassFromString(@"GMVFaceDetector") != nil) {
  393. [sdkServiceInstalledArray
  394. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceFace))];
  395. }
  396. // ML Vision On Device Barcode.
  397. if (NSClassFromString(@"FIRVisionBarcodeDetector") != nil &&
  398. NSClassFromString(@"GMVBarcodeDetector") != nil) {
  399. [sdkServiceInstalledArray
  400. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceBarcode))];
  401. }
  402. // ML Vision On Device Text.
  403. if (NSClassFromString(@"FIRVisionTextDetector") != nil &&
  404. NSClassFromString(@"GMVTextDetector") != nil) {
  405. [sdkServiceInstalledArray
  406. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceText))];
  407. }
  408. // ML Vision On Device Image Label.
  409. if (NSClassFromString(@"FIRVisionLabelDetector") != nil &&
  410. NSClassFromString(@"GMVLabelDetector") != nil) {
  411. [sdkServiceInstalledArray
  412. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceLabel))];
  413. }
  414. // ML Vision On Device Object.
  415. if (NSClassFromString(@"FIRVisionObjectDetector") != nil) {
  416. [sdkServiceInstalledArray
  417. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLVisionOnDeviceObjectDetection))];
  418. }
  419. // ML Model Interpreter
  420. if (NSClassFromString(@"FIRCustomModelInterpreter") != nil) {
  421. [sdkServiceInstalledArray
  422. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceMLModelInterpreter))];
  423. }
  424. // Database
  425. if (NSClassFromString(@"FIRDatabase") != nil) {
  426. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDatabase))];
  427. }
  428. // DynamicDeepLink
  429. if (NSClassFromString(@"FIRDynamicLinks") != nil) {
  430. [sdkServiceInstalledArray
  431. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceDynamicLinks))];
  432. }
  433. // Auth
  434. if (NSClassFromString(@"FIRAuth") != nil) {
  435. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuth))];
  436. }
  437. // AuthUI
  438. if (NSClassFromString(@"FUIAuth") != nil) {
  439. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceAuthUI))];
  440. }
  441. // Firestore
  442. if (NSClassFromString(@"FIRFirestore") != nil) {
  443. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFirestore))];
  444. }
  445. // Functions
  446. if (NSClassFromString(@"FIRFunctions") != nil) {
  447. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceFunctions))];
  448. }
  449. // Performance
  450. if (NSClassFromString(@"FIRPerformance") != nil) {
  451. [sdkServiceInstalledArray
  452. addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServicePerformance))];
  453. }
  454. // Storage
  455. if (NSClassFromString(@"FIRStorage") != nil) {
  456. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceStorage))];
  457. }
  458. // SignIn via Google pod
  459. if (NSClassFromString(@"GIDSignIn") != nil && NSClassFromString(@"GGLContext") != nil) {
  460. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceSignIn))];
  461. }
  462. // Analytics via Google pod
  463. if (NSClassFromString(@"GAI") != nil && NSClassFromString(@"GGLContext") != nil) {
  464. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kGGLServiceAnalytics))];
  465. }
  466. // In-App Messaging
  467. if (NSClassFromString(@"FIRInAppMessaging") != nil) {
  468. [sdkServiceInstalledArray addObject:@(FIRMapFromServiceStringToTypeEnum(kFIRServiceIAM))];
  469. }
  470. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType *servicesInstalled =
  471. malloc(sizeof(logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType) *
  472. sdkServiceInstalledArray.count);
  473. for (NSUInteger i = 0; i < sdkServiceInstalledArray.count; i++) {
  474. NSNumber *typeEnum = sdkServiceInstalledArray[i];
  475. logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType serviceType =
  476. (int32_t)typeEnum.integerValue;
  477. servicesInstalled[i] = serviceType;
  478. }
  479. config->sdk_service_installed = servicesInstalled;
  480. config->sdk_service_installed_count = (int32_t)sdkServiceInstalledArray.count;
  481. }
  482. /** Populates the proto with the number of linked frameworks.
  483. *
  484. * @param config The proto to populate.
  485. */
  486. void FIRPopulateProtoWithNumberOfLinkedFrameworks(
  487. logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
  488. int numFrameworks = -1; // Subtract the app binary itself.
  489. unsigned int numImages;
  490. const char **imageNames = objc_copyImageNames(&numImages);
  491. for (unsigned int i = 0; i < numImages; i++) {
  492. NSString *imageName = [NSString stringWithUTF8String:imageNames[i]];
  493. if ([imageName rangeOfString:@"System/Library"].length != 0 // Apple .frameworks
  494. || [imageName rangeOfString:@"Developer/Library"].length != 0 // Xcode debug .frameworks
  495. || [imageName rangeOfString:@"usr/lib"].length != 0) { // Public .dylibs
  496. continue;
  497. }
  498. numFrameworks++;
  499. }
  500. free(imageNames);
  501. config->dynamic_framework_count = numFrameworks;
  502. config->has_dynamic_framework_count = 1;
  503. }
  504. /** Populates the proto with Info.plist values.
  505. *
  506. * @param config The proto to populate.
  507. */
  508. void FIRPopulateProtoWithInfoPlistValues(logs_proto_mobilesdk_ios_ICoreConfiguration *config) {
  509. NSDictionary<NSString *, id> *info = [[NSBundle mainBundle] infoDictionary];
  510. NSString *xcodeVersion = info[@"DTXcodeBuild"] ?: @"";
  511. NSString *sdkVersion = info[@"DTSDKBuild"] ?: @"";
  512. NSString *combinedVersions = [NSString stringWithFormat:@"%@-%@", xcodeVersion, sdkVersion];
  513. config->apple_framework_version = FIREncodeString(combinedVersions);
  514. NSString *minVersion = info[@"MinimumOSVersion"];
  515. if (minVersion) {
  516. config->min_supported_ios_version = FIREncodeString(minVersion);
  517. }
  518. // Apps can turn off swizzling in the Info.plist, check if they've explicitly set the value and
  519. // report it. It's enabled by default.
  520. NSNumber *appDelegateSwizzledNum = info[@"FirebaseAppDelegateProxyEnabled"];
  521. BOOL appDelegateSwizzled = YES;
  522. if ([appDelegateSwizzledNum isKindOfClass:[NSNumber class]]) {
  523. appDelegateSwizzled = [appDelegateSwizzledNum boolValue];
  524. }
  525. config->swizzling_enabled = appDelegateSwizzled;
  526. config->has_swizzling_enabled = 1;
  527. }
  528. #pragma mark - FIRCoreDiagnosticsInterop
  529. + (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
  530. FIRCoreDiagnostics *diagnostics = [FIRCoreDiagnostics sharedInstance];
  531. [diagnostics sendDiagnosticsData:diagnosticsData];
  532. }
  533. - (void)sendDiagnosticsData:(nonnull id<FIRCoreDiagnosticsData>)diagnosticsData {
  534. dispatch_async(self.diagnosticsQueue, ^{
  535. NSDictionary<NSString *, id> *diagnosticObjects = diagnosticsData.diagnosticObjects;
  536. NSNumber *isDataCollectionDefaultEnabled =
  537. diagnosticObjects[kFIRCDIsDataCollectionDefaultEnabledKey];
  538. if (isDataCollectionDefaultEnabled && ![isDataCollectionDefaultEnabled boolValue]) {
  539. return;
  540. }
  541. // Create the proto.
  542. logs_proto_mobilesdk_ios_ICoreConfiguration icore_config =
  543. logs_proto_mobilesdk_ios_ICoreConfiguration_init_default;
  544. icore_config.using_gdt = 1;
  545. icore_config.has_using_gdt = 1;
  546. // Populate the proto with information.
  547. FIRPopulateProtoWithInfoFromUserInfoParams(&icore_config, diagnosticObjects);
  548. FIRPopulateProtoWithCommonInfoFromApp(&icore_config, diagnosticObjects);
  549. FIRPopulateProtoWithInstalledServices(&icore_config);
  550. FIRPopulateProtoWithNumberOfLinkedFrameworks(&icore_config);
  551. FIRPopulateProtoWithInfoPlistValues(&icore_config);
  552. [self setHeartbeatFlagIfNeededToConfig:&icore_config];
  553. // This log object is capable of converting the proto to bytes.
  554. FIRCoreDiagnosticsLog *log = [[FIRCoreDiagnosticsLog alloc] initWithConfig:icore_config];
  555. // Send the log as a telemetry event.
  556. GDTEvent *event = [self.transport eventForTransport];
  557. event.dataObject = (id<GDTEventDataObject>)log;
  558. [self.transport sendTelemetryEvent:event];
  559. });
  560. }
  561. #pragma mark - Heartbeat
  562. - (void)setHeartbeatFlagIfNeededToConfig:(logs_proto_mobilesdk_ios_ICoreConfiguration *)config {
  563. // Check if need to send a heartbeat.
  564. NSDate *currentDate = [NSDate date];
  565. NSDate *lastCheckin = [self.heartbeatDateStorage date];
  566. if (lastCheckin) {
  567. // Ensure the previous checkin was on a different date in the past.
  568. if ([self isDate:currentDate inSameDayOrBeforeThan:lastCheckin]) {
  569. return;
  570. }
  571. }
  572. // Update heartbeat sent date.
  573. NSError *error;
  574. if (![self.heartbeatDateStorage setDate:currentDate error:&error]) {
  575. GULLogError(kFIRCoreDiagnostics, NO, @"I-COR100004", @"Unable to persist internal state: %@",
  576. error);
  577. }
  578. // Set the flag.
  579. config->sdk_name = logs_proto_mobilesdk_ios_ICoreConfiguration_ServiceType_ICORE;
  580. config->has_sdk_name = 1;
  581. }
  582. - (BOOL)isDate:(NSDate *)date1 inSameDayOrBeforeThan:(NSDate *)date2 {
  583. return [[NSCalendar currentCalendar] isDate:date1 inSameDayAsDate:date2] ||
  584. [date1 compare:date2] == NSOrderedAscending;
  585. }
  586. @end