FIRCoreDiagnostics.m 26 KB

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