| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "components/previous_session_info/previous_session_info.h" |
| |
| #include <mach/mach.h> |
| |
| #import <UIKit/UIKit.h> |
| |
| #import "base/ios/ios_util.h" |
| #import "base/metrics/field_trial.h" |
| #include "base/strings/string_number_conversions.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "base/system/sys_info.h" |
| #import "base/time/time.h" |
| #import "base/timer/timer.h" |
| #import "components/previous_session_info/previous_session_info_private.h" |
| #import "components/variations/variations_crash_keys.h" |
| #import "components/version_info/version_info.h" |
| |
| using previous_session_info_constants::DeviceBatteryState; |
| using previous_session_info_constants::DeviceThermalState; |
| using previous_session_info_constants::kPreviousSessionInfoParamsPrefix; |
| |
| namespace { |
| |
| // Returns timestamp (in seconds since January 2001) when OS has started. |
| NSTimeInterval GetOSStartTimeIntervalSinceReferenceDate() { |
| return NSDate.timeIntervalSinceReferenceDate - |
| NSProcessInfo.processInfo.systemUptime; |
| } |
| |
| // Translates a UIDeviceBatteryState value to DeviceBatteryState value. |
| DeviceBatteryState GetBatteryStateFromUIDeviceBatteryState( |
| UIDeviceBatteryState device_battery_state) { |
| switch (device_battery_state) { |
| case UIDeviceBatteryStateUnknown: |
| return DeviceBatteryState::kUnknown; |
| case UIDeviceBatteryStateUnplugged: |
| return DeviceBatteryState::kUnplugged; |
| case UIDeviceBatteryStateCharging: |
| return DeviceBatteryState::kCharging; |
| case UIDeviceBatteryStateFull: |
| return DeviceBatteryState::kFull; |
| } |
| |
| return DeviceBatteryState::kUnknown; |
| } |
| |
| // Translates a NSProcessInfoThermalState value to DeviceThermalState value. |
| DeviceThermalState GetThermalStateFromNSProcessInfoThermalState( |
| NSProcessInfoThermalState process_info_thermal_state) { |
| switch (process_info_thermal_state) { |
| case NSProcessInfoThermalStateNominal: |
| return DeviceThermalState::kNominal; |
| case NSProcessInfoThermalStateFair: |
| return DeviceThermalState::kFair; |
| case NSProcessInfoThermalStateSerious: |
| return DeviceThermalState::kSerious; |
| case NSProcessInfoThermalStateCritical: |
| return DeviceThermalState::kCritical; |
| } |
| |
| return DeviceThermalState::kUnknown; |
| } |
| |
| // NSUserDefaults keys. |
| // - The (string) application version. |
| NSString* const kLastRanVersion = @"LastRanVersion"; |
| // - The (string) device language. |
| NSString* const kLastRanLanguage = @"LastRanLanguage"; |
| // - The (integer) available device storage, in kilobytes. |
| NSString* const kPreviousSessionInfoAvailableDeviceStorage = |
| @"PreviousSessionInfoAvailableDeviceStorage"; |
| // - The (float) battery charge level. |
| NSString* const kPreviousSessionInfoBatteryLevel = |
| @"PreviousSessionInfoBatteryLevel"; |
| // - The (integer) underlying value of the DeviceBatteryState enum representing |
| // the device battery state. |
| NSString* const kPreviousSessionInfoBatteryState = |
| @"PreviousSessionInfoBatteryState"; |
| // - The (Date) of the recording start. |
| NSString* const kPreviousSessionInfoStartTime = @"PreviousSessionInfoStartTime"; |
| // - The (Date) of the estimated end of the session. |
| NSString* const kPreviousSessionInfoEndTime = @"PreviousSessionInfoEndTime"; |
| // - The (string) OS version. |
| NSString* const kPreviousSessionInfoOSVersion = @"PreviousSessionInfoOSVersion"; |
| // - The (integer) underlying value of the DeviceThermalState enum representing |
| // the device thermal state. |
| NSString* const kPreviousSessionInfoThermalState = |
| @"PreviousSessionInfoThermalState"; |
| // - A (boolean) describing whether the last session received |
| // ApplicationWillTerminate Notification. |
| NSString* const kPreviousSessionInfoAppWillTerminate = |
| @"PreviousSessionInfoAppWillTerminate"; |
| |
| // Return a key prefixed with the params prefix. |
| NSString* ReportParamKey(NSString* key) { |
| return [NSString |
| stringWithFormat:@"%@%@", kPreviousSessionInfoParamsPrefix, key]; |
| } |
| |
| // Objective-C bridge to observe changes in the FieldTrialList. |
| class FieldTrialListObserverBridge : public base::FieldTrialList::Observer { |
| public: |
| explicit FieldTrialListObserverBridge() {} |
| |
| private: |
| FieldTrialListObserverBridge(const PreviousSessionInfo&) = delete; |
| FieldTrialListObserverBridge& operator=(const FieldTrialListObserverBridge&) = |
| delete; |
| |
| // base::FieldTrialList::Observer: |
| void OnFieldTrialGroupFinalized(const base::FieldTrial& trial, |
| const std::string& group_name) override { |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| variations::ExperimentListInfo info = variations::GetExperimentListInfo(); |
| |
| // Normally this call would go through -setReportParameterValue, which |
| // calls -setObject and -synchronize on NSUserDefaults. However, since |
| // this call is really noisy, just call setObject and skip synchronize. |
| [NSUserDefaults.standardUserDefaults |
| setObject:base::SysUTF8ToNSString( |
| base::NumberToString(info.num_experiments)) |
| forKey:ReportParamKey(@"num-experiments")]; |
| [NSUserDefaults.standardUserDefaults |
| setObject:base::SysUTF8ToNSString(info.experiment_list) |
| forKey:ReportParamKey(@"variations")]; |
| }); |
| } |
| }; |
| |
| } // namespace |
| |
| namespace previous_session_info_constants { |
| NSString* const kPreviousSessionInfoApplicationState = |
| @"PreviousSessionInfoApplicationState"; |
| NSString* const kDidSeeMemoryWarningShortlyBeforeTerminating = |
| @"DidSeeMemoryWarning"; |
| NSString* const kOSStartTime = @"OSStartTime"; |
| NSString* const kPreviousSessionInfoRestoringSession = |
| @"PreviousSessionInfoRestoringSession"; |
| NSString* const kPreviousSessionInfoParamsPrefix = |
| @"PreviousSessionInfoParams."; |
| NSString* const kPreviousSessionInfoMemoryFootprint = |
| @"PreviousSessionInfoMemoryFootprint"; |
| NSString* const kPreviousSessionInfoTabCount = @"PreviousSessionInfoTabCount"; |
| NSString* const kPreviousSessionInfoInactiveTabCount = |
| @"PreviousSessionInfoInactiveTabCount"; |
| NSString* const kPreviousSessionInfoOTRTabCount = |
| @"PreviousSessionInfoOTRTabCount"; |
| NSString* const kPreviousSessionInfoWarmStartCount = |
| @"PreviousSessionInfoWarmStartCount"; |
| } // namespace previous_session_info_constants |
| |
| @interface PreviousSessionInfo () { |
| // Observe updates to field trial list. |
| std::unique_ptr<FieldTrialListObserverBridge> _fieldTrialListObserver; |
| } |
| |
| // Whether beginRecordingCurrentSession was called. |
| @property(nonatomic, assign) BOOL didBeginRecordingCurrentSession; |
| |
| // Whether recording data is in progress. |
| @property(nonatomic, assign) BOOL recordingCurrentSession; |
| |
| // Used for setting and resetting kPreviousSessionInfoRestoringSession flag. |
| // Can be greater than one if multiple sessions are being restored in parallel. |
| @property(atomic, assign) int numberOfSessionsBeingRestored; |
| |
| // Redefined to be read-write. |
| @property(nonatomic, assign) NSInteger availableDeviceStorage; |
| @property(nonatomic, assign) float deviceBatteryLevel; |
| @property(nonatomic, assign) DeviceBatteryState deviceBatteryState; |
| @property(nonatomic, assign) DeviceThermalState deviceThermalState; |
| @property(nonatomic, assign) BOOL didSeeMemoryWarningShortlyBeforeTerminating; |
| @property(nonatomic, assign) BOOL isFirstSessionAfterUpgrade; |
| @property(nonatomic, assign) BOOL isFirstSessionAfterLanguageChange; |
| @property(nonatomic, assign) BOOL OSRestartedAfterPreviousSession; |
| @property(nonatomic, strong) NSString* OSVersion; |
| @property(nonatomic, strong) NSDate* sessionStartTime; |
| @property(nonatomic, strong) NSDate* sessionEndTime; |
| @property(nonatomic, assign) BOOL terminatedDuringSessionRestoration; |
| @property(atomic, copy) NSDictionary<NSString*, NSString*>* reportParameters; |
| @property(nonatomic, assign) NSInteger memoryFootprint; |
| @property(nonatomic, assign) BOOL applicationWillTerminateWasReceived; |
| @property(nonatomic, assign) NSInteger tabCount; |
| @property(nonatomic, assign) NSInteger inactiveTabCount; |
| @property(nonatomic, assign) NSInteger OTRTabCount; |
| @property(atomic, strong) NSString* breadcrumbs; |
| @property(nonatomic, assign) NSInteger warmStartCount; |
| |
| @end |
| |
| @implementation PreviousSessionInfo { |
| std::unique_ptr<UIApplicationState> _applicationState; |
| base::RepeatingTimer _memoryFootprintUpdateTimer; |
| } |
| |
| // Singleton PreviousSessionInfo. |
| static PreviousSessionInfo* gSharedInstance = nil; |
| |
| + (instancetype)sharedInstance { |
| if (!gSharedInstance) { |
| gSharedInstance = [[PreviousSessionInfo alloc] init]; |
| |
| // Load the persisted information. |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| |
| gSharedInstance->_applicationState.reset(); |
| if ([defaults objectForKey:previous_session_info_constants:: |
| kPreviousSessionInfoApplicationState]) { |
| gSharedInstance->_applicationState = std::make_unique<UIApplicationState>( |
| static_cast<UIApplicationState>([defaults |
| integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoApplicationState])); |
| } |
| |
| gSharedInstance.availableDeviceStorage = -1; |
| if ([defaults objectForKey:kPreviousSessionInfoAvailableDeviceStorage]) { |
| gSharedInstance.availableDeviceStorage = |
| [defaults integerForKey:kPreviousSessionInfoAvailableDeviceStorage]; |
| } |
| gSharedInstance.didSeeMemoryWarningShortlyBeforeTerminating = |
| [defaults boolForKey:previous_session_info_constants:: |
| kDidSeeMemoryWarningShortlyBeforeTerminating]; |
| gSharedInstance.deviceBatteryState = static_cast<DeviceBatteryState>( |
| [defaults integerForKey:kPreviousSessionInfoBatteryState]); |
| gSharedInstance.deviceBatteryLevel = |
| [defaults floatForKey:kPreviousSessionInfoBatteryLevel]; |
| gSharedInstance.deviceThermalState = static_cast<DeviceThermalState>( |
| [defaults integerForKey:kPreviousSessionInfoThermalState]); |
| gSharedInstance.sessionStartTime = |
| [defaults objectForKey:kPreviousSessionInfoStartTime]; |
| gSharedInstance.sessionEndTime = |
| [defaults objectForKey:kPreviousSessionInfoEndTime]; |
| |
| NSString* versionOfOSAtLastRun = |
| [defaults stringForKey:kPreviousSessionInfoOSVersion]; |
| gSharedInstance.OSVersion = versionOfOSAtLastRun; |
| |
| NSString* lastRanVersion = [defaults stringForKey:kLastRanVersion]; |
| NSString* currentVersion = |
| base::SysUTF8ToNSString(version_info::GetVersionNumber()); |
| gSharedInstance.isFirstSessionAfterUpgrade = |
| ![lastRanVersion isEqualToString:currentVersion]; |
| |
| NSTimeInterval lastSystemStartTime = |
| [defaults doubleForKey:previous_session_info_constants::kOSStartTime]; |
| |
| gSharedInstance.OSRestartedAfterPreviousSession = |
| // Allow 5 seconds variation to account for rounding error. |
| (abs(lastSystemStartTime - GetOSStartTimeIntervalSinceReferenceDate()) > |
| 5) && |
| // Ensure that previous session actually exists. |
| lastSystemStartTime; |
| |
| NSString* lastRanLanguage = [defaults stringForKey:kLastRanLanguage]; |
| NSString* currentLanguage = [[NSLocale preferredLanguages] firstObject]; |
| gSharedInstance.isFirstSessionAfterLanguageChange = |
| ![lastRanLanguage isEqualToString:currentLanguage]; |
| |
| gSharedInstance.terminatedDuringSessionRestoration = |
| [defaults boolForKey:previous_session_info_constants:: |
| kPreviousSessionInfoRestoringSession]; |
| |
| NSMutableDictionary* reportParameters = [[NSMutableDictionary alloc] init]; |
| NSUInteger prefix_length = kPreviousSessionInfoParamsPrefix.length; |
| for (NSString* key in [defaults dictionaryRepresentation].allKeys) { |
| if ([key hasPrefix:kPreviousSessionInfoParamsPrefix]) { |
| NSString* crash_key = [key substringFromIndex:prefix_length]; |
| reportParameters[crash_key] = [defaults stringForKey:key]; |
| [defaults removeObjectForKey:key]; |
| } |
| } |
| gSharedInstance.reportParameters = reportParameters; |
| |
| gSharedInstance.memoryFootprint = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoMemoryFootprint]; |
| |
| gSharedInstance.applicationWillTerminateWasReceived = |
| [defaults boolForKey:kPreviousSessionInfoAppWillTerminate]; |
| gSharedInstance.tabCount = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoTabCount]; |
| gSharedInstance.inactiveTabCount = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoInactiveTabCount]; |
| gSharedInstance.OTRTabCount = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoOTRTabCount]; |
| gSharedInstance.warmStartCount = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoWarmStartCount]; |
| } |
| return gSharedInstance; |
| } |
| |
| + (void)resetSharedInstanceForTesting { |
| gSharedInstance = nil; |
| } |
| |
| - (void)beginRecordingCurrentSession { |
| if (self.didBeginRecordingCurrentSession) |
| return; |
| self.didBeginRecordingCurrentSession = YES; |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| |
| // Set the current Chrome version. |
| NSString* currentVersion = |
| base::SysUTF8ToNSString(version_info::GetVersionNumber()); |
| [defaults setObject:currentVersion forKey:kLastRanVersion]; |
| |
| // Set the current OS start time. |
| [defaults setDouble:GetOSStartTimeIntervalSinceReferenceDate() |
| forKey:previous_session_info_constants::kOSStartTime]; |
| |
| // Set the current OS version. |
| NSString* currentOSVersion = |
| base::SysUTF8ToNSString(base::SysInfo::OperatingSystemVersion()); |
| [defaults setObject:currentOSVersion forKey:kPreviousSessionInfoOSVersion]; |
| |
| // Set the current language. |
| NSString* currentLanguage = [[NSLocale preferredLanguages] firstObject]; |
| [defaults setObject:(currentLanguage ?: @"") forKey:kLastRanLanguage]; |
| |
| // Clear the memory warning flag. |
| [defaults |
| removeObjectForKey:previous_session_info_constants:: |
| kDidSeeMemoryWarningShortlyBeforeTerminating]; |
| |
| [[NSUserDefaults standardUserDefaults] |
| removeObjectForKey:kPreviousSessionInfoAppWillTerminate]; |
| [[NSUserDefaults standardUserDefaults] |
| removeObjectForKey:kPreviousSessionInfoAvailableDeviceStorage]; |
| |
| [defaults setObject:[NSDate date] forKey:kPreviousSessionInfoStartTime]; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateApplicationState) |
| name:UIApplicationDidEnterBackgroundNotification |
| object:nil]; |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateApplicationState) |
| name:UIApplicationWillEnterForegroundNotification |
| object:nil]; |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(protectedDataWillBecomeUnavailable) |
| name:UIApplicationProtectedDataWillBecomeUnavailable |
| object:nil]; |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(protectedDataDidBecomeAvailable) |
| name:UIApplicationProtectedDataWillBecomeUnavailable |
| object:nil]; |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateApplicationState) |
| name:UIApplicationDidBecomeActiveNotification |
| object:nil]; |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateApplicationState) |
| name:UIApplicationWillResignActiveNotification |
| object:nil]; |
| |
| [UIDevice currentDevice].batteryMonitoringEnabled = YES; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateStoredBatteryLevel) |
| name:UIDeviceBatteryLevelDidChangeNotification |
| object:nil]; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateStoredBatteryState) |
| name:UIDeviceBatteryStateDidChangeNotification |
| object:nil]; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(updateStoredThermalState) |
| name:NSProcessInfoThermalStateDidChangeNotification |
| object:nil]; |
| |
| [[NSNotificationCenter defaultCenter] |
| addObserver:self |
| selector:@selector(applicationWillTerminate) |
| name:UIApplicationWillTerminateNotification |
| object:nil]; |
| |
| [self resumeRecordingCurrentSession]; |
| } |
| |
| - (void)beginRecordingFieldTrials { |
| _fieldTrialListObserver = std::make_unique<FieldTrialListObserverBridge>(); |
| bool success = |
| base::FieldTrialList::AddObserver(_fieldTrialListObserver.get()); |
| DCHECK(success); |
| } |
| |
| - (void)startRecordingMemoryFootprintWithInterval:(base::TimeDelta)interval { |
| _memoryFootprintUpdateTimer.Start(FROM_HERE, interval, base::BindRepeating(^{ |
| [self updateMemoryFootprint]; |
| })); |
| } |
| |
| - (void)stopRecordingMemoryFootprint { |
| _memoryFootprintUpdateTimer.Stop(); |
| } |
| - (void)resumeRecordingCurrentSession { |
| if (self.recordingCurrentSession) |
| return; |
| self.recordingCurrentSession = YES; |
| [self updateApplicationState]; |
| [self updateStoredBatteryLevel]; |
| [self updateStoredBatteryState]; |
| [self updateStoredThermalState]; |
| // Save critical state information for crash detection. |
| [[NSUserDefaults standardUserDefaults] synchronize]; |
| } |
| |
| - (void)pauseRecordingCurrentSession { |
| self.recordingCurrentSession = NO; |
| } |
| |
| - (void)protectedDataWillBecomeUnavailable { |
| [self pauseRecordingCurrentSession]; |
| } |
| |
| - (void)protectedDataDidBecomeAvailable { |
| [self resumeRecordingCurrentSession]; |
| } |
| |
| - (UIApplicationState*)applicationState { |
| return _applicationState.get(); |
| } |
| |
| - (void)updateSessionEndTime { |
| if (!self.recordingCurrentSession) |
| return; |
| [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] |
| forKey:kPreviousSessionInfoEndTime]; |
| } |
| |
| - (void)updateStoredBatteryLevel { |
| if (!self.recordingCurrentSession) |
| return; |
| [[NSUserDefaults standardUserDefaults] |
| setFloat:[UIDevice currentDevice].batteryLevel |
| forKey:kPreviousSessionInfoBatteryLevel]; |
| [self updateSessionEndTime]; |
| } |
| |
| - (void)updateApplicationState { |
| if (!self.recordingCurrentSession) |
| return; |
| [[NSUserDefaults standardUserDefaults] |
| setInteger:UIApplication.sharedApplication.applicationState |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoApplicationState]; |
| |
| [self updateSessionEndTime]; |
| } |
| |
| - (void)updateStoredBatteryState { |
| if (!self.recordingCurrentSession) |
| return; |
| UIDevice* device = [UIDevice currentDevice]; |
| // Translate value to an app defined enum as the system could change the |
| // underlying values of UIDeviceBatteryState between OS versions. |
| DeviceBatteryState batteryState = |
| GetBatteryStateFromUIDeviceBatteryState(device.batteryState); |
| NSInteger batteryStateValue = |
| static_cast<std::underlying_type<DeviceBatteryState>::type>(batteryState); |
| |
| [[NSUserDefaults standardUserDefaults] |
| setInteger:batteryStateValue |
| forKey:kPreviousSessionInfoBatteryState]; |
| |
| [self updateSessionEndTime]; |
| } |
| |
| - (void)updateStoredThermalState { |
| if (!self.recordingCurrentSession) |
| return; |
| NSProcessInfo* processInfo = [NSProcessInfo processInfo]; |
| // Translate value to an app defined enum as the system could change the |
| // underlying values of NSProcessInfoThermalState between OS versions. |
| DeviceThermalState thermalState = |
| GetThermalStateFromNSProcessInfoThermalState([processInfo thermalState]); |
| NSInteger thermalStateValue = |
| static_cast<std::underlying_type<DeviceThermalState>::type>(thermalState); |
| |
| [[NSUserDefaults standardUserDefaults] |
| setInteger:thermalStateValue |
| forKey:kPreviousSessionInfoThermalState]; |
| |
| [self updateSessionEndTime]; |
| } |
| |
| - (void)applicationWillTerminate { |
| [NSUserDefaults.standardUserDefaults |
| setBool:YES |
| forKey:kPreviousSessionInfoAppWillTerminate]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)updateMemoryFootprint { |
| if (!self.recordingCurrentSession) |
| return; |
| |
| task_vm_info taskInfoData; |
| mach_msg_type_number_t count = sizeof(task_vm_info) / sizeof(natural_t); |
| kern_return_t result = |
| task_info(mach_task_self(), TASK_VM_INFO, |
| reinterpret_cast<task_info_t>(&taskInfoData), &count); |
| if (result == KERN_SUCCESS) { |
| [NSUserDefaults.standardUserDefaults |
| setInteger:taskInfoData.phys_footprint |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoMemoryFootprint]; |
| [self updateSessionEndTime]; |
| } |
| } |
| |
| - (void)setMemoryWarningFlag { |
| if (!self.didBeginRecordingCurrentSession) |
| return; |
| |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| [defaults setBool:YES |
| forKey:previous_session_info_constants:: |
| kDidSeeMemoryWarningShortlyBeforeTerminating]; |
| // Save critical state information for crash detection. |
| [defaults synchronize]; |
| } |
| |
| - (void)resetMemoryWarningFlag { |
| if (!self.didBeginRecordingCurrentSession) |
| return; |
| |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| [defaults |
| removeObjectForKey:previous_session_info_constants:: |
| kDidSeeMemoryWarningShortlyBeforeTerminating]; |
| // Save critical state information for crash detection. |
| [defaults synchronize]; |
| } |
| |
| - (void)incrementWarmStartCount { |
| NSUserDefaults* defaults = NSUserDefaults.standardUserDefaults; |
| NSInteger warmStartCount = |
| [defaults integerForKey:previous_session_info_constants:: |
| kPreviousSessionInfoWarmStartCount]; |
| [defaults setInteger:warmStartCount + 1 |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoWarmStartCount]; |
| [defaults synchronize]; |
| } |
| |
| - (void)resetWarmStartCount { |
| [NSUserDefaults.standardUserDefaults |
| setInteger:0 |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoWarmStartCount]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (base::ScopedClosureRunner)startSessionRestoration { |
| if (self.numberOfSessionsBeingRestored == 0) { |
| [NSUserDefaults.standardUserDefaults |
| setBool:YES |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoRestoringSession]; |
| // Save critical state information for crash detection. |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| ++self.numberOfSessionsBeingRestored; |
| |
| return base::ScopedClosureRunner(base::BindOnce(^{ |
| --self.numberOfSessionsBeingRestored; |
| if (self.numberOfSessionsBeingRestored == 0) { |
| [self resetSessionRestorationFlag]; |
| } |
| })); |
| } |
| |
| - (void)resetSessionRestorationFlag { |
| gSharedInstance.terminatedDuringSessionRestoration = NO; |
| [NSUserDefaults.standardUserDefaults |
| removeObjectForKey:previous_session_info_constants:: |
| kPreviousSessionInfoRestoringSession]; |
| // Save critical state information for crash detection. |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)updateCurrentSessionTabCount:(NSInteger)count { |
| [NSUserDefaults.standardUserDefaults |
| setInteger:count |
| forKey:previous_session_info_constants::kPreviousSessionInfoTabCount]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)updateCurrentSessionInactiveTabCount:(NSInteger)count { |
| [NSUserDefaults.standardUserDefaults |
| setInteger:count |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoInactiveTabCount]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)updateCurrentSessionOTRTabCount:(NSInteger)count { |
| [NSUserDefaults.standardUserDefaults |
| setInteger:count |
| forKey:previous_session_info_constants:: |
| kPreviousSessionInfoOTRTabCount]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)setBreadcrumbsLog:(NSString*)breadcrumbs { |
| gSharedInstance.breadcrumbs = breadcrumbs; |
| } |
| |
| - (void)setReportParameterValue:(NSString*)value forKey:(NSString*)key { |
| if (![NSThread isMainThread]) { |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| [self setReportParameterValue:value forKey:key]; |
| }); |
| return; |
| } |
| DCHECK([NSThread isMainThread]); |
| // Previously this logic would read and write an NSDictionary, but it lead to |
| // crashes within the NSUserDefaults logic. Instead, write a separate defaults |
| // entry for each key. |
| [NSUserDefaults.standardUserDefaults setObject:value |
| forKey:ReportParamKey(key)]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| - (void)setReportParameterURL:(const GURL&)URL forKey:(NSString*)key { |
| // Store only URL origin (not whole URL spec) as requested by Privacy Team. |
| [self |
| setReportParameterValue:base::SysUTF8ToNSString( |
| URL.DeprecatedGetOriginAsURL().spec().c_str()) |
| forKey:key]; |
| } |
| |
| - (void)removeReportParameterForKey:(NSString*)key { |
| if (![NSThread isMainThread]) { |
| dispatch_async(dispatch_get_main_queue(), ^{ |
| [self removeReportParameterForKey:key]; |
| }); |
| return; |
| } |
| DCHECK([NSThread isMainThread]); |
| [NSUserDefaults.standardUserDefaults removeObjectForKey:ReportParamKey(key)]; |
| [NSUserDefaults.standardUserDefaults synchronize]; |
| } |
| |
| @end |