| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/app/application_delegate/metrics_mediator.h" |
| |
| #include <sys/sysctl.h> |
| |
| #include "base/bind.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/user_metrics_action.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/task/post_task.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/time.h" |
| #include "build/branding_buildflags.h" |
| #include "components/crash/core/common/crash_keys.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/metrics/metrics_service.h" |
| #include "components/prefs/pref_service.h" |
| #import "components/previous_session_info/previous_session_info.h" |
| #include "components/ukm/ios/ukm_reporting_ios_util.h" |
| #import "ios/chrome/app/application_delegate/metric_kit_subscriber.h" |
| #import "ios/chrome/app/application_delegate/startup_information.h" |
| #include "ios/chrome/browser/application_context.h" |
| #include "ios/chrome/browser/chrome_url_constants.h" |
| #include "ios/chrome/browser/crash_report/crash_helper.h" |
| #include "ios/chrome/browser/main/browser.h" |
| #include "ios/chrome/browser/metrics/first_user_action_recorder.h" |
| #import "ios/chrome/browser/net/connection_type_observer_bridge.h" |
| #include "ios/chrome/browser/pref_names.h" |
| #include "ios/chrome/browser/system_flags.h" |
| #import "ios/chrome/browser/ui/browser_view/browser_view_controller.h" |
| #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h" |
| #import "ios/chrome/browser/ui/main/browser_interface_provider.h" |
| #import "ios/chrome/browser/ui/main/connection_information.h" |
| #import "ios/chrome/browser/ui/main/scene_state.h" |
| #import "ios/chrome/browser/ui/ntp/ntp_util.h" |
| #import "ios/chrome/browser/web_state_list/web_state_list.h" |
| #include "ios/chrome/browser/widget_kit/features.h" |
| #include "ios/chrome/common/app_group/app_group_metrics.h" |
| #include "ios/chrome/common/app_group/app_group_metrics_mainapp.h" |
| #import "ios/chrome/common/credential_provider/constants.h" |
| #include "ios/public/provider/chrome/browser/app_distribution/app_distribution_api.h" |
| #include "ios/web/public/thread/web_task_traits.h" |
| #include "ios/web/public/thread/web_thread.h" |
| #import "ios/web/public/web_state.h" |
| #include "url/gurl.h" |
| |
| #if BUILDFLAG(ENABLE_WIDGET_KIT_EXTENSION) |
| #import "ios/chrome/browser/widget_kit/widget_metrics_util.h" // nogncheck |
| #endif |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| // The key to a NSUserDefaults entry logging the number of times classes are |
| // loaded before a scene is attached. |
| NSString* const kLoadTimePreferenceKey = @"LoadTimePreferenceKey"; |
| |
| // The time when Objective C objects are loaded. |
| base::TimeTicks g_load_time; |
| |
| // The amount of time (in seconds) to wait for the user to start a new task. |
| const NSTimeInterval kFirstUserActionTimeout = 30.0; |
| |
| // Histograms fired in extensions that need to be re-fired from the main app. |
| const metrics_mediator::HistogramNameCountPair kHistogramsFromExtension[] = { |
| { |
| @"IOS.CredentialExtension.PasswordCreated", |
| static_cast<int>(CPEPasswordCreated::kMaxValue) + 1, |
| }, |
| { |
| @"IOS.CredentialExtension.NewCredentialUsername", |
| static_cast<int>(CPENewCredentialUsername::kMaxValue) + 1, |
| }}; |
| |
| // Returns time delta since app launch as retrieved from kernel info about |
| // the current process. |
| base::TimeDelta TimeDeltaSinceAppLaunchFromProcess() { |
| struct kinfo_proc info; |
| size_t length = sizeof(struct kinfo_proc); |
| int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, (int)getpid()}; |
| const int kr = sysctl(mib, base::size(mib), &info, &length, nullptr, 0); |
| DCHECK_EQ(KERN_SUCCESS, kr); |
| |
| const struct timeval time = info.kp_proc.p_starttime; |
| const NSTimeInterval time_since_1970 = |
| time.tv_sec + (time.tv_usec / (double)USEC_PER_SEC); |
| NSDate* date = [NSDate dateWithTimeIntervalSince1970:time_since_1970]; |
| return base::Seconds(-date.timeIntervalSinceNow); |
| } |
| } // namespace |
| |
| // A class to log the "load" time in uma. |
| @interface ObjectLoadTimeLogger : NSObject |
| @end |
| |
| @implementation ObjectLoadTimeLogger |
| + (void)load { |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| [defaults setInteger:[defaults integerForKey:kLoadTimePreferenceKey] + 1 |
| forKey:kLoadTimePreferenceKey]; |
| g_load_time = base::TimeTicks::Now(); |
| } |
| @end |
| |
| namespace metrics_mediator { |
| NSString* const kAppEnteredBackgroundDateKey = @"kAppEnteredBackgroundDate"; |
| |
| void RecordWidgetUsage(base::span<const HistogramNameCountPair> histograms) { |
| using base::SysNSStringToUTF8; |
| |
| // Dictionary containing the respective metric for each NSUserDefault's key. |
| NSDictionary<NSString*, NSString*>* keyMetric = @{ |
| app_group:: |
| kContentExtensionDisplayCount : @"IOS.ContentExtension.DisplayCount", |
| app_group:: |
| kSearchExtensionDisplayCount : @"IOS.SearchExtension.DisplayCount", |
| app_group:: |
| kCredentialExtensionDisplayCount : @"IOS.CredentialExtension.DisplayCount", |
| app_group:: |
| kCredentialExtensionReauthCount : @"IOS.CredentialExtension.ReauthCount", |
| app_group:: |
| kCredentialExtensionCopyURLCount : @"IOS.CredentialExtension.CopyURLCount", |
| app_group::kCredentialExtensionCopyUsernameCount : |
| @"IOS.CredentialExtension.CopyUsernameCount", |
| app_group::kCredentialExtensionCopyPasswordCount : |
| @"IOS.CredentialExtension.CopyPasswordCount", |
| app_group::kCredentialExtensionShowPasswordCount : |
| @"IOS.CredentialExtension.ShowPasswordCount", |
| app_group:: |
| kCredentialExtensionSearchCount : @"IOS.CredentialExtension.SearchCount", |
| app_group::kCredentialExtensionPasswordUseCount : |
| @"IOS.CredentialExtension.PasswordUseCount", |
| app_group::kCredentialExtensionQuickPasswordUseCount : |
| @"IOS.CredentialExtension.QuickPasswordUseCount", |
| app_group::kCredentialExtensionFetchPasswordFailureCount : |
| @"IOS.CredentialExtension.FetchPasswordFailure", |
| app_group::kCredentialExtensionFetchPasswordNilArgumentCount : |
| @"IOS.CredentialExtension.FetchPasswordNilArgument", |
| app_group::kCredentialExtensionKeychainSavePasswordFailureCount : |
| @"IOS.CredentialExtension.KeychainSavePasswordFailureCount", |
| app_group::kCredentialExtensionSaveCredentialFailureCount : |
| @"IOS.CredentialExtension.SaveCredentialFailureCount", |
| }; |
| |
| NSUserDefaults* shared_defaults = app_group::GetGroupUserDefaults(); |
| for (NSString* key in keyMetric) { |
| int count = [shared_defaults integerForKey:key]; |
| if (count != 0) { |
| base::UmaHistogramCounts1000(SysNSStringToUTF8(keyMetric[key]), count); |
| [shared_defaults setInteger:0 forKey:key]; |
| if ([key isEqual:app_group::kCredentialExtensionPasswordUseCount] || |
| [key isEqual:app_group::kCredentialExtensionQuickPasswordUseCount]) { |
| LogLikelyInterestedDefaultBrowserUserActivity( |
| DefaultPromoTypeMadeForIOS); |
| } |
| } |
| } |
| |
| for (const HistogramNameCountPair& pair : histograms) { |
| int maxSamples = pair.buckets; |
| // Check each possible bucket to see if it has any events to emit. |
| for (int bucket = 0; bucket < maxSamples; ++bucket) { |
| NSString* key = app_group::HistogramCountKey(pair.name, bucket); |
| int count = [shared_defaults integerForKey:key]; |
| if (count != 0) { |
| [shared_defaults setInteger:0 forKey:key]; |
| std::string histogramName = SysNSStringToUTF8(pair.name); |
| for (int emitCount = 0; emitCount < count; ++emitCount) { |
| base::UmaHistogramExactLinear(histogramName, bucket, maxSamples + 1); |
| } |
| } |
| } |
| } |
| } |
| } // namespace metrics_mediator |
| |
| using metrics_mediator::kAppEnteredBackgroundDateKey; |
| |
| @interface MetricsMediator ()<CRConnectionTypeObserverBridge> { |
| // Whether or not the crash reports present at startup have been processed to |
| // determine if the last app lifetime ended in an OOM crash. |
| BOOL _hasProcessedCrashReportsPresentAtStartup; |
| |
| // Observer for the connection type. Contains a valid object only if the |
| // metrics setting is set to wifi-only. |
| std::unique_ptr<ConnectionTypeObserverBridge> _connectionTypeObserverBridge; |
| } |
| |
| // Starts or stops metrics recording and/or uploading. |
| - (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading; |
| // Sets variables needed by the app_group application to collect UMA data. |
| // Process the pending logs produced by extensions. |
| // Called on start (cold and warm) and UMA settings change to update the |
| // collecting settings in extensions. |
| - (void)setAppGroupMetricsEnabled:(BOOL)enabled; |
| // Processes crash reports present at startup. |
| - (void)processCrashReportsPresentAtStartup; |
| // Starts or stops crash recording and/or uploading. |
| - (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading; |
| // Starts or stops watching for wwan events. |
| - (void)setWatchWWANEnabled:(BOOL)enabled; |
| // Enable/disable transmission of accumulated logs and crash reports (dumps). |
| - (void)setReporting:(BOOL)enableReporting; |
| // Enable/Disable uploading crash reports. |
| - (void)setBreakpadUploadingEnabled:(BOOL)enableUploading; |
| // Returns YES if the metrics are enabled and the reporting is wifi-only. |
| - (BOOL)isMetricsReportingEnabledWifiOnly; |
| // Update metrics prefs on a permission (opt-in/out) change. When opting out, |
| // this clears various client ids. When opting in, this resets saving crash |
| // prefs, so as not to trigger upload of various stale data. |
| // Mirrors the function in metrics_reporting_state.cc. |
| - (void)updateMetricsPrefsOnPermissionChange:(BOOL)enabled; |
| // Logs the number of tabs with UMAHistogramCount100 and allows testing. |
| + (void)recordNumTabAtStartup:(int)numTabs; |
| // Logs the number of tabs with UMAHistogramCount100 and allows testing. |
| + (void)recordNumTabAtResume:(int)numTabs; |
| // Logs the number of NTP tabs with UMAHistogramCount100 and allows testing. |
| + (void)recordNumNTPTabAtStartup:(int)numTabs; |
| // Logs the number of NTP tabs with UMAHistogramCount100 and allows testing. |
| + (void)recordNumNTPTabAtResume:(int)numTabs; |
| // Logs the number of live NTP tabs with UMAHistogramCount100 and allows |
| // testing. |
| + (void)recordNumLiveNTPTabAtResume:(int)numTabs; |
| |
| @end |
| |
| @implementation MetricsMediator |
| |
| #pragma mark - Public methods. |
| |
| + (void)logStartupDuration:(id<StartupInformation>)startupInformation |
| connectionInformation:(id<ConnectionInformation>)connectionInformation { |
| if (![startupInformation isColdStart]) |
| return; |
| |
| base::TimeTicks now = base::TimeTicks::Now(); |
| const base::TimeDelta processStartToNowTime = |
| TimeDeltaSinceAppLaunchFromProcess(); |
| const base::TimeDelta loadToNowTime = now - g_load_time; |
| const base::TimeDelta mainToNowTime = |
| now - [startupInformation appLaunchTime]; |
| const base::TimeDelta didFinishLaunchingToNowTime = |
| now - [startupInformation didFinishLaunchingTime]; |
| const base::TimeDelta sceneConnectionToNowTime = |
| now - [startupInformation firstSceneConnectionTime]; |
| |
| NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; |
| int consecutiveLoads = [defaults integerForKey:kLoadTimePreferenceKey]; |
| [defaults removeObjectForKey:kLoadTimePreferenceKey]; |
| |
| base::UmaHistogramTimes("Startup.ColdStartFromProcessCreationTimeV2", |
| processStartToNowTime); |
| base::UmaHistogramTimes("Startup.TimeFromProcessCreationToLoad", |
| processStartToNowTime - loadToNowTime); |
| base::UmaHistogramTimes("Startup.TimeFromProcessCreationToMainCall", |
| processStartToNowTime - mainToNowTime); |
| base::UmaHistogramTimes( |
| "Startup.TimeFromProcessCreationToDidFinishLaunchingCall", |
| processStartToNowTime - didFinishLaunchingToNowTime); |
| base::UmaHistogramTimes("Startup.TimeFromProcessCreationToSceneConnection", |
| processStartToNowTime - sceneConnectionToNowTime); |
| base::UmaHistogramCounts100("Startup.ConsecutiveLoadsWithoutLaunch", |
| consecutiveLoads); |
| |
| if ([connectionInformation startupParameters]) { |
| base::UmaHistogramTimes("Startup.ColdStartWithExternalURLTime", |
| mainToNowTime); |
| } else { |
| base::UmaHistogramTimes("Startup.ColdStartWithoutExternalURLTime", |
| mainToNowTime); |
| } |
| } |
| |
| + (void)logDateInUserDefaults { |
| [[NSUserDefaults standardUserDefaults] |
| setObject:[NSDate date] |
| forKey:metrics_mediator::kAppEnteredBackgroundDateKey]; |
| } |
| |
| + (void)logLaunchMetricsWithStartupInformation: |
| (id<StartupInformation>)startupInformation |
| connectedScenes:(NSArray<SceneState*>*)scenes { |
| RecordAndResetUkmLogSizeOnSuccessCounter(); |
| |
| int numTabs = 0; |
| int numNTPTabs = 0; |
| int numLiveNTPTabs = 0; |
| for (SceneState* scene in scenes) { |
| if (!scene.interfaceProvider) { |
| // The scene might not yet be initiated. |
| // TODO(crbug.com/1064611): This will not be an issue when the tabs are |
| // counted in sessions instead of scenes. |
| continue; |
| } |
| |
| const WebStateList* web_state_list = |
| scene.interfaceProvider.mainInterface.browser->GetWebStateList(); |
| numTabs += web_state_list->count(); |
| for (int i = 0; i < web_state_list->count(); i++) { |
| if (IsURLNewTabPage(web_state_list->GetWebStateAt(i)->GetVisibleURL())) { |
| numNTPTabs++; |
| } |
| } |
| BrowserViewController* bvc = scene.interfaceProvider.currentInterface.bvc; |
| numLiveNTPTabs += [bvc liveNTPCount]; |
| } |
| |
| if (startupInformation.isColdStart) { |
| [self recordNumTabAtStartup:numTabs]; |
| [self recordNumNTPTabAtStartup:numNTPTabs]; |
| } else { |
| [self recordNumTabAtResume:numTabs]; |
| [self recordNumNTPTabAtResume:numNTPTabs]; |
| // Only log at resume since there are likely no live NTPs on startup. |
| [self recordNumLiveNTPTabAtResume:numLiveNTPTabs]; |
| } |
| |
| if (UIAccessibilityIsVoiceOverRunning()) { |
| base::RecordAction( |
| base::UserMetricsAction("MobileVoiceOverActiveOnLaunch")); |
| } |
| |
| #if BUILDFLAG(ENABLE_WIDGET_KIT_EXTENSION) |
| [WidgetMetricsUtil logInstalledWidgets]; |
| |
| #endif |
| |
| // Create the first user action recorder and schedule a task to expire it |
| // after some timeout. If unable to determine the last time the app entered |
| // the background (i.e. either first run or restore after crash), don't bother |
| // recording the first user action since fresh start wouldn't be triggered. |
| NSDate* lastAppClose = [[NSUserDefaults standardUserDefaults] |
| objectForKey:kAppEnteredBackgroundDateKey]; |
| if (lastAppClose) { |
| NSTimeInterval interval = -[lastAppClose timeIntervalSinceNow]; |
| [startupInformation |
| activateFirstUserActionRecorderWithBackgroundTime:interval]; |
| |
| SceneState* activeScene = nil; |
| for (SceneState* scene in scenes) { |
| if (scene.activationLevel == SceneActivationLevelForegroundActive) { |
| activeScene = scene; |
| break; |
| } |
| } |
| |
| if (activeScene) { |
| web::WebState* currentWebState = |
| activeScene.interfaceProvider.currentInterface.browser |
| ->GetWebStateList() |
| ->GetActiveWebState(); |
| if (currentWebState && |
| currentWebState->GetLastCommittedURL() == kChromeUINewTabURL) { |
| startupInformation.firstUserActionRecorder->RecordStartOnNTP(); |
| [startupInformation resetFirstUserActionRecorder]; |
| } else { |
| [startupInformation |
| expireFirstUserActionRecorderAfterDelay:kFirstUserActionTimeout]; |
| } |
| } |
| // Remove the value so it's not reused if the app crashes. |
| [[NSUserDefaults standardUserDefaults] |
| removeObjectForKey:kAppEnteredBackgroundDateKey]; |
| } |
| } |
| |
| - (void)updateMetricsStateBasedOnPrefsUserTriggered:(BOOL)isUserTriggered { |
| BOOL optIn = [self areMetricsEnabled]; |
| BOOL allowUploading = [self isUploadingEnabled]; |
| if (isUserTriggered) |
| [self updateMetricsPrefsOnPermissionChange:optIn]; |
| [self setMetricsEnabled:optIn withUploading:allowUploading]; |
| [self setBreakpadEnabled:optIn withUploading:allowUploading]; |
| [self setWatchWWANEnabled:optIn]; |
| [self setAppGroupMetricsEnabled:optIn]; |
| [[MetricKitSubscriber sharedInstance] setEnabled:optIn]; |
| } |
| |
| - (BOOL)areMetricsEnabled { |
| // If this if-def changes, it needs to be changed in |
| // IOSChromeMainParts::IsMetricsReportingEnabled and settings_egtest.mm. |
| #if BUILDFLAG(GOOGLE_CHROME_BRANDING) |
| BOOL optIn = GetApplicationContext()->GetLocalState()->GetBoolean( |
| metrics::prefs::kMetricsReportingEnabled); |
| #else |
| // If a startup crash has been requested, then pretend that metrics have been |
| // enabled, so that the app will go into recovery mode. |
| BOOL optIn = experimental_flags::IsStartupCrashEnabled(); |
| #endif |
| return optIn; |
| } |
| |
| - (BOOL)isUploadingEnabled { |
| return [self areMetricsEnabled]; |
| } |
| |
| #pragma mark - Internal methods. |
| |
| - (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading { |
| metrics::MetricsService* metrics = |
| GetApplicationContext()->GetMetricsService(); |
| DCHECK(metrics); |
| if (!metrics) |
| return; |
| if (enabled) { |
| if (!metrics->recording_active()) |
| metrics->Start(); |
| |
| if (allowUploading) |
| metrics->EnableReporting(); |
| else |
| metrics->DisableReporting(); |
| } else { |
| if (metrics->recording_active()) |
| metrics->Stop(); |
| } |
| } |
| |
| - (void)setAppGroupMetricsEnabled:(BOOL)enabled { |
| if (enabled) { |
| PrefService* prefs = GetApplicationContext()->GetLocalState(); |
| NSString* brandCode = |
| base::SysUTF8ToNSString(ios::provider::GetBrandCode()); |
| |
| app_group::main_app::EnableMetrics( |
| base::SysUTF8ToNSString( |
| GetApplicationContext()->GetMetricsService()->GetClientId()), |
| brandCode, prefs->GetInt64(metrics::prefs::kInstallDate), |
| prefs->GetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp)); |
| |
| // If metrics are enabled, process the logs. Otherwise, just delete them. |
| // TODO(crbug.com/782685): remove related code. |
| } else { |
| app_group::main_app::DisableMetrics(); |
| } |
| metrics_mediator::RecordWidgetUsage(kHistogramsFromExtension); |
| } |
| |
| - (void)processCrashReportsPresentAtStartup { |
| _hasProcessedCrashReportsPresentAtStartup = YES; |
| } |
| |
| - (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading { |
| crash_helper::SetUserEnabledUploading(enabled); |
| if (enabled) { |
| crash_helper::SetEnabled(true); |
| |
| // Do some processing of the crash reports present at startup. Note that |
| // this processing must be done before uploading is enabled because once |
| // uploading starts the number of crash reports present will begin to |
| // decrease as they are uploaded. The ordering is ensured here because both |
| // the crash report processing and the upload enabling are handled by |
| // posting blocks to a single |dispath_queue_t| in BreakpadController. |
| if (!_hasProcessedCrashReportsPresentAtStartup && allowUploading) { |
| [self processCrashReportsPresentAtStartup]; |
| } |
| [self setBreakpadUploadingEnabled:(![[PreviousSessionInfo sharedInstance] |
| isFirstSessionAfterUpgrade] && |
| allowUploading)]; |
| } else { |
| crash_helper::SetEnabled(false); |
| } |
| } |
| |
| - (void)setWatchWWANEnabled:(BOOL)enabled { |
| if (!enabled) { |
| _connectionTypeObserverBridge.reset(); |
| return; |
| } |
| |
| if (!_connectionTypeObserverBridge) { |
| _connectionTypeObserverBridge.reset(new ConnectionTypeObserverBridge(self)); |
| } |
| } |
| |
| - (void)updateMetricsPrefsOnPermissionChange:(BOOL)enabled { |
| // TODO(crbug.com/635669): Consolidate with metrics_reporting_state.cc |
| // function. |
| metrics::MetricsService* metrics = |
| GetApplicationContext()->GetMetricsService(); |
| DCHECK(metrics); |
| if (!metrics) |
| return; |
| if (enabled) { |
| // When a user opts in to the metrics reporting service, the previously |
| // collected data should be cleared to ensure that nothing is reported |
| // before a user opts in and all reported data is accurate. |
| if (!metrics->recording_active()) |
| metrics->ClearSavedStabilityMetrics(); |
| } else { |
| // Clear the client id pref when opting out. |
| // Note: Clearing client id will not affect the running state (e.g. field |
| // trial randomization), as the pref is only read on startup. |
| GetApplicationContext()->GetLocalState()->ClearPref( |
| metrics::prefs::kMetricsClientID); |
| GetApplicationContext()->GetLocalState()->ClearPref( |
| metrics::prefs::kMetricsReportingEnabledTimestamp); |
| crash_keys::ClearMetricsClientId(); |
| } |
| } |
| |
| + (void)disableReporting { |
| crash_helper::SetUploadingEnabled(false); |
| metrics::MetricsService* metrics = |
| GetApplicationContext()->GetMetricsService(); |
| DCHECK(metrics); |
| metrics->DisableReporting(); |
| } |
| |
| + (void)applicationDidEnterBackground:(NSInteger)memoryWarningCount { |
| base::RecordAction(base::UserMetricsAction("MobileEnteredBackground")); |
| base::UmaHistogramCounts100("MemoryWarning.OccurrencesPerSession", |
| memoryWarningCount); |
| |
| task_vm_info task_info_data; |
| 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>(&task_info_data), &count); |
| if (result == KERN_SUCCESS) { |
| mach_vm_size_t footprint_mb = task_info_data.phys_footprint / 1024 / 1024; |
| base::UmaHistogramMemoryLargeMB( |
| "Memory.Browser.MemoryFootprint.OnBackground", footprint_mb); |
| } |
| } |
| |
| #pragma mark - CRConnectionTypeObserverBridge implementation |
| |
| - (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type { |
| BOOL wwanEnabled = net::NetworkChangeNotifier::IsConnectionCellular(type); |
| // Currently the MainController only cares about WWAN state for the metrics |
| // reporting preference. If it's disabled, or the wifi-only preference is |
| // not set, we don't care. In fact, we should not even be getting this call. |
| DCHECK([self isMetricsReportingEnabledWifiOnly]); |
| // |wwanEnabled| is true if a cellular connection such as EDGE or GPRS is |
| // used. Otherwise, either there is no connection available, or another link |
| // (such as WiFi) is used. |
| if (wwanEnabled) { |
| // If WWAN mode is on, wifi-only prefs should be disabled. |
| // For the crash reporter, we still want to record the crashes. |
| [self setBreakpadUploadingEnabled:NO]; |
| [self setReporting:NO]; |
| } else if ([self areMetricsEnabled]) { |
| // Double-check that the metrics reporting preference is enabled. |
| if (![[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade]) |
| [self setBreakpadUploadingEnabled:YES]; |
| [self setReporting:YES]; |
| } |
| } |
| |
| #pragma mark - interfaces methods |
| |
| + (void)recordNumTabAtStartup:(int)numTabs { |
| base::UmaHistogramCounts100("Tabs.CountAtStartup", numTabs); |
| } |
| |
| + (void)recordNumTabAtResume:(int)numTabs { |
| base::UmaHistogramCounts100("Tabs.CountAtResume", numTabs); |
| } |
| |
| + (void)recordNumNTPTabAtStartup:(int)numTabs { |
| base::UmaHistogramCounts100("Tabs.NTPCountAtStartup", numTabs); |
| } |
| |
| + (void)recordNumNTPTabAtResume:(int)numTabs { |
| base::UmaHistogramCounts100("Tabs.NTPCountAtResume", numTabs); |
| } |
| |
| + (void)recordNumLiveNTPTabAtResume:(int)numTabs { |
| base::UmaHistogramCounts100("Tabs.LiveNTPCountAtResume", numTabs); |
| } |
| |
| - (void)setBreakpadUploadingEnabled:(BOOL)enableUploading { |
| crash_helper::SetUploadingEnabled(enableUploading); |
| } |
| |
| - (void)setReporting:(BOOL)enableReporting { |
| if (enableReporting) { |
| GetApplicationContext()->GetMetricsService()->EnableReporting(); |
| } else { |
| GetApplicationContext()->GetMetricsService()->DisableReporting(); |
| } |
| } |
| |
| - (BOOL)isMetricsReportingEnabledWifiOnly { |
| return GetApplicationContext()->GetLocalState()->GetBoolean( |
| metrics::prefs::kMetricsReportingEnabled); |
| } |
| |
| @end |