FIRCoreDiagnostics.m 27 KB

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