| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- // Copyright 2020 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #import <UIKit/UIKit.h>
- #import "FirebasePerformance/Sources/Common/FPRConstants.h"
- #import "FirebasePerformance/Sources/Configurations/FPRConfigurations+Private.h"
- #import "FirebasePerformance/Sources/Configurations/FPRConfigurations.h"
- #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags+Private.h"
- #import "FirebasePerformance/Sources/Configurations/FPRRemoteConfigFlags.h"
- #import "FirebaseCore/Sources/Private/FirebaseCoreInternal.h"
- FPRConfigName kFPRConfigDataCollectionEnabled = @"dataCollectionEnabled";
- FPRConfigName kFPRConfigInstrumentationEnabled = @"instrumentationEnabled";
- NSString *const kFPRConfigInstrumentationUserPreference =
- @"com.firebase.performanceInsrumentationEnabled";
- NSString *const kFPRConfigInstrumentationPlistKey = @"firebase_performance_instrumentation_enabled";
- NSString *const kFPRConfigCollectionUserPreference = @"com.firebase.performanceCollectionEnabled";
- NSString *const kFPRConfigCollectionPlistKey = @"firebase_performance_collection_enabled";
- NSString *const kFPRDiagnosticsUserPreference = @"FPRDiagnosticsLocal";
- NSString *const kFPRDiagnosticsEnabledPlistKey = @"FPRDiagnosticsLocal";
- NSString *const kFPRConfigCollectionDeactivationPlistKey =
- @"firebase_performance_collection_deactivated";
- NSString *const kFPRConfigLogSource = @"com.firebase.performanceLogSource";
- @implementation FPRConfigurations
- static dispatch_once_t gSharedInstanceToken;
- + (instancetype)sharedInstance {
- static FPRConfigurations *instance = nil;
- dispatch_once(&gSharedInstanceToken, ^{
- FPRConfigurationSource sources = FPRConfigurationSourceRemoteConfig;
- instance = [[FPRConfigurations alloc] initWithSources:sources];
- });
- return instance;
- }
- + (void)reset {
- // TODO(b/120032990): Reset the singletons that this singleton uses.
- gSharedInstanceToken = 0;
- [[NSUserDefaults standardUserDefaults]
- removeObjectForKey:kFPRConfigInstrumentationUserPreference];
- [[NSUserDefaults standardUserDefaults] removeObjectForKey:kFPRConfigCollectionUserPreference];
- }
- - (instancetype)initWithSources:(FPRConfigurationSource)source {
- self = [super init];
- if (self) {
- _sources = source;
- [self setupRemoteConfigFlags];
- // Register for notifications to update configs.
- [self registerForNotifications];
- self.FIRAppClass = [FIRApp class];
- self.userDefaults = [NSUserDefaults standardUserDefaults];
- self.infoDictionary = [NSBundle mainBundle].infoDictionary;
- self.mainBundleIdentifier = [NSBundle mainBundle].bundleIdentifier;
- self.updateQueue = dispatch_queue_create("com.google.perf.configUpdate", DISPATCH_QUEUE_SERIAL);
- }
- return self;
- }
- - (void)registerForNotifications {
- [[NSNotificationCenter defaultCenter] addObserver:self
- selector:@selector(update)
- name:UIApplicationDidBecomeActiveNotification
- object:nil];
- }
- /** Searches the main bundle and the bundle from bundleForClass: info dictionaries for the key and
- * returns the first result.
- *
- * @param key The key to search the info dictionaries for.
- * @return The first object found in the info dictionary of the main bundle and bundleForClass:.
- */
- - (nullable id)objectForInfoDictionaryKey:(NSString *)key {
- // If the config infoDictionary has been set to a new dictionary, only use the original dictionary
- // instead of the new dictionary.
- if (self.infoDictionary != [NSBundle mainBundle].infoDictionary) {
- return self.infoDictionary[key]; // nullable.
- }
- NSArray<NSBundle *> *bundles = @[ [NSBundle mainBundle], [NSBundle bundleForClass:[self class]] ];
- for (NSBundle *bundle in bundles) {
- id object = [bundle objectForInfoDictionaryKey:key];
- if (object) {
- return object; // nonnull.
- }
- }
- return nil;
- }
- - (void)update {
- dispatch_async(self.updateQueue, ^{
- if (!self.remoteConfigFlags) {
- [self setupRemoteConfigFlags];
- }
- [self.remoteConfigFlags update];
- });
- }
- /**
- * Sets up the remote config flags instance based on 3 different factors:
- * 1. Is the firebase app configured?
- * 2. Is the remote config source enabled?
- * 3. If the Remote Config flags instance exists already?
- */
- - (void)setupRemoteConfigFlags {
- if (!self.remoteConfigFlags && [self.FIRAppClass isDefaultAppConfigured] &&
- (self.sources & FPRConfigurationSourceRemoteConfig) == FPRConfigurationSourceRemoteConfig) {
- self.remoteConfigFlags = [FPRRemoteConfigFlags sharedInstance];
- }
- }
- #pragma mark - Overridden Properties
- - (void)setDataCollectionEnabled:(BOOL)dataCollectionEnabled {
- [self.userDefaults setBool:dataCollectionEnabled forKey:kFPRConfigCollectionUserPreference];
- }
- // The data collection flag is determined by this order:
- // 1. A plist flag for permanently disabling data collection
- // 2. The runtime flag (NSUserDefaults)
- // 3. A plist flag for enabling/disabling (overrideable)
- // 4. The global data collection switch from Core.
- - (BOOL)isDataCollectionEnabled {
- /**
- * Perf only works with the default app, so validate it exists then use the value from the global
- * data collection from the default app as the base value if no other values are set.
- */
- if (![self.FIRAppClass isDefaultAppConfigured]) {
- return NO;
- }
- BOOL dataCollectionPreference = [self.FIRAppClass defaultApp].isDataCollectionDefaultEnabled;
- // Check if data collection is permanently disabled by plist. If so, disable data collection.
- id dataCollectionDeactivationObject =
- [self objectForInfoDictionaryKey:kFPRConfigCollectionDeactivationPlistKey];
- if (dataCollectionDeactivationObject) {
- BOOL dataCollectionDeactivated = [dataCollectionDeactivationObject boolValue];
- if (dataCollectionDeactivated) {
- return NO;
- }
- }
- /**
- * Check if the performance collection preference key is available in NSUserDefaults.
- * If it exists - Just honor that and return that value.
- * If it does not exist - Check if firebase_performance_collection_enabled exists in Info.plist.
- * If it exists - honor that and return that value.
- * If not - return YES stating performance collection is enabled.
- */
- id dataCollectionPreferenceObject =
- [self.userDefaults objectForKey:kFPRConfigCollectionUserPreference];
- if (dataCollectionPreferenceObject) {
- dataCollectionPreference = [dataCollectionPreferenceObject boolValue];
- } else {
- dataCollectionPreferenceObject = [self objectForInfoDictionaryKey:kFPRConfigCollectionPlistKey];
- if (dataCollectionPreferenceObject) {
- dataCollectionPreference = [dataCollectionPreferenceObject boolValue];
- }
- }
- return dataCollectionPreference;
- }
- - (void)setInstrumentationEnabled:(BOOL)instrumentationEnabled {
- [self.userDefaults setBool:instrumentationEnabled forKey:kFPRConfigInstrumentationUserPreference];
- }
- - (BOOL)isInstrumentationEnabled {
- BOOL instrumentationPreference = YES;
- id instrumentationPreferenceObject =
- [self.userDefaults objectForKey:kFPRConfigInstrumentationUserPreference];
- /**
- * Check if the performance instrumentation preference key is available in NSUserDefaults.
- * If it exists - Just honor that and return that value.
- * If not - Check if firebase_performance_instrumentation_enabled exists in Info.plist.
- * If it exists - honor that and return that value.
- * If not - return YES stating performance instrumentation is enabled.
- */
- if (instrumentationPreferenceObject) {
- instrumentationPreference = [instrumentationPreferenceObject boolValue];
- } else {
- instrumentationPreferenceObject =
- [self objectForInfoDictionaryKey:kFPRConfigInstrumentationPlistKey];
- if (instrumentationPreferenceObject) {
- instrumentationPreference = [instrumentationPreferenceObject boolValue];
- }
- }
- return instrumentationPreference;
- }
- #pragma mark - Fireperf SDK configurations.
- - (BOOL)sdkEnabled {
- BOOL enabled = YES;
- if (self.remoteConfigFlags) {
- enabled = [self.remoteConfigFlags performanceSDKEnabledWithDefaultValue:enabled];
- }
- // Check if the current version is one of the disabled versions.
- if ([[self sdkDisabledVersions] containsObject:[NSString stringWithUTF8String:kFPRSDKVersion]]) {
- enabled = NO;
- }
- // If there is a plist override, honor that value.
- // NOTE: PList override should ideally be used only for tests and not for production.
- id plistObject = [self objectForInfoDictionaryKey:@"firebase_performance_sdk_enabled"];
- if (plistObject) {
- enabled = [plistObject boolValue];
- }
- return enabled;
- }
- - (BOOL)diagnosticsEnabled {
- BOOL enabled = NO;
- /**
- * Check if the diagnostics preference key is available in NSUserDefaults.
- * If it exists - Just honor that and return that value.
- * If not - Check if firebase_performance_instrumentation_enabled exists in Info.plist.
- * If it exists - honor that and return that value.
- * If not - return NO stating diagnostics is disabled.
- */
- id diagnosticsEnabledPreferenceObject =
- [self.userDefaults objectForKey:kFPRDiagnosticsUserPreference];
- if (diagnosticsEnabledPreferenceObject) {
- enabled = [diagnosticsEnabledPreferenceObject boolValue];
- } else {
- id diagnosticsEnabledObject = [self objectForInfoDictionaryKey:kFPRDiagnosticsEnabledPlistKey];
- if (diagnosticsEnabledObject) {
- enabled = [diagnosticsEnabledObject boolValue];
- }
- }
- return enabled;
- }
- - (NSSet<NSString *> *)sdkDisabledVersions {
- NSMutableSet<NSString *> *disabledVersions = [[NSMutableSet<NSString *> alloc] init];
- if (self.remoteConfigFlags) {
- NSSet<NSString *> *sdkDisabledVersions =
- [self.remoteConfigFlags sdkDisabledVersionsWithDefaultValue:[disabledVersions copy]];
- if (sdkDisabledVersions.count > 0) {
- [disabledVersions addObjectsFromArray:[sdkDisabledVersions allObjects]];
- }
- }
- return [disabledVersions copy];
- }
- - (int)logSource {
- /**
- * Order of preference of returning the log source.
- * If it is an autopush build (based on environment variable), always return
- * LogRequest_LogSource_FireperfAutopush (461). If there is a recent value of remote config fetch,
- * honor that value. If logSource cached value (NSUserDefaults value) exists, honor that. Fallback
- * to the default value LogRequest_LogSource_Fireperf (462).
- */
- int logSource = 462;
- NSDictionary<NSString *, NSString *> *environment = [NSProcessInfo processInfo].environment;
- if (environment[@"FPR_AUTOPUSH_ENV"] != nil &&
- [environment[@"FPR_AUTOPUSH_ENV"] isEqualToString:@"1"]) {
- logSource = 461;
- } else {
- if (self.remoteConfigFlags) {
- logSource = [self.remoteConfigFlags logSourceWithDefaultValue:462];
- }
- }
- return logSource;
- }
- #pragma mark - Log sampling configurations.
- - (float)logTraceSamplingRate {
- float samplingRate = 1.0f;
- if (self.remoteConfigFlags) {
- float rcSamplingRate = [self.remoteConfigFlags traceSamplingRateWithDefaultValue:samplingRate];
- if (rcSamplingRate >= 0) {
- samplingRate = rcSamplingRate;
- }
- }
- return samplingRate;
- }
- - (float)logNetworkSamplingRate {
- float samplingRate = 1.0f;
- if (self.remoteConfigFlags) {
- float rcSamplingRate =
- [self.remoteConfigFlags networkRequestSamplingRateWithDefaultValue:samplingRate];
- if (rcSamplingRate >= 0) {
- samplingRate = rcSamplingRate;
- }
- }
- return samplingRate;
- }
- #pragma mark - Traces rate limiting configurations.
- - (uint32_t)foregroundEventCount {
- uint32_t eventCount = 300;
- if (self.remoteConfigFlags) {
- eventCount =
- [self.remoteConfigFlags rateLimitTraceCountInForegroundWithDefaultValue:eventCount];
- }
- return eventCount;
- }
- - (uint32_t)foregroundEventTimeLimit {
- uint32_t timeLimit = 600;
- if (self.remoteConfigFlags) {
- timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
- }
- uint32_t timeLimitInMinutes = timeLimit / 60;
- return timeLimitInMinutes;
- }
- - (uint32_t)backgroundEventCount {
- uint32_t eventCount = 30;
- if (self.remoteConfigFlags) {
- eventCount =
- [self.remoteConfigFlags rateLimitTraceCountInBackgroundWithDefaultValue:eventCount];
- }
- return eventCount;
- }
- - (uint32_t)backgroundEventTimeLimit {
- uint32_t timeLimit = 600;
- if (self.remoteConfigFlags) {
- timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
- }
- uint32_t timeLimitInMinutes = timeLimit / 60;
- return timeLimitInMinutes;
- }
- #pragma mark - Network requests rate limiting configurations.
- - (uint32_t)foregroundNetworkEventCount {
- uint32_t eventCount = 700;
- if (self.remoteConfigFlags) {
- eventCount = [self.remoteConfigFlags
- rateLimitNetworkRequestCountInForegroundWithDefaultValue:eventCount];
- }
- return eventCount;
- }
- - (uint32_t)foregroundNetworkEventTimeLimit {
- uint32_t timeLimit = 600;
- if (self.remoteConfigFlags) {
- timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
- }
- uint32_t timeLimitInMinutes = timeLimit / 60;
- return timeLimitInMinutes;
- }
- - (uint32_t)backgroundNetworkEventCount {
- uint32_t eventCount = 70;
- if (self.remoteConfigFlags) {
- eventCount = [self.remoteConfigFlags
- rateLimitNetworkRequestCountInBackgroundWithDefaultValue:eventCount];
- }
- return eventCount;
- }
- - (uint32_t)backgroundNetworkEventTimeLimit {
- uint32_t timeLimit = 600;
- if (self.remoteConfigFlags) {
- timeLimit = [self.remoteConfigFlags rateLimitTimeDurationWithDefaultValue:timeLimit];
- }
- uint32_t timeLimitInMinutes = timeLimit / 60;
- return timeLimitInMinutes;
- }
- #pragma mark - Sessions feature related configurations.
- - (float_t)sessionsSamplingPercentage {
- float samplingPercentage = 1.0f; // One Percent.
- if (self.remoteConfigFlags) {
- float rcSamplingRate =
- [self.remoteConfigFlags sessionSamplingRateWithDefaultValue:(samplingPercentage / 100)];
- if (rcSamplingRate >= 0) {
- samplingPercentage = rcSamplingRate * 100;
- }
- }
- id plistObject = [self objectForInfoDictionaryKey:@"sessionsSamplingPercentage"];
- if (plistObject) {
- samplingPercentage = [plistObject floatValue];
- }
- return samplingPercentage;
- }
- - (uint32_t)maxSessionLengthInMinutes {
- uint32_t sessionLengthInMinutes = 240;
- if (self.remoteConfigFlags) {
- sessionLengthInMinutes =
- [self.remoteConfigFlags sessionMaxDurationWithDefaultValue:sessionLengthInMinutes];
- }
- // If the session max length gets set to 0, default it to 240 minutes.
- if (sessionLengthInMinutes == 0) {
- return 240;
- }
- return sessionLengthInMinutes;
- }
- - (uint32_t)cpuSamplingFrequencyInForegroundInMS {
- uint32_t samplingFrequency = 100;
- if (self.remoteConfigFlags) {
- samplingFrequency = [self.remoteConfigFlags
- sessionGaugeCPUCaptureFrequencyInForegroundWithDefaultValue:samplingFrequency];
- }
- return samplingFrequency;
- }
- - (uint32_t)cpuSamplingFrequencyInBackgroundInMS {
- uint32_t samplingFrequency = 0;
- if (self.remoteConfigFlags) {
- samplingFrequency = [self.remoteConfigFlags
- sessionGaugeCPUCaptureFrequencyInBackgroundWithDefaultValue:samplingFrequency];
- }
- return samplingFrequency;
- }
- - (uint32_t)memorySamplingFrequencyInForegroundInMS {
- uint32_t samplingFrequency = 100;
- if (self.remoteConfigFlags) {
- samplingFrequency = [self.remoteConfigFlags
- sessionGaugeMemoryCaptureFrequencyInForegroundWithDefaultValue:samplingFrequency];
- }
- return samplingFrequency;
- }
- - (uint32_t)memorySamplingFrequencyInBackgroundInMS {
- uint32_t samplingFrequency = 0;
- if (self.remoteConfigFlags) {
- samplingFrequency = [self.remoteConfigFlags
- sessionGaugeMemoryCaptureFrequencyInBackgroundWithDefaultValue:samplingFrequency];
- }
- return samplingFrequency;
- }
- #pragma mark - Google Data Transport related configurations.
- - (float_t)fllTransportPercentage {
- // Order of precedence is:
- //
- // Any RC config flags exists?
- // -> Yes
- // -> If Transport flag exists, honor the value (active rollout scenario)
- // -> Otherwise, send to Fll (deprecation scenario)
- // -> No
- // -> Send to clearcut (onboarding scenario)
- //
- // If a PList override also exists than that takes the priority
- // By default send to Clearcut
- float transportPercentage = 0.0f; // Range [0 - 100]
- if (self.remoteConfigFlags && [self.remoteConfigFlags containsRemoteConfigFlags]) {
- // If Transport flag exists, honor the value (active rollout scenario)
- // Otherwise, send to Fll (deprecation scenario)
- transportPercentage = [self.remoteConfigFlags fllTransportPercentageWithDefaultValue:100.0f];
- }
- // If a PList override also exists than that takes the priority
- id plistObject = [self objectForInfoDictionaryKey:@"fllTransportPercentage"];
- if (plistObject) {
- transportPercentage = [plistObject floatValue];
- }
- return transportPercentage;
- }
- @end
|