FIRCoreDiagnostics.m 27 KB

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