AutorÃa | Ultima modificación | Ver Log |
// 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;
}
- (PrewarmDetectionMode)prewarmDetectionMode {
PrewarmDetectionMode mode = PrewarmDetectionModeActivePrewarm;
if (self.remoteConfigFlags) {
mode = [self.remoteConfigFlags getIntValueForFlag:@"fpr_prewarm_detection"
defaultValue:(int)mode];
}
return mode;
}
#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;
}
@end