OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/app/application_delegate/metrics_mediator.h" |
| 6 |
| 7 #include "base/ios/weak_nsobject.h" |
| 8 #include "base/mac/bind_objc_block.h" |
| 9 #include "base/metrics/user_metrics_action.h" |
| 10 #include "base/strings/sys_string_conversions.h" |
| 11 #include "components/crash/core/common/crash_keys.h" |
| 12 #include "components/metrics/metrics_pref_names.h" |
| 13 #include "components/metrics/metrics_service.h" |
| 14 #include "components/prefs/pref_service.h" |
| 15 #import "ios/chrome/app/application_delegate/startup_information.h" |
| 16 #include "ios/chrome/browser/application_context.h" |
| 17 #include "ios/chrome/browser/chrome_url_constants.h" |
| 18 #include "ios/chrome/browser/crash_report/breakpad_helper.h" |
| 19 #import "ios/chrome/browser/crash_report/crash_report_background_uploader.h" |
| 20 #include "ios/chrome/browser/experimental_flags.h" |
| 21 #include "ios/chrome/browser/metrics/first_user_action_recorder.h" |
| 22 #import "ios/chrome/browser/metrics/previous_session_info.h" |
| 23 #import "ios/chrome/browser/net/connection_type_observer_bridge.h" |
| 24 #include "ios/chrome/browser/pref_names.h" |
| 25 #import "ios/chrome/browser/tabs/tab.h" |
| 26 #import "ios/chrome/browser/tabs/tab_model.h" |
| 27 #import "ios/chrome/browser/ui/main/browser_view_information.h" |
| 28 #include "ios/chrome/common/app_group/app_group_metrics_mainapp.h" |
| 29 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h" |
| 30 #include "ios/public/provider/chrome/browser/distribution/app_distribution_provi
der.h" |
| 31 #include "ios/web/public/web_thread.h" |
| 32 #include "url/gurl.h" |
| 33 |
| 34 namespace { |
| 35 // The amount of time (in seconds) between two background fetch calls. |
| 36 // TODO(crbug.com/496172): Re-enable background fetch. |
| 37 const NSTimeInterval kBackgroundFetchIntervalDelay = |
| 38 UIApplicationBackgroundFetchIntervalNever; |
| 39 // The amount of time (in seconds) to wait for the user to start a new task. |
| 40 const NSTimeInterval kFirstUserActionTimeout = 30.0; |
| 41 } // namespace |
| 42 |
| 43 namespace metrics_mediator { |
| 44 NSString* const kAppEnteredBackgroundDateKey = @"kAppEnteredBackgroundDate"; |
| 45 } // namespace metrics_mediator_constants |
| 46 |
| 47 using metrics_mediator::kAppEnteredBackgroundDateKey; |
| 48 |
| 49 @interface MetricsMediator ()<CRConnectionTypeObserverBridge> { |
| 50 // Whether or not the crash reports present at startup have been processed to |
| 51 // determine if the last app lifetime ended in an OOM crash. |
| 52 BOOL _hasProcessedCrashReportsPresentAtStartup; |
| 53 |
| 54 // Observer for the connection type. Contains a valid object only if the |
| 55 // metrics setting is set to wifi-only. |
| 56 std::unique_ptr<ConnectionTypeObserverBridge> connectionTypeObserverBridge_; |
| 57 } |
| 58 |
| 59 // Starts or stops metrics recording and/or uploading. |
| 60 - (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading; |
| 61 // Sets variables needed by the app_group application to collect UMA data. |
| 62 // Process the pending logs produced by extensions. |
| 63 // Called on start (cold and warm) and UMA settings change to update the |
| 64 // collecting settings in extensions. |
| 65 - (void)setAppGroupMetricsEnabled:(BOOL)enabled; |
| 66 // Processes crash reports present at startup. |
| 67 - (void)processCrashReportsPresentAtStartup; |
| 68 // Starts or stops crash recording and/or uploading. |
| 69 - (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading; |
| 70 // Starts or stops watching for wwan events. |
| 71 - (void)setWatchWWANEnabled:(BOOL)enabled; |
| 72 // Enable/disable transmission of accumulated logs and crash reports (dumps). |
| 73 - (void)setReporting:(BOOL)enableReporting; |
| 74 // Enable/Disable uploading crash reports. |
| 75 - (void)setBreakpadUploadingEnabled:(BOOL)enableUploading; |
| 76 // Returns YES if the metrics are enabled and the reporting is wifi-only. |
| 77 - (BOOL)isMetricsReportingEnabledWifiOnly; |
| 78 // Update metrics prefs on a permission (opt-in/out) change. When opting out, |
| 79 // this clears various client ids. When opting in, this resets saving crash |
| 80 // prefs, so as not to trigger upload of various stale data. |
| 81 // Mirrors the function in metrics_reporting_state.cc. |
| 82 - (void)updateMetricsPrefsOnPermissionChange:(BOOL)enabled; |
| 83 // Logs the number of tabs with UMAHistogramCount100 and allows testing. |
| 84 + (void)recordNumTabAtStartup:(int)numTabs; |
| 85 // Logs the number of tabs with UMAHistogramCount100 and allows testing. |
| 86 + (void)recordNumTabAtResume:(int)numTabs; |
| 87 |
| 88 @end |
| 89 |
| 90 @implementation MetricsMediator |
| 91 |
| 92 #pragma mark - Public methods. |
| 93 |
| 94 + (void)logStartupDuration:(id<StartupInformation>)startupInformation { |
| 95 if (![startupInformation isColdStart]) |
| 96 return; |
| 97 |
| 98 base::TimeDelta startDuration = |
| 99 base::TimeTicks::Now() - [startupInformation appLaunchTime]; |
| 100 if ([startupInformation startupParameters]) { |
| 101 UMA_HISTOGRAM_TIMES("Startup.ColdStartWithExternalURLTime", startDuration); |
| 102 } else { |
| 103 UMA_HISTOGRAM_TIMES("Startup.ColdStartWithoutExternalURLTime", |
| 104 startDuration); |
| 105 } |
| 106 } |
| 107 |
| 108 + (void)logDateInUserDefaults { |
| 109 [[NSUserDefaults standardUserDefaults] |
| 110 setObject:[NSDate date] |
| 111 forKey:metrics_mediator::kAppEnteredBackgroundDateKey]; |
| 112 } |
| 113 |
| 114 + (void)logLaunchMetricsWithStartupInformation: |
| 115 (id<StartupInformation>)startupInformation |
| 116 browserViewInformation: |
| 117 (id<BrowserViewInformation>)browserViewInformation { |
| 118 int numTabs = static_cast<int>([[browserViewInformation mainTabModel] count]); |
| 119 if (startupInformation.isColdStart) { |
| 120 [self recordNumTabAtStartup:numTabs]; |
| 121 } else { |
| 122 [self recordNumTabAtResume:numTabs]; |
| 123 } |
| 124 |
| 125 if (UIAccessibilityIsVoiceOverRunning()) { |
| 126 base::RecordAction( |
| 127 base::UserMetricsAction("MobileVoiceOverActiveOnLaunch")); |
| 128 } |
| 129 |
| 130 // Create the first user action recorder and schedule a task to expire it |
| 131 // after some timeout. If unable to determine the last time the app entered |
| 132 // the background (i.e. either first run or restore after crash), don't bother |
| 133 // recording the first user action since fresh start wouldn't be triggered. |
| 134 NSDate* lastAppClose = [[NSUserDefaults standardUserDefaults] |
| 135 objectForKey:kAppEnteredBackgroundDateKey]; |
| 136 if (lastAppClose) { |
| 137 NSTimeInterval interval = -[lastAppClose timeIntervalSinceNow]; |
| 138 [startupInformation |
| 139 activateFirstUserActionRecorderWithBackgroundTime:interval]; |
| 140 GURL ntpUrl = GURL(kChromeUINewTabURL); |
| 141 |
| 142 Tab* currentTab = [[browserViewInformation currentTabModel] currentTab]; |
| 143 if (currentTab && [currentTab url] == ntpUrl) { |
| 144 startupInformation.firstUserActionRecorder->RecordStartOnNTP(); |
| 145 [startupInformation resetFirstUserActionRecorder]; |
| 146 } else { |
| 147 [startupInformation |
| 148 expireFirstUserActionRecorderAfterDelay:kFirstUserActionTimeout]; |
| 149 } |
| 150 // Remove the value so it's not reused if the app crashes. |
| 151 [[NSUserDefaults standardUserDefaults] |
| 152 removeObjectForKey:kAppEnteredBackgroundDateKey]; |
| 153 } |
| 154 } |
| 155 |
| 156 - (void)updateMetricsStateBasedOnPrefsUserTriggered:(BOOL)isUserTriggered { |
| 157 BOOL optIn = [self areMetricsEnabled]; |
| 158 BOOL allowUploading = [self isUploadingEnabled]; |
| 159 BOOL wifiOnly = GetApplicationContext()->GetLocalState()->GetBoolean( |
| 160 prefs::kMetricsReportingWifiOnly); |
| 161 |
| 162 if (isUserTriggered) |
| 163 [self updateMetricsPrefsOnPermissionChange:optIn]; |
| 164 [self setMetricsEnabled:optIn withUploading:allowUploading]; |
| 165 [self setBreakpadEnabled:optIn withUploading:allowUploading]; |
| 166 [self setWatchWWANEnabled:(optIn && wifiOnly)]; |
| 167 } |
| 168 |
| 169 - (BOOL)areMetricsEnabled { |
| 170 // If this if-def changes, it needs to be changed in |
| 171 // IOSChromeMainParts::IsMetricsReportingEnabled and settings_egtest.mm. |
| 172 #if defined(GOOGLE_CHROME_BUILD) |
| 173 BOOL optIn = GetApplicationContext()->GetLocalState()->GetBoolean( |
| 174 metrics::prefs::kMetricsReportingEnabled); |
| 175 #else |
| 176 // If a startup crash has been requested, then pretend that metrics have been |
| 177 // enabled, so that the app will go into recovery mode. |
| 178 BOOL optIn = experimental_flags::IsStartupCrashEnabled(); |
| 179 #endif |
| 180 return optIn; |
| 181 } |
| 182 |
| 183 - (BOOL)isUploadingEnabled { |
| 184 BOOL optIn = [self areMetricsEnabled]; |
| 185 BOOL wifiOnly = GetApplicationContext()->GetLocalState()->GetBoolean( |
| 186 prefs::kMetricsReportingWifiOnly); |
| 187 BOOL allowUploading = optIn; |
| 188 if (optIn && wifiOnly) { |
| 189 BOOL usingWWAN = net::NetworkChangeNotifier::IsConnectionCellular( |
| 190 net::NetworkChangeNotifier::GetConnectionType()); |
| 191 allowUploading = !usingWWAN; |
| 192 } |
| 193 return allowUploading; |
| 194 } |
| 195 |
| 196 #pragma mark - Internal methods. |
| 197 |
| 198 - (void)setMetricsEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading { |
| 199 metrics::MetricsService* metrics = |
| 200 GetApplicationContext()->GetMetricsService(); |
| 201 DCHECK(metrics); |
| 202 if (!metrics) |
| 203 return; |
| 204 if (enabled) { |
| 205 [[UIApplication sharedApplication] |
| 206 setMinimumBackgroundFetchInterval:kBackgroundFetchIntervalDelay]; |
| 207 if (!metrics->recording_active()) |
| 208 metrics->Start(); |
| 209 |
| 210 if (allowUploading) |
| 211 metrics->EnableReporting(); |
| 212 else |
| 213 metrics->DisableReporting(); |
| 214 } else { |
| 215 if (metrics->recording_active()) |
| 216 metrics->Stop(); |
| 217 [[UIApplication sharedApplication] |
| 218 setMinimumBackgroundFetchInterval: |
| 219 UIApplicationBackgroundFetchIntervalNever]; |
| 220 } |
| 221 } |
| 222 |
| 223 - (void)setAppGroupMetricsEnabled:(BOOL)enabled { |
| 224 metrics::MetricsService* metrics = |
| 225 GetApplicationContext()->GetMetricsService(); |
| 226 if (enabled) { |
| 227 PrefService* prefs = GetApplicationContext()->GetLocalState(); |
| 228 NSString* brandCode = |
| 229 base::SysUTF8ToNSString(ios::GetChromeBrowserProvider() |
| 230 ->GetAppDistributionProvider() |
| 231 ->GetDistributionBrandCode()); |
| 232 app_group::main_app::EnableMetrics( |
| 233 base::SysUTF8ToNSString(metrics->GetClientId()), brandCode, |
| 234 prefs->GetInt64(metrics::prefs::kInstallDate), |
| 235 prefs->GetInt64(metrics::prefs::kMetricsReportingEnabledTimestamp)); |
| 236 } else { |
| 237 app_group::main_app::DisableMetrics(); |
| 238 } |
| 239 |
| 240 // If metrics are enabled, process the logs. Otherwise, just delete them. |
| 241 base::mac::ScopedBlock<app_group::ProceduralBlockWithData> callback; |
| 242 if (enabled) { |
| 243 callback.reset( |
| 244 ^(NSData* log_content) { |
| 245 std::string log(static_cast<const char*>([log_content bytes]), |
| 246 static_cast<size_t>([log_content length])); |
| 247 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, |
| 248 base::BindBlock(^{ |
| 249 metrics->PushExternalLog(log); |
| 250 })); |
| 251 }, |
| 252 base::scoped_policy::RETAIN); |
| 253 } |
| 254 |
| 255 web::WebThread::PostTask( |
| 256 web::WebThread::FILE, FROM_HERE, |
| 257 base::Bind(&app_group::main_app::ProcessPendingLogs, callback)); |
| 258 } |
| 259 |
| 260 - (void)processCrashReportsPresentAtStartup { |
| 261 _hasProcessedCrashReportsPresentAtStartup = YES; |
| 262 |
| 263 breakpad_helper::GetCrashReportCount(^(int crashReportCount) { |
| 264 [[CrashReportBackgroundUploader sharedInstance] |
| 265 setHasPendingCrashReportsToUploadAtStartup:(crashReportCount > 0)]; |
| 266 }); |
| 267 } |
| 268 |
| 269 - (void)setBreakpadEnabled:(BOOL)enabled withUploading:(BOOL)allowUploading { |
| 270 if (enabled) { |
| 271 breakpad_helper::SetEnabled(true); |
| 272 |
| 273 // Do some processing of the crash reports present at startup. Note that |
| 274 // this processing must be done before uploading is enabled because once |
| 275 // uploading starts the number of crash reports present will begin to |
| 276 // decrease as they are uploaded. The ordering is ensured here because both |
| 277 // the crash report processing and the upload enabling are handled by |
| 278 // posting blocks to a single |dispath_queue_t| in BreakpadController. |
| 279 if (!_hasProcessedCrashReportsPresentAtStartup && allowUploading) { |
| 280 [self processCrashReportsPresentAtStartup]; |
| 281 } |
| 282 [self setBreakpadUploadingEnabled:(![[PreviousSessionInfo sharedInstance] |
| 283 isFirstSessionAfterUpgrade] && |
| 284 allowUploading)]; |
| 285 } else { |
| 286 breakpad_helper::SetEnabled(false); |
| 287 } |
| 288 } |
| 289 |
| 290 - (void)setWatchWWANEnabled:(BOOL)enabled { |
| 291 if (!enabled) { |
| 292 connectionTypeObserverBridge_.reset(); |
| 293 return; |
| 294 } |
| 295 |
| 296 if (!connectionTypeObserverBridge_) { |
| 297 connectionTypeObserverBridge_.reset(new ConnectionTypeObserverBridge(self)); |
| 298 } |
| 299 } |
| 300 |
| 301 - (void)updateMetricsPrefsOnPermissionChange:(BOOL)enabled { |
| 302 // TODO(crbug.com/635669): Consolidate with metrics_reporting_state.cc |
| 303 // function. |
| 304 metrics::MetricsService* metrics = |
| 305 GetApplicationContext()->GetMetricsService(); |
| 306 DCHECK(metrics); |
| 307 if (!metrics) |
| 308 return; |
| 309 if (enabled) { |
| 310 // When a user opts in to the metrics reporting service, the previously |
| 311 // collected data should be cleared to ensure that nothing is reported |
| 312 // before a user opts in and all reported data is accurate. |
| 313 if (!metrics->recording_active()) |
| 314 metrics->ClearSavedStabilityMetrics(); |
| 315 } else { |
| 316 // Clear the client id pref when opting out. |
| 317 // Note: Clearing client id will not affect the running state (e.g. field |
| 318 // trial randomization), as the pref is only read on startup. |
| 319 GetApplicationContext()->GetLocalState()->ClearPref( |
| 320 metrics::prefs::kMetricsClientID); |
| 321 GetApplicationContext()->GetLocalState()->ClearPref( |
| 322 metrics::prefs::kMetricsReportingEnabledTimestamp); |
| 323 crash_keys::ClearMetricsClientId(); |
| 324 } |
| 325 } |
| 326 |
| 327 + (void)disableReporting { |
| 328 breakpad_helper::SetUploadingEnabled(false); |
| 329 metrics::MetricsService* metrics = |
| 330 GetApplicationContext()->GetMetricsService(); |
| 331 DCHECK(metrics); |
| 332 metrics->DisableReporting(); |
| 333 } |
| 334 |
| 335 + (void)applicationDidEnterBackground:(NSInteger)memoryWarningCount { |
| 336 base::RecordAction(base::UserMetricsAction("MobileEnteredBackground")); |
| 337 UMA_HISTOGRAM_COUNTS_100("MemoryWarning.OccurrencesPerSession", |
| 338 memoryWarningCount); |
| 339 } |
| 340 |
| 341 #pragma mark - CRConnectionTypeObserverBridge implementation |
| 342 |
| 343 - (void)connectionTypeChanged:(net::NetworkChangeNotifier::ConnectionType)type { |
| 344 BOOL wwanEnabled = net::NetworkChangeNotifier::IsConnectionCellular(type); |
| 345 // Currently the MainController only cares about WWAN state for the metrics |
| 346 // reporting preference. If it's disabled, or the wifi-only preference is |
| 347 // not set, we don't care. In fact, we should not even be getting this call. |
| 348 DCHECK([self isMetricsReportingEnabledWifiOnly]); |
| 349 // |wwanEnabled| is true if a cellular connection such as EDGE or GPRS is |
| 350 // used. Otherwise, either there is no connection available, or another link |
| 351 // (such as WiFi) is used. |
| 352 if (wwanEnabled) { |
| 353 // If WWAN mode is on, wifi-only prefs should be disabled. |
| 354 // For the crash reporter, we still want to record the crashes. |
| 355 [self setBreakpadUploadingEnabled:NO]; |
| 356 [self setReporting:NO]; |
| 357 } else if ([self areMetricsEnabled]) { |
| 358 // Double-check that the metrics reporting preference is enabled. |
| 359 if (![[PreviousSessionInfo sharedInstance] isFirstSessionAfterUpgrade]) |
| 360 [self setBreakpadUploadingEnabled:YES]; |
| 361 [self setReporting:YES]; |
| 362 } |
| 363 } |
| 364 |
| 365 #pragma mark - interfaces methods |
| 366 |
| 367 + (void)recordNumTabAtStartup:(int)numTabs { |
| 368 UMA_HISTOGRAM_COUNTS_100("Tabs.CountAtStartup", numTabs); |
| 369 } |
| 370 |
| 371 + (void)recordNumTabAtResume:(int)numTabs { |
| 372 UMA_HISTOGRAM_COUNTS_100("Tabs.CountAtResume", numTabs); |
| 373 } |
| 374 |
| 375 - (void)setBreakpadUploadingEnabled:(BOOL)enableUploading { |
| 376 breakpad_helper::SetUploadingEnabled(enableUploading); |
| 377 } |
| 378 |
| 379 - (void)setReporting:(BOOL)enableReporting { |
| 380 if (enableReporting) { |
| 381 GetApplicationContext()->GetMetricsService()->EnableReporting(); |
| 382 } else { |
| 383 GetApplicationContext()->GetMetricsService()->DisableReporting(); |
| 384 } |
| 385 } |
| 386 |
| 387 - (BOOL)isMetricsReportingEnabledWifiOnly { |
| 388 return GetApplicationContext()->GetLocalState()->GetBoolean( |
| 389 metrics::prefs::kMetricsReportingEnabled) && |
| 390 GetApplicationContext()->GetLocalState()->GetBoolean( |
| 391 prefs::kMetricsReportingWifiOnly); |
| 392 } |
| 393 |
| 394 @end |
OLD | NEW |