FPRConfigurations.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. // Copyright 2020 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import <UIKit/UIKit.h>
  15. #import <GoogleUtilities/GULUserDefaults.h>
  16. #import "FirebasePerformance/Sources/Common/FPRConstants.h"
  17. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations+Private.h"
  18. #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
  19. #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags+Private.h"
  20. #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags.h"
  21. #import "FirebaseCore/Extension/FirebaseCoreInternal.h"
  22. FPRConfigName kFPRConfigDataCollectionEnabled = @"dataCollectionEnabled";
  23. FPRConfigName kFPRConfigInstrumentationEnabled = @"instrumentationEnabled";
  24. NSString *const kFPRConfigInstrumentationUserPreference =
  25. @"com.firebase.performanceInsrumentationEnabled";
  26. NSString *const kFPRConfigInstrumentationPlistKey = @"firebase_performance_instrumentation_enabled";
  27. NSString *const kFPRConfigCollectionUserPreference = @"com.firebase.performanceCollectionEnabled";
  28. NSString *const kFPRConfigCollectionPlistKey = @"firebase_performance_collection_enabled";
  29. NSString *const kFPRDiagnosticsUserPreference = @"FPRDiagnosticsLocal";
  30. NSString *const kFPRDiagnosticsEnabledPlistKey = @"FPRDiagnosticsLocal";
  31. NSString *const kFPRConfigCollectionDeactivationPlistKey =
  32. @"firebase_performance_collection_deactivated";
  33. NSString *const kFPRConfigLogSource = @"com.firebase.performanceLogSource";
  34. @implementation FPRConfigurations
  35. static dispatch_once_t gSharedInstanceToken;
  36. + (instancetype)sharedInstance {
  37. static FPRConfigurations *instance = nil;
  38. dispatch_once(&gSharedInstanceToken, ^{
  39. FPRConfigurationSource sources = FPRConfigurationSourceRemoteConfig;
  40. instance = [[FPRConfigurations alloc] initWithSources:sources];
  41. });
  42. return instance;
  43. }
  44. + (void)reset {
  45. // TODO(b/120032990): Reset the singletons that this singleton uses.
  46. gSharedInstanceToken = 0;
  47. [[GULUserDefaults standardUserDefaults]
  48. removeObjectForKey:kFPRConfigInstrumentationUserPreference];
  49. [[GULUserDefaults standardUserDefaults] removeObjectForKey:kFPRConfigCollectionUserPreference];
  50. }
  51. - (instancetype)initWithSources:(FPRConfigurationSource)source {
  52. self = [super init];
  53. if (self) {
  54. _sources = source;
  55. [self setupRemoteConfigFlags];
  56. // Register for notifications to update configs.
  57. [self registerForNotifications];
  58. self.FIRAppClass = [FIRApp class];
  59. self.userDefaults = [GULUserDefaults standardUserDefaults];
  60. self.infoDictionary = [NSBundle mainBundle].infoDictionary;
  61. self.mainBundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
  62. self.updateQueue = dispatch_queue_create("com.google.perf.configUpdate", DISPATCH_QUEUE_SERIAL);
  63. }
  64. return self;
  65. }
  66. - (void)registerForNotifications {
  67. [[NSNotificationCenter defaultCenter] addObserver:self
  68. selector:@selector(update)
  69. name:UIApplicationDidBecomeActiveNotification
  70. object:nil];
  71. }
  72. /** Searches the main bundle and the bundle from bundleForClass: info dictionaries for the key and
  73. * returns the first result.
  74. *
  75. * @param key The key to search the info dictionaries for.
  76. * @return The first object found in the info dictionary of the main bundle and bundleForClass:.
  77. */
  78. - (nullable id)objectForInfoDictionaryKey:(NSString *)key {
  79. // If the config infoDictionary has been set to a new dictionary, only use the original dictionary
  80. // instead of the new dictionary.
  81. if (self.infoDictionary != [NSBundle mainBundle].infoDictionary) {
  82. return self.infoDictionary[key]; // nullable.
  83. }
  84. NSArray<NSBundle *> *bundles = @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
  85. for (NSBundle *bundle in bundles) {
  86. id object = [bundle objectForInfoDictionaryKey:key];
  87. if (object) {
  88. return object; // nonnull.
  89. }
  90. }
  91. return nil;
  92. }
  93. - (void)update {
  94. dispatch_async(self.updateQueue, ^{
  95. if (!self.remoteConfigFlags) {
  96. [self setupRemoteConfigFlags];
  97. }
  98. [self.remoteConfigFlags update];
  99. });
  100. }
  101. /**
  102. * Sets up the remote config flags instance based on 3 different factors:
  103. * 1. Is the firebase app configured?
  104. * 2. Is the remote config source enabled?
  105. * 3. If the Remote Config flags instance exists already?
  106. */
  107. - (void)setupRemoteConfigFlags {
  108. if (!self.remoteConfigFlags && [self.FIRAppClass isDefaultAppConfigured] &&
  109. (self.sources & FPRConfigurationSourceRemoteConfig) == FPRConfigurationSourceRemoteConfig) {
  110. self.remoteConfigFlags = [FPRRemoteConfigFlags sharedInstance];
  111. }
  112. }
  113. #pragma mark - Overridden Properties
  114. - (void)setDataCollectionEnabled:(BOOL)dataCollectionEnabled {
  115. [self.userDefaults setBool:dataCollectionEnabled forKey:kFPRConfigCollectionUserPreference];
  116. }
  117. // The data collection flag is determined by this order:
  118. // 1. A plist flag for permanently disabling data collection
  119. // 2. The runtime flag (GULUserDefaults)
  120. // 3. A plist flag for enabling/disabling (overrideable)
  121. // 4. The global data collection switch from Core.
  122. - (BOOL)isDataCollectionEnabled {
  123. /**
  124. * Perf only works with the default app, so validate it exists then use the value from the global
  125. * data collection from the default app as the base value if no other values are set.
  126. */
  127. if (![self.FIRAppClass isDefaultAppConfigured]) {
  128. return NO;
  129. }
  130. BOOL dataCollectionPreference = [self.FIRAppClass defaultApp].isDataCollectionDefaultEnabled;
  131. // Check if data collection is permanently disabled by plist. If so, disable data collection.
  132. id dataCollectionDeactivationObject =
  133. [self objectForInfoDictionaryKey:kFPRConfigCollectionDeactivationPlistKey];
  134. if (dataCollectionDeactivationObject) {
  135. BOOL dataCollectionDeactivated = [dataCollectionDeactivationObject boolValue];
  136. if (dataCollectionDeactivated) {
  137. return NO;
  138. }
  139. }
  140. /**
  141. * Check if the performance collection preference key is available in GULUserDefaults.
  142. * If it exists - Just honor that and return that value.
  143. * If it does not exist - Check if firebase_performance_collection_enabled exists in Info.plist.
  144. * If it exists - honor that and return that value.
  145. * If not - return YES stating performance collection is enabled.
  146. */
  147. id dataCollectionPreferenceObject =
  148. [self.userDefaults objectForKey:kFPRConfigCollectionUserPreference];
  149. if (dataCollectionPreferenceObject) {
  150. dataCollectionPreference = [dataCollectionPreferenceObject boolValue];
  151. } else {
  152. dataCollectionPreferenceObject = [self objectForInfoDictionaryKey:kFPRConfigCollectionPlistKey];
  153. if (dataCollectionPreferenceObject) {
  154. dataCollectionPreference = [dataCollectionPreferenceObject boolValue];
  155. }
  156. }
  157. return dataCollectionPreference;
  158. }
  159. - (void)setInstrumentationEnabled:(BOOL)instrumentationEnabled {
  160. [self.userDefaults setBool:instrumentationEnabled forKey:kFPRConfigInstrumentationUserPreference];
  161. }
  162. - (BOOL)isInstrumentationEnabled {
  163. BOOL instrumentationPreference = YES;
  164. id instrumentationPreferenceObject =
  165. [self.userDefaults objectForKey:kFPRConfigInstrumentationUserPreference];
  166. /**
  167. * Check if the performance instrumentation preference key is available in GULUserDefaults.
  168. * If it exists - Just honor that and return that value.
  169. * If not - Check if firebase_performance_instrumentation_enabled exists in Info.plist.
  170. * If it exists - honor that and return that value.
  171. * If not - return YES stating performance instrumentation is enabled.
  172. */
  173. if (instrumentationPreferenceObject) {
  174. instrumentationPreference = [instrumentationPreferenceObject boolValue];
  175. } else {
  176. instrumentationPreferenceObject =
  177. [self objectForInfoDictionaryKey:kFPRConfigInstrumentationPlistKey];
  178. if (instrumentationPreferenceObject) {
  179. instrumentationPreference = [instrumentationPreferenceObject boolValue];
  180. }
  181. }
  182. return instrumentationPreference;
  183. }
  184. #pragma mark - Fireperf SDK configurations.
  185. - (BOOL)sdkEnabled {
  186. BOOL enabled = YES;
  187. if (self.remoteConfigFlags) {
  188. enabled = [self.remoteConfigFlags performanceSDKEnabledWithDefaultValue:enabled];
  189. }
  190. // Check if the current version is one of the disabled versions.
  191. if ([[self sdkDisabledVersions] containsObject:[NSString stringWithUTF8String:kFPRSDKVersion]]) {
  192. enabled = NO;
  193. }
  194. // If there is a plist override, honor that value.
  195. // NOTE: PList override should ideally be used only for tests and not for production.
  196. id plistObject = [self objectForInfoDictionaryKey:@"firebase_performance_sdk_enabled"];
  197. if (plistObject) {
  198. enabled = [plistObject boolValue];
  199. }
  200. return enabled;
  201. }
  202. - (BOOL)diagnosticsEnabled {
  203. BOOL enabled = NO;
  204. /**
  205. * Check if the diagnostics preference key is available in GULUserDefaults.
  206. * If it exists - Just honor that and return that value.
  207. * If not - Check if firebase_performance_instrumentation_enabled exists in Info.plist.
  208. * If it exists - honor that and return that value.
  209. * If not - return NO stating diagnostics is disabled.
  210. */
  211. id diagnosticsEnabledPreferenceObject =
  212. [self.userDefaults objectForKey:kFPRDiagnosticsUserPreference];
  213. if (diagnosticsEnabledPreferenceObject) {
  214. enabled = [diagnosticsEnabledPreferenceObject boolValue];
  215. } else {
  216. id diagnosticsEnabledObject = [self objectForInfoDictionaryKey:kFPRDiagnosticsEnabledPlistKey];
  217. if (diagnosticsEnabledObject) {
  218. enabled = [diagnosticsEnabledObject boolValue];
  219. }
  220. }
  221. return enabled;
  222. }
  223. - (NSSet<NSString *> *)sdkDisabledVersions {
  224. NSMutableSet<NSString *> *disabledVersions = [[NSMutableSet<NSString *> alloc] init];
  225. if (self.remoteConfigFlags) {
  226. NSSet<NSString *> *sdkDisabledVersions =
  227. [self.remoteConfigFlags sdkDisabledVersionsWithDefaultValue:[disabledVersions copy]];
  228. if (sdkDisabledVersions.count > 0) {
  229. [disabledVersions addObjectsFromArray:[sdkDisabledVersions allObjects]];
  230. }
  231. }
  232. return [disabledVersions copy];
  233. }
  234. - (int)logSource {
  235. /**
  236. * Order of preference of returning the log source.
  237. * If it is an autopush build (based on environment variable), always return
  238. * LogRequest_LogSource_FireperfAutopush (461). If there is a recent value of remote config fetch,
  239. * honor that value. If logSource cached value (GULUserDefaults value) exists, honor that.
  240. * Fallback to the default value LogRequest_LogSource_Fireperf (462).
  241. */
  242. int logSource = 462;
  243. NSDictionary<NSString *, NSString *> *environment = [NSProcessInfo processInfo].environment;
  244. if (environment[@"FPR_AUTOPUSH_ENV"] != nil &&
  245. [environment[@"FPR_AUTOPUSH_ENV"] isEqualToString:@"1"]) {
  246. logSource = 461;
  247. } else {
  248. if (self.remoteConfigFlags) {
  249. logSource = [self.remoteConfigFlags logSourceWithDefaultValue:462];
  250. }
  251. }
  252. return logSource;
  253. }
  254. - (PrewarmDetectionMode)prewarmDetectionMode {
  255. PrewarmDetectionMode mode = PrewarmDetectionModeActivePrewarm;
  256. if (self.remoteConfigFlags) {
  257. mode = [self.remoteConfigFlags getIntValueForFlag:@"fpr_prewarm_detection"
  258. defaultValue:(int)mode];
  259. }
  260. return mode;
  261. }
  262. #pragma mark - Log sampling configurations.
  263. - (float)logTraceSamplingRate {
  264. float samplingRate = 1.0f;
  265. if (self.remoteConfigFlags) {
  266. float rcSamplingRate = [self.remoteConfigFlags traceSamplingRateWithDefaultValue:samplingRate];
  267. if (rcSamplingRate >= 0) {
  268. samplingRate = rcSamplingRate;
  269. }
  270. }
  271. return samplingRate;
  272. }
  273. - (float)logNetworkSamplingRate {
  274. float samplingRate = 1.0f;
  275. if (self.remoteConfigFlags) {
  276. float rcSamplingRate =
  277. [self.remoteConfigFlags networkRequestSamplingRateWithDefaultValue:samplingRate];
  278. if (rcSamplingRate >= 0) {
  279. samplingRate = rcSamplingRate;
  280. }
  281. }
  282. return samplingRate;
  283. }
  284. #pragma mark - Traces rate limiting configurations.
  285. - (uint32_t)foregroundEventCount {
  286. uint32_t eventCount = 300;
  287. if (self.remoteConfigFlags) {
  288. eventCount =
  289. [self.remoteConfigFlags rateLimitTraceCountInForegroundWithDefaultValue:eventCount];
  290. }
  291. return eventCount;
  292. }
  293. - (uint32_t)foregroundEventTimeLimit {
  294. uint32_t timeLimit = 600;
  295. if (self.remoteConfigFlags) {
  296. timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
  297. }
  298. uint32_t timeLimitInMinutes = timeLimit / 60;
  299. return timeLimitInMinutes;
  300. }
  301. - (uint32_t)backgroundEventCount {
  302. uint32_t eventCount = 30;
  303. if (self.remoteConfigFlags) {
  304. eventCount =
  305. [self.remoteConfigFlags rateLimitTraceCountInBackgroundWithDefaultValue:eventCount];
  306. }
  307. return eventCount;
  308. }
  309. - (uint32_t)backgroundEventTimeLimit {
  310. uint32_t timeLimit = 600;
  311. if (self.remoteConfigFlags) {
  312. timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
  313. }
  314. uint32_t timeLimitInMinutes = timeLimit / 60;
  315. return timeLimitInMinutes;
  316. }
  317. #pragma mark - Network requests rate limiting configurations.
  318. - (uint32_t)foregroundNetworkEventCount {
  319. uint32_t eventCount = 700;
  320. if (self.remoteConfigFlags) {
  321. eventCount = [self.remoteConfigFlags
  322. rateLimitNetworkRequestCountInForegroundWithDefaultValue:eventCount];
  323. }
  324. return eventCount;
  325. }
  326. - (uint32_t)foregroundNetworkEventTimeLimit {
  327. uint32_t timeLimit = 600;
  328. if (self.remoteConfigFlags) {
  329. timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
  330. }
  331. uint32_t timeLimitInMinutes = timeLimit / 60;
  332. return timeLimitInMinutes;
  333. }
  334. - (uint32_t)backgroundNetworkEventCount {
  335. uint32_t eventCount = 70;
  336. if (self.remoteConfigFlags) {
  337. eventCount = [self.remoteConfigFlags
  338. rateLimitNetworkRequestCountInBackgroundWithDefaultValue:eventCount];
  339. }
  340. return eventCount;
  341. }
  342. - (uint32_t)backgroundNetworkEventTimeLimit {
  343. uint32_t timeLimit = 600;
  344. if (self.remoteConfigFlags) {
  345. timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
  346. }
  347. uint32_t timeLimitInMinutes = timeLimit / 60;
  348. return timeLimitInMinutes;
  349. }
  350. #pragma mark - Sessions feature related configurations.
  351. - (float_t)sessionsSamplingPercentage {
  352. float samplingPercentage = 1.0f; // One Percent.
  353. if (self.remoteConfigFlags) {
  354. float rcSamplingRate =
  355. [self.remoteConfigFlags sessionSamplingRateWithDefaultValue:(samplingPercentage / 100)];
  356. if (rcSamplingRate >= 0) {
  357. samplingPercentage = rcSamplingRate * 100;
  358. }
  359. }
  360. id plistObject = [self objectForInfoDictionaryKey:@"sessionsSamplingPercentage"];
  361. if (plistObject) {
  362. samplingPercentage = [plistObject floatValue];
  363. }
  364. return samplingPercentage;
  365. }
  366. - (uint32_t)maxSessionLengthInMinutes {
  367. uint32_t sessionLengthInMinutes = 240;
  368. if (self.remoteConfigFlags) {
  369. sessionLengthInMinutes =
  370. [self.remoteConfigFlags sessionMaxDurationWithDefaultValue:sessionLengthInMinutes];
  371. }
  372. // If the session max length gets set to 0, default it to 240 minutes.
  373. if (sessionLengthInMinutes == 0) {
  374. return 240;
  375. }
  376. return sessionLengthInMinutes;
  377. }
  378. - (uint32_t)cpuSamplingFrequencyInForegroundInMS {
  379. uint32_t samplingFrequency = 100;
  380. if (self.remoteConfigFlags) {
  381. samplingFrequency = [self.remoteConfigFlags
  382. sessionGaugeCPUCaptureFrequencyInForegroundWithDefaultValue:samplingFrequency];
  383. }
  384. return samplingFrequency;
  385. }
  386. - (uint32_t)cpuSamplingFrequencyInBackgroundInMS {
  387. uint32_t samplingFrequency = 0;
  388. if (self.remoteConfigFlags) {
  389. samplingFrequency = [self.remoteConfigFlags
  390. sessionGaugeCPUCaptureFrequencyInBackgroundWithDefaultValue:samplingFrequency];
  391. }
  392. return samplingFrequency;
  393. }
  394. - (uint32_t)memorySamplingFrequencyInForegroundInMS {
  395. uint32_t samplingFrequency = 100;
  396. if (self.remoteConfigFlags) {
  397. samplingFrequency = [self.remoteConfigFlags
  398. sessionGaugeMemoryCaptureFrequencyInForegroundWithDefaultValue:samplingFrequency];
  399. }
  400. return samplingFrequency;
  401. }
  402. - (uint32_t)memorySamplingFrequencyInBackgroundInMS {
  403. uint32_t samplingFrequency = 0;
  404. if (self.remoteConfigFlags) {
  405. samplingFrequency = [self.remoteConfigFlags
  406. sessionGaugeMemoryCaptureFrequencyInBackgroundWithDefaultValue:samplingFrequency];
  407. }
  408. return samplingFrequency;
  409. }
  410. @end