1// Copyright 2014 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//------------------------------------------------------------------------------ 6// Description of the life cycle of a instance of MetricsService. 7// 8// OVERVIEW 9// 10// A MetricsService instance is typically created at application startup. It is 11// the central controller for the acquisition of log data, and the automatic 12// transmission of that log data to an external server. Its major job is to 13// manage logs, grouping them for transmission, and transmitting them. As part 14// of its grouping, MS finalizes logs by including some just-in-time gathered 15// memory statistics, snapshotting the current stats of numerous histograms, 16// closing the logs, translating to protocol buffer format, and compressing the 17// results for transmission. Transmission includes submitting a compressed log 18// as data in a URL-post, and retransmitting (or retaining at process 19// termination) if the attempted transmission failed. Retention across process 20// terminations is done using the the PrefServices facilities. The retained logs 21// (the ones that never got transmitted) are compressed and base64-encoded 22// before being persisted. 23// 24// Logs fall into one of two categories: "initial logs," and "ongoing logs." 25// There is at most one initial log sent for each complete run of Chrome (from 26// startup, to browser shutdown). An initial log is generally transmitted some 27// short time (1 minute?) after startup, and includes stats such as recent crash 28// info, the number and types of plugins, etc. The external server's response 29// to the initial log conceptually tells this MS if it should continue 30// transmitting logs (during this session). The server response can actually be 31// much more detailed, and always includes (at a minimum) how often additional 32// ongoing logs should be sent. 33// 34// After the above initial log, a series of ongoing logs will be transmitted. 35// The first ongoing log actually begins to accumulate information stating when 36// the MS was first constructed. Note that even though the initial log is 37// commonly sent a full minute after startup, the initial log does not include 38// much in the way of user stats. The most common interlog period (delay) 39// is 30 minutes. That time period starts when the first user action causes a 40// logging event. This means that if there is no user action, there may be long 41// periods without any (ongoing) log transmissions. Ongoing logs typically 42// contain very detailed records of user activities (ex: opened tab, closed 43// tab, fetched URL, maximized window, etc.) In addition, just before an 44// ongoing log is closed out, a call is made to gather memory statistics. Those 45// memory statistics are deposited into a histogram, and the log finalization 46// code is then called. In the finalization, a call to a Histogram server 47// acquires a list of all local histograms that have been flagged for upload 48// to the UMA server. The finalization also acquires the most recent number 49// of page loads, along with any counts of renderer or plugin crashes. 50// 51// When the browser shuts down, there will typically be a fragment of an ongoing 52// log that has not yet been transmitted. At shutdown time, that fragment is 53// closed (including snapshotting histograms), and persisted, for potential 54// transmission during a future run of the product. 55// 56// There are two slightly abnormal shutdown conditions. There is a 57// "disconnected scenario," and a "really fast startup and shutdown" scenario. 58// In the "never connected" situation, the user has (during the running of the 59// process) never established an internet connection. As a result, attempts to 60// transmit the initial log have failed, and a lot(?) of data has accumulated in 61// the ongoing log (which didn't yet get closed, because there was never even a 62// contemplation of sending it). There is also a kindred "lost connection" 63// situation, where a loss of connection prevented an ongoing log from being 64// transmitted, and a (still open) log was stuck accumulating a lot(?) of data, 65// while the earlier log retried its transmission. In both of these 66// disconnected situations, two logs need to be, and are, persistently stored 67// for future transmission. 68// 69// The other unusual shutdown condition, termed "really fast startup and 70// shutdown," involves the deliberate user termination of the process before 71// the initial log is even formed or transmitted. In that situation, no logging 72// is done, but the historical crash statistics remain (unlogged) for inclusion 73// in a future run's initial log. (i.e., we don't lose crash stats). 74// 75// With the above overview, we can now describe the state machine's various 76// states, based on the State enum specified in the state_ member. Those states 77// are: 78// 79// INITIALIZED, // Constructor was called. 80// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. 81// INIT_TASK_DONE, // Waiting for timer to send initial log. 82// SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. 83// SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. 84// SENDING_OLD_LOGS, // Sending unsent logs from previous session. 85// SENDING_CURRENT_LOGS, // Sending ongoing logs as they acrue. 86// 87// In more detail, we have: 88// 89// INITIALIZED, // Constructor was called. 90// The MS has been constructed, but has taken no actions to compose the 91// initial log. 92// 93// INIT_TASK_SCHEDULED, // Waiting for deferred init tasks to finish. 94// Typically about 30 seconds after startup, a task is sent to a second thread 95// (the file thread) to perform deferred (lower priority and slower) 96// initialization steps such as getting the list of plugins. That task will 97// (when complete) make an async callback (via a Task) to indicate the 98// completion. 99// 100// INIT_TASK_DONE, // Waiting for timer to send initial log. 101// The callback has arrived, and it is now possible for an initial log to be 102// created. This callback typically arrives back less than one second after 103// the deferred init task is dispatched. 104// 105// SENDING_INITIAL_STABILITY_LOG, // Initial stability log being sent. 106// During initialization, if a crash occurred during the previous session, an 107// initial stability log will be generated and registered with the log manager. 108// This state will be entered if a stability log was prepared during metrics 109// service initialization (in InitializeMetricsRecordingState()) and is waiting 110// to be transmitted when it's time to send up the first log (per the reporting 111// scheduler). If there is no initial stability log (e.g. there was no previous 112// crash), then this state will be skipped and the state will advance to 113// SENDING_INITIAL_METRICS_LOG. 114// 115// SENDING_INITIAL_METRICS_LOG, // Initial metrics log being sent. 116// This state is entered after the initial metrics log has been composed, and 117// prepared for transmission. This happens after SENDING_INITIAL_STABILITY_LOG 118// if there was an initial stability log (see above). It is also the case that 119// any previously unsent logs have been loaded into instance variables for 120// possible transmission. 121// 122// SENDING_OLD_LOGS, // Sending unsent logs from previous session. 123// This state indicates that the initial log for this session has been 124// successfully sent and it is now time to send any logs that were 125// saved from previous sessions. All such logs will be transmitted before 126// exiting this state, and proceeding with ongoing logs from the current session 127// (see next state). 128// 129// SENDING_CURRENT_LOGS, // Sending standard current logs as they accrue. 130// Current logs are being accumulated. Typically every 20 minutes a log is 131// closed and finalized for transmission, at the same time as a new log is 132// started. 133// 134// The progression through the above states is simple, and sequential, in the 135// most common use cases. States proceed from INITIAL to SENDING_CURRENT_LOGS, 136// and remain in the latter until shutdown. 137// 138// The one unusual case is when the user asks that we stop logging. When that 139// happens, any staged (transmission in progress) log is persisted, and any log 140// that is currently accumulating is also finalized and persisted. We then 141// regress back to the SEND_OLD_LOGS state in case the user enables log 142// recording again during this session. This way anything we have persisted 143// will be sent automatically if/when we progress back to SENDING_CURRENT_LOG 144// state. 145// 146// Another similar case is on mobile, when the application is backgrounded and 147// then foregrounded again. Backgrounding created new "old" stored logs, so the 148// state drops back from SENDING_CURRENT_LOGS to SENDING_OLD_LOGS so those logs 149// will be sent. 150// 151// Also note that whenever we successfully send an old log, we mirror the list 152// of logs into the PrefService. This ensures that IF we crash, we won't start 153// up and retransmit our old logs again. 154// 155// Due to race conditions, it is always possible that a log file could be sent 156// twice. For example, if a log file is sent, but not yet acknowledged by 157// the external server, and the user shuts down, then a copy of the log may be 158// saved for re-transmission. These duplicates could be filtered out server 159// side, but are not expected to be a significant problem. 160// 161// 162//------------------------------------------------------------------------------ 163 164#include "components/metrics/metrics_service.h" 165 166#include <algorithm> 167 168#include "base/bind.h" 169#include "base/callback.h" 170#include "base/metrics/histogram.h" 171#include "base/metrics/histogram_base.h" 172#include "base/metrics/histogram_samples.h" 173#include "base/metrics/sparse_histogram.h" 174#include "base/metrics/statistics_recorder.h" 175#include "base/prefs/pref_registry_simple.h" 176#include "base/prefs/pref_service.h" 177#include "base/strings/string_number_conversions.h" 178#include "base/strings/utf_string_conversions.h" 179#include "base/threading/platform_thread.h" 180#include "base/threading/thread.h" 181#include "base/threading/thread_restrictions.h" 182#include "base/time/time.h" 183#include "base/tracked_objects.h" 184#include "base/values.h" 185#include "components/metrics/metrics_log.h" 186#include "components/metrics/metrics_log_manager.h" 187#include "components/metrics/metrics_log_uploader.h" 188#include "components/metrics/metrics_pref_names.h" 189#include "components/metrics/metrics_reporting_scheduler.h" 190#include "components/metrics/metrics_service_client.h" 191#include "components/metrics/metrics_state_manager.h" 192#include "components/variations/entropy_provider.h" 193 194namespace metrics { 195 196namespace { 197 198// Check to see that we're being called on only one thread. 199bool IsSingleThreaded() { 200 static base::PlatformThreadId thread_id = 0; 201 if (!thread_id) 202 thread_id = base::PlatformThread::CurrentId(); 203 return base::PlatformThread::CurrentId() == thread_id; 204} 205 206// The delay, in seconds, after starting recording before doing expensive 207// initialization work. 208#if defined(OS_ANDROID) || defined(OS_IOS) 209// On mobile devices, a significant portion of sessions last less than a minute. 210// Use a shorter timer on these platforms to avoid losing data. 211// TODO(dfalcantara): To avoid delaying startup, tighten up initialization so 212// that it occurs after the user gets their initial page. 213const int kInitializationDelaySeconds = 5; 214#else 215const int kInitializationDelaySeconds = 30; 216#endif 217 218// The maximum number of events in a log uploaded to the UMA server. 219const int kEventLimit = 2400; 220 221// If an upload fails, and the transmission was over this byte count, then we 222// will discard the log, and not try to retransmit it. We also don't persist 223// the log to the prefs for transmission during the next chrome session if this 224// limit is exceeded. 225const size_t kUploadLogAvoidRetransmitSize = 100 * 1024; 226 227// Interval, in minutes, between state saves. 228const int kSaveStateIntervalMinutes = 5; 229 230// The metrics server's URL. 231const char kServerUrl[] = "https://clients4.google.com/uma/v2"; 232 233// The MIME type for the uploaded metrics data. 234const char kMimeType[] = "application/vnd.chrome.uma"; 235 236enum ResponseStatus { 237 UNKNOWN_FAILURE, 238 SUCCESS, 239 BAD_REQUEST, // Invalid syntax or log too large. 240 NO_RESPONSE, 241 NUM_RESPONSE_STATUSES 242}; 243 244ResponseStatus ResponseCodeToStatus(int response_code) { 245 switch (response_code) { 246 case -1: 247 return NO_RESPONSE; 248 case 200: 249 return SUCCESS; 250 case 400: 251 return BAD_REQUEST; 252 default: 253 return UNKNOWN_FAILURE; 254 } 255} 256 257void MarkAppCleanShutdownAndCommit(CleanExitBeacon* clean_exit_beacon, 258 PrefService* local_state) { 259 clean_exit_beacon->WriteBeaconValue(true); 260 local_state->SetInteger(metrics::prefs::kStabilityExecutionPhase, 261 MetricsService::SHUTDOWN_COMPLETE); 262 // Start writing right away (write happens on a different thread). 263 local_state->CommitPendingWrite(); 264} 265 266} // namespace 267 268 269SyntheticTrialGroup::SyntheticTrialGroup(uint32 trial, uint32 group) { 270 id.name = trial; 271 id.group = group; 272} 273 274SyntheticTrialGroup::~SyntheticTrialGroup() { 275} 276 277// static 278MetricsService::ShutdownCleanliness MetricsService::clean_shutdown_status_ = 279 MetricsService::CLEANLY_SHUTDOWN; 280 281MetricsService::ExecutionPhase MetricsService::execution_phase_ = 282 MetricsService::UNINITIALIZED_PHASE; 283 284// static 285void MetricsService::RegisterPrefs(PrefRegistrySimple* registry) { 286 DCHECK(IsSingleThreaded()); 287 metrics::MetricsStateManager::RegisterPrefs(registry); 288 MetricsLog::RegisterPrefs(registry); 289 290 registry->RegisterInt64Pref(metrics::prefs::kInstallDate, 0); 291 292 registry->RegisterInt64Pref(metrics::prefs::kStabilityLaunchTimeSec, 0); 293 registry->RegisterInt64Pref(metrics::prefs::kStabilityLastTimestampSec, 0); 294 registry->RegisterStringPref(metrics::prefs::kStabilityStatsVersion, 295 std::string()); 296 registry->RegisterInt64Pref(metrics::prefs::kStabilityStatsBuildTime, 0); 297 registry->RegisterBooleanPref(metrics::prefs::kStabilityExitedCleanly, true); 298 registry->RegisterIntegerPref(metrics::prefs::kStabilityExecutionPhase, 299 UNINITIALIZED_PHASE); 300 registry->RegisterBooleanPref(metrics::prefs::kStabilitySessionEndCompleted, 301 true); 302 registry->RegisterIntegerPref(metrics::prefs::kMetricsSessionID, -1); 303 304 registry->RegisterListPref(metrics::prefs::kMetricsInitialLogs); 305 registry->RegisterListPref(metrics::prefs::kMetricsOngoingLogs); 306 307 registry->RegisterInt64Pref(metrics::prefs::kUninstallLaunchCount, 0); 308 registry->RegisterInt64Pref(metrics::prefs::kUninstallMetricsUptimeSec, 0); 309} 310 311MetricsService::MetricsService(metrics::MetricsStateManager* state_manager, 312 metrics::MetricsServiceClient* client, 313 PrefService* local_state) 314 : log_manager_(local_state, kUploadLogAvoidRetransmitSize), 315 histogram_snapshot_manager_(this), 316 state_manager_(state_manager), 317 client_(client), 318 local_state_(local_state), 319 clean_exit_beacon_(client->GetRegistryBackupKey(), local_state), 320 recording_active_(false), 321 reporting_active_(false), 322 test_mode_active_(false), 323 state_(INITIALIZED), 324 has_initial_stability_log_(false), 325 log_upload_in_progress_(false), 326 idle_since_last_transmission_(false), 327 session_id_(-1), 328 self_ptr_factory_(this), 329 state_saver_factory_(this) { 330 DCHECK(IsSingleThreaded()); 331 DCHECK(state_manager_); 332 DCHECK(client_); 333 DCHECK(local_state_); 334 335 // Set the install date if this is our first run. 336 int64 install_date = local_state_->GetInt64(metrics::prefs::kInstallDate); 337 if (install_date == 0) { 338 local_state_->SetInt64(metrics::prefs::kInstallDate, 339 base::Time::Now().ToTimeT()); 340 } 341} 342 343MetricsService::~MetricsService() { 344 DisableRecording(); 345} 346 347void MetricsService::InitializeMetricsRecordingState() { 348 InitializeMetricsState(); 349 350 base::Closure callback = base::Bind(&MetricsService::StartScheduledUpload, 351 self_ptr_factory_.GetWeakPtr()); 352 scheduler_.reset(new MetricsReportingScheduler(callback)); 353} 354 355void MetricsService::Start() { 356 HandleIdleSinceLastTransmission(false); 357 EnableRecording(); 358 EnableReporting(); 359} 360 361bool MetricsService::StartIfMetricsReportingEnabled() { 362 const bool enabled = state_manager_->IsMetricsReportingEnabled(); 363 if (enabled) 364 Start(); 365 return enabled; 366} 367 368void MetricsService::StartRecordingForTests() { 369 test_mode_active_ = true; 370 EnableRecording(); 371 DisableReporting(); 372} 373 374void MetricsService::Stop() { 375 HandleIdleSinceLastTransmission(false); 376 DisableReporting(); 377 DisableRecording(); 378} 379 380void MetricsService::EnableReporting() { 381 if (reporting_active_) 382 return; 383 reporting_active_ = true; 384 StartSchedulerIfNecessary(); 385} 386 387void MetricsService::DisableReporting() { 388 reporting_active_ = false; 389} 390 391std::string MetricsService::GetClientId() { 392 return state_manager_->client_id(); 393} 394 395int64 MetricsService::GetInstallDate() { 396 return local_state_->GetInt64(metrics::prefs::kInstallDate); 397} 398 399scoped_ptr<const base::FieldTrial::EntropyProvider> 400MetricsService::CreateEntropyProvider() { 401 // TODO(asvitkine): Refactor the code so that MetricsService does not expose 402 // this method. 403 return state_manager_->CreateEntropyProvider(); 404} 405 406void MetricsService::EnableRecording() { 407 DCHECK(IsSingleThreaded()); 408 409 if (recording_active_) 410 return; 411 recording_active_ = true; 412 413 state_manager_->ForceClientIdCreation(); 414 client_->SetMetricsClientId(state_manager_->client_id()); 415 if (!log_manager_.current_log()) 416 OpenNewLog(); 417 418 for (size_t i = 0; i < metrics_providers_.size(); ++i) 419 metrics_providers_[i]->OnRecordingEnabled(); 420 421 base::RemoveActionCallback(action_callback_); 422 action_callback_ = base::Bind(&MetricsService::OnUserAction, 423 base::Unretained(this)); 424 base::AddActionCallback(action_callback_); 425} 426 427void MetricsService::DisableRecording() { 428 DCHECK(IsSingleThreaded()); 429 430 if (!recording_active_) 431 return; 432 recording_active_ = false; 433 434 base::RemoveActionCallback(action_callback_); 435 436 for (size_t i = 0; i < metrics_providers_.size(); ++i) 437 metrics_providers_[i]->OnRecordingDisabled(); 438 439 PushPendingLogsToPersistentStorage(); 440} 441 442bool MetricsService::recording_active() const { 443 DCHECK(IsSingleThreaded()); 444 return recording_active_; 445} 446 447bool MetricsService::reporting_active() const { 448 DCHECK(IsSingleThreaded()); 449 return reporting_active_; 450} 451 452void MetricsService::RecordDelta(const base::HistogramBase& histogram, 453 const base::HistogramSamples& snapshot) { 454 log_manager_.current_log()->RecordHistogramDelta(histogram.histogram_name(), 455 snapshot); 456} 457 458void MetricsService::InconsistencyDetected( 459 base::HistogramBase::Inconsistency problem) { 460 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowser", 461 problem, base::HistogramBase::NEVER_EXCEEDED_VALUE); 462} 463 464void MetricsService::UniqueInconsistencyDetected( 465 base::HistogramBase::Inconsistency problem) { 466 UMA_HISTOGRAM_ENUMERATION("Histogram.InconsistenciesBrowserUnique", 467 problem, base::HistogramBase::NEVER_EXCEEDED_VALUE); 468} 469 470void MetricsService::InconsistencyDetectedInLoggedCount(int amount) { 471 UMA_HISTOGRAM_COUNTS("Histogram.InconsistentSnapshotBrowser", 472 std::abs(amount)); 473} 474 475void MetricsService::HandleIdleSinceLastTransmission(bool in_idle) { 476 // If there wasn't a lot of action, maybe the computer was asleep, in which 477 // case, the log transmissions should have stopped. Here we start them up 478 // again. 479 if (!in_idle && idle_since_last_transmission_) 480 StartSchedulerIfNecessary(); 481 idle_since_last_transmission_ = in_idle; 482} 483 484void MetricsService::OnApplicationNotIdle() { 485 if (recording_active_) 486 HandleIdleSinceLastTransmission(false); 487} 488 489void MetricsService::RecordStartOfSessionEnd() { 490 LogCleanShutdown(); 491 RecordBooleanPrefValue(metrics::prefs::kStabilitySessionEndCompleted, false); 492} 493 494void MetricsService::RecordCompletedSessionEnd() { 495 LogCleanShutdown(); 496 RecordBooleanPrefValue(metrics::prefs::kStabilitySessionEndCompleted, true); 497} 498 499#if defined(OS_ANDROID) || defined(OS_IOS) 500void MetricsService::OnAppEnterBackground() { 501 scheduler_->Stop(); 502 503 MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_); 504 505 // At this point, there's no way of knowing when the process will be 506 // killed, so this has to be treated similar to a shutdown, closing and 507 // persisting all logs. Unlinke a shutdown, the state is primed to be ready 508 // to continue logging and uploading if the process does return. 509 if (recording_active() && state_ >= SENDING_INITIAL_STABILITY_LOG) { 510 PushPendingLogsToPersistentStorage(); 511 // Persisting logs closes the current log, so start recording a new log 512 // immediately to capture any background work that might be done before the 513 // process is killed. 514 OpenNewLog(); 515 } 516} 517 518void MetricsService::OnAppEnterForeground() { 519 clean_exit_beacon_.WriteBeaconValue(false); 520 StartSchedulerIfNecessary(); 521} 522#else 523void MetricsService::LogNeedForCleanShutdown() { 524 clean_exit_beacon_.WriteBeaconValue(false); 525 // Redundant setting to be sure we call for a clean shutdown. 526 clean_shutdown_status_ = NEED_TO_SHUTDOWN; 527} 528#endif // defined(OS_ANDROID) || defined(OS_IOS) 529 530// static 531void MetricsService::SetExecutionPhase(ExecutionPhase execution_phase, 532 PrefService* local_state) { 533 execution_phase_ = execution_phase; 534 local_state->SetInteger(metrics::prefs::kStabilityExecutionPhase, 535 execution_phase_); 536} 537 538void MetricsService::RecordBreakpadRegistration(bool success) { 539 if (!success) 540 IncrementPrefValue(metrics::prefs::kStabilityBreakpadRegistrationFail); 541 else 542 IncrementPrefValue(metrics::prefs::kStabilityBreakpadRegistrationSuccess); 543} 544 545void MetricsService::RecordBreakpadHasDebugger(bool has_debugger) { 546 if (!has_debugger) 547 IncrementPrefValue(metrics::prefs::kStabilityDebuggerNotPresent); 548 else 549 IncrementPrefValue(metrics::prefs::kStabilityDebuggerPresent); 550} 551 552//------------------------------------------------------------------------------ 553// private methods 554//------------------------------------------------------------------------------ 555 556 557//------------------------------------------------------------------------------ 558// Initialization methods 559 560void MetricsService::InitializeMetricsState() { 561 const int64 buildtime = MetricsLog::GetBuildTime(); 562 const std::string version = client_->GetVersionString(); 563 bool version_changed = false; 564 if (local_state_->GetInt64(prefs::kStabilityStatsBuildTime) != buildtime || 565 local_state_->GetString(prefs::kStabilityStatsVersion) != version) { 566 local_state_->SetString(metrics::prefs::kStabilityStatsVersion, version); 567 local_state_->SetInt64(metrics::prefs::kStabilityStatsBuildTime, buildtime); 568 version_changed = true; 569 } 570 571 log_manager_.LoadPersistedUnsentLogs(); 572 573 session_id_ = local_state_->GetInteger(metrics::prefs::kMetricsSessionID); 574 575 if (!clean_exit_beacon_.exited_cleanly()) { 576 IncrementPrefValue(metrics::prefs::kStabilityCrashCount); 577 // Reset flag, and wait until we call LogNeedForCleanShutdown() before 578 // monitoring. 579 clean_exit_beacon_.WriteBeaconValue(true); 580 } 581 582 if (!clean_exit_beacon_.exited_cleanly() || ProvidersHaveStabilityMetrics()) { 583 // TODO(rtenneti): On windows, consider saving/getting execution_phase from 584 // the registry. 585 int execution_phase = 586 local_state_->GetInteger(metrics::prefs::kStabilityExecutionPhase); 587 UMA_HISTOGRAM_SPARSE_SLOWLY("Chrome.Browser.CrashedExecutionPhase", 588 execution_phase); 589 590 // If the previous session didn't exit cleanly, or if any provider 591 // explicitly requests it, prepare an initial stability log - 592 // provided UMA is enabled. 593 if (state_manager_->IsMetricsReportingEnabled()) 594 PrepareInitialStabilityLog(); 595 } 596 597 // If no initial stability log was generated and there was a version upgrade, 598 // clear the stability stats from the previous version (so that they don't get 599 // attributed to the current version). This could otherwise happen due to a 600 // number of different edge cases, such as if the last version crashed before 601 // it could save off a system profile or if UMA reporting is disabled (which 602 // normally results in stats being accumulated). 603 if (!has_initial_stability_log_ && version_changed) { 604 for (size_t i = 0; i < metrics_providers_.size(); ++i) 605 metrics_providers_[i]->ClearSavedStabilityMetrics(); 606 607 // Reset the prefs that are managed by MetricsService/MetricsLog directly. 608 local_state_->SetInteger(prefs::kStabilityCrashCount, 0); 609 local_state_->SetInteger(prefs::kStabilityExecutionPhase, 610 UNINITIALIZED_PHASE); 611 local_state_->SetInteger(prefs::kStabilityIncompleteSessionEndCount, 0); 612 local_state_->SetInteger(prefs::kStabilityLaunchCount, 0); 613 local_state_->SetBoolean(prefs::kStabilitySessionEndCompleted, true); 614 } 615 616 // Update session ID. 617 ++session_id_; 618 local_state_->SetInteger(metrics::prefs::kMetricsSessionID, session_id_); 619 620 // Stability bookkeeping 621 IncrementPrefValue(metrics::prefs::kStabilityLaunchCount); 622 623 DCHECK_EQ(UNINITIALIZED_PHASE, execution_phase_); 624 SetExecutionPhase(START_METRICS_RECORDING, local_state_); 625 626 if (!local_state_->GetBoolean( 627 metrics::prefs::kStabilitySessionEndCompleted)) { 628 IncrementPrefValue(metrics::prefs::kStabilityIncompleteSessionEndCount); 629 // This is marked false when we get a WM_ENDSESSION. 630 local_state_->SetBoolean(metrics::prefs::kStabilitySessionEndCompleted, 631 true); 632 } 633 634 // Call GetUptimes() for the first time, thus allowing all later calls 635 // to record incremental uptimes accurately. 636 base::TimeDelta ignored_uptime_parameter; 637 base::TimeDelta startup_uptime; 638 GetUptimes(local_state_, &startup_uptime, &ignored_uptime_parameter); 639 DCHECK_EQ(0, startup_uptime.InMicroseconds()); 640 // For backwards compatibility, leave this intact in case Omaha is checking 641 // them. metrics::prefs::kStabilityLastTimestampSec may also be useless now. 642 // TODO(jar): Delete these if they have no uses. 643 local_state_->SetInt64(metrics::prefs::kStabilityLaunchTimeSec, 644 base::Time::Now().ToTimeT()); 645 646 // Bookkeeping for the uninstall metrics. 647 IncrementLongPrefsValue(metrics::prefs::kUninstallLaunchCount); 648 649 // Kick off the process of saving the state (so the uptime numbers keep 650 // getting updated) every n minutes. 651 ScheduleNextStateSave(); 652} 653 654void MetricsService::OnUserAction(const std::string& action) { 655 if (!ShouldLogEvents()) 656 return; 657 658 log_manager_.current_log()->RecordUserAction(action); 659 HandleIdleSinceLastTransmission(false); 660} 661 662void MetricsService::FinishedGatheringInitialMetrics() { 663 DCHECK_EQ(INIT_TASK_SCHEDULED, state_); 664 state_ = INIT_TASK_DONE; 665 666 // Create the initial log. 667 if (!initial_metrics_log_.get()) { 668 initial_metrics_log_ = CreateLog(MetricsLog::ONGOING_LOG); 669 NotifyOnDidCreateMetricsLog(); 670 } 671 672 scheduler_->InitTaskComplete(); 673} 674 675void MetricsService::GetUptimes(PrefService* pref, 676 base::TimeDelta* incremental_uptime, 677 base::TimeDelta* uptime) { 678 base::TimeTicks now = base::TimeTicks::Now(); 679 // If this is the first call, init |first_updated_time_| and 680 // |last_updated_time_|. 681 if (last_updated_time_.is_null()) { 682 first_updated_time_ = now; 683 last_updated_time_ = now; 684 } 685 *incremental_uptime = now - last_updated_time_; 686 *uptime = now - first_updated_time_; 687 last_updated_time_ = now; 688 689 const int64 incremental_time_secs = incremental_uptime->InSeconds(); 690 if (incremental_time_secs > 0) { 691 int64 metrics_uptime = 692 pref->GetInt64(metrics::prefs::kUninstallMetricsUptimeSec); 693 metrics_uptime += incremental_time_secs; 694 pref->SetInt64(metrics::prefs::kUninstallMetricsUptimeSec, metrics_uptime); 695 } 696} 697 698void MetricsService::NotifyOnDidCreateMetricsLog() { 699 DCHECK(IsSingleThreaded()); 700 for (size_t i = 0; i < metrics_providers_.size(); ++i) 701 metrics_providers_[i]->OnDidCreateMetricsLog(); 702} 703 704//------------------------------------------------------------------------------ 705// State save methods 706 707void MetricsService::ScheduleNextStateSave() { 708 state_saver_factory_.InvalidateWeakPtrs(); 709 710 base::MessageLoop::current()->PostDelayedTask(FROM_HERE, 711 base::Bind(&MetricsService::SaveLocalState, 712 state_saver_factory_.GetWeakPtr()), 713 base::TimeDelta::FromMinutes(kSaveStateIntervalMinutes)); 714} 715 716void MetricsService::SaveLocalState() { 717 RecordCurrentState(local_state_); 718 719 // TODO(jar):110021 Does this run down the batteries???? 720 ScheduleNextStateSave(); 721} 722 723 724//------------------------------------------------------------------------------ 725// Recording control methods 726 727void MetricsService::OpenNewLog() { 728 DCHECK(!log_manager_.current_log()); 729 730 log_manager_.BeginLoggingWithLog(CreateLog(MetricsLog::ONGOING_LOG)); 731 NotifyOnDidCreateMetricsLog(); 732 if (state_ == INITIALIZED) { 733 // We only need to schedule that run once. 734 state_ = INIT_TASK_SCHEDULED; 735 736 base::MessageLoop::current()->PostDelayedTask( 737 FROM_HERE, 738 base::Bind(&MetricsService::StartGatheringMetrics, 739 self_ptr_factory_.GetWeakPtr()), 740 base::TimeDelta::FromSeconds(kInitializationDelaySeconds)); 741 } 742} 743 744void MetricsService::StartGatheringMetrics() { 745 client_->StartGatheringMetrics( 746 base::Bind(&MetricsService::FinishedGatheringInitialMetrics, 747 self_ptr_factory_.GetWeakPtr())); 748} 749 750void MetricsService::CloseCurrentLog() { 751 if (!log_manager_.current_log()) 752 return; 753 754 // TODO(jar): Integrate bounds on log recording more consistently, so that we 755 // can stop recording logs that are too big much sooner. 756 if (log_manager_.current_log()->num_events() > kEventLimit) { 757 UMA_HISTOGRAM_COUNTS("UMA.Discarded Log Events", 758 log_manager_.current_log()->num_events()); 759 log_manager_.DiscardCurrentLog(); 760 OpenNewLog(); // Start trivial log to hold our histograms. 761 } 762 763 // Put incremental data (histogram deltas, and realtime stats deltas) at the 764 // end of all log transmissions (initial log handles this separately). 765 // RecordIncrementalStabilityElements only exists on the derived 766 // MetricsLog class. 767 MetricsLog* current_log = log_manager_.current_log(); 768 DCHECK(current_log); 769 std::vector<variations::ActiveGroupId> synthetic_trials; 770 GetCurrentSyntheticFieldTrials(&synthetic_trials); 771 current_log->RecordEnvironment( 772 metrics_providers_.get(), synthetic_trials, GetInstallDate()); 773 base::TimeDelta incremental_uptime; 774 base::TimeDelta uptime; 775 GetUptimes(local_state_, &incremental_uptime, &uptime); 776 current_log->RecordStabilityMetrics(metrics_providers_.get(), 777 incremental_uptime, uptime); 778 779 current_log->RecordGeneralMetrics(metrics_providers_.get()); 780 RecordCurrentHistograms(); 781 782 log_manager_.FinishCurrentLog(); 783} 784 785void MetricsService::PushPendingLogsToPersistentStorage() { 786 if (state_ < SENDING_INITIAL_STABILITY_LOG) 787 return; // We didn't and still don't have time to get plugin list etc. 788 789 CloseCurrentLog(); 790 log_manager_.PersistUnsentLogs(); 791 792 // If there was a staged and/or current log, then there is now at least one 793 // log waiting to be uploaded. 794 if (log_manager_.has_unsent_logs()) 795 state_ = SENDING_OLD_LOGS; 796} 797 798//------------------------------------------------------------------------------ 799// Transmission of logs methods 800 801void MetricsService::StartSchedulerIfNecessary() { 802 // Never schedule cutting or uploading of logs in test mode. 803 if (test_mode_active_) 804 return; 805 806 // Even if reporting is disabled, the scheduler is needed to trigger the 807 // creation of the initial log, which must be done in order for any logs to be 808 // persisted on shutdown or backgrounding. 809 if (recording_active() && 810 (reporting_active() || state_ < SENDING_INITIAL_STABILITY_LOG)) { 811 scheduler_->Start(); 812 } 813} 814 815void MetricsService::StartScheduledUpload() { 816 // If we're getting no notifications, then the log won't have much in it, and 817 // it's possible the computer is about to go to sleep, so don't upload and 818 // stop the scheduler. 819 // If recording has been turned off, the scheduler doesn't need to run. 820 // If reporting is off, proceed if the initial log hasn't been created, since 821 // that has to happen in order for logs to be cut and stored when persisting. 822 // TODO(stuartmorgan): Call Stop() on the scheduler when reporting and/or 823 // recording are turned off instead of letting it fire and then aborting. 824 if (idle_since_last_transmission_ || 825 !recording_active() || 826 (!reporting_active() && state_ >= SENDING_INITIAL_STABILITY_LOG)) { 827 scheduler_->Stop(); 828 scheduler_->UploadCancelled(); 829 return; 830 } 831 832 // If the callback was to upload an old log, but there no longer is one, 833 // just report success back to the scheduler to begin the ongoing log 834 // callbacks. 835 // TODO(stuartmorgan): Consider removing the distinction between 836 // SENDING_OLD_LOGS and SENDING_CURRENT_LOGS to simplify the state machine 837 // now that the log upload flow is the same for both modes. 838 if (state_ == SENDING_OLD_LOGS && !log_manager_.has_unsent_logs()) { 839 state_ = SENDING_CURRENT_LOGS; 840 scheduler_->UploadFinished(true /* healthy */, false /* no unsent logs */); 841 return; 842 } 843 // If there are unsent logs, send the next one. If not, start the asynchronous 844 // process of finalizing the current log for upload. 845 if (state_ == SENDING_OLD_LOGS) { 846 DCHECK(log_manager_.has_unsent_logs()); 847 log_manager_.StageNextLogForUpload(); 848 SendStagedLog(); 849 } else { 850 client_->CollectFinalMetrics( 851 base::Bind(&MetricsService::OnFinalLogInfoCollectionDone, 852 self_ptr_factory_.GetWeakPtr())); 853 } 854} 855 856void MetricsService::OnFinalLogInfoCollectionDone() { 857 // If somehow there is a log upload in progress, we return and hope things 858 // work out. The scheduler isn't informed since if this happens, the scheduler 859 // will get a response from the upload. 860 DCHECK(!log_upload_in_progress_); 861 if (log_upload_in_progress_) 862 return; 863 864 // Abort if metrics were turned off during the final info gathering. 865 if (!recording_active()) { 866 scheduler_->Stop(); 867 scheduler_->UploadCancelled(); 868 return; 869 } 870 871 StageNewLog(); 872 873 // If logs shouldn't be uploaded, stop here. It's important that this check 874 // be after StageNewLog(), otherwise the previous logs will never be loaded, 875 // and thus the open log won't be persisted. 876 // TODO(stuartmorgan): This is unnecessarily complicated; restructure loading 877 // of previous logs to not require running part of the upload logic. 878 // http://crbug.com/157337 879 if (!reporting_active()) { 880 scheduler_->Stop(); 881 scheduler_->UploadCancelled(); 882 return; 883 } 884 885 SendStagedLog(); 886} 887 888void MetricsService::StageNewLog() { 889 if (log_manager_.has_staged_log()) 890 return; 891 892 switch (state_) { 893 case INITIALIZED: 894 case INIT_TASK_SCHEDULED: // We should be further along by now. 895 NOTREACHED(); 896 return; 897 898 case INIT_TASK_DONE: 899 if (has_initial_stability_log_) { 900 // There's an initial stability log, ready to send. 901 log_manager_.StageNextLogForUpload(); 902 has_initial_stability_log_ = false; 903 state_ = SENDING_INITIAL_STABILITY_LOG; 904 } else { 905 PrepareInitialMetricsLog(); 906 state_ = SENDING_INITIAL_METRICS_LOG; 907 } 908 break; 909 910 case SENDING_OLD_LOGS: 911 NOTREACHED(); // Shouldn't be staging a new log during old log sending. 912 return; 913 914 case SENDING_CURRENT_LOGS: 915 CloseCurrentLog(); 916 OpenNewLog(); 917 log_manager_.StageNextLogForUpload(); 918 break; 919 920 default: 921 NOTREACHED(); 922 return; 923 } 924 925 DCHECK(log_manager_.has_staged_log()); 926} 927 928bool MetricsService::ProvidersHaveStabilityMetrics() { 929 // Check whether any metrics provider has stability metrics. 930 for (size_t i = 0; i < metrics_providers_.size(); ++i) { 931 if (metrics_providers_[i]->HasStabilityMetrics()) 932 return true; 933 } 934 935 return false; 936} 937 938void MetricsService::PrepareInitialStabilityLog() { 939 DCHECK_EQ(INITIALIZED, state_); 940 941 scoped_ptr<MetricsLog> initial_stability_log( 942 CreateLog(MetricsLog::INITIAL_STABILITY_LOG)); 943 944 // Do not call NotifyOnDidCreateMetricsLog here because the stability 945 // log describes stats from the _previous_ session. 946 947 if (!initial_stability_log->LoadSavedEnvironmentFromPrefs()) 948 return; 949 950 log_manager_.PauseCurrentLog(); 951 log_manager_.BeginLoggingWithLog(initial_stability_log.Pass()); 952 953 // Note: Some stability providers may record stability stats via histograms, 954 // so this call has to be after BeginLoggingWithLog(). 955 log_manager_.current_log()->RecordStabilityMetrics( 956 metrics_providers_.get(), base::TimeDelta(), base::TimeDelta()); 957 RecordCurrentStabilityHistograms(); 958 959 // Note: RecordGeneralMetrics() intentionally not called since this log is for 960 // stability stats from a previous session only. 961 962 log_manager_.FinishCurrentLog(); 963 log_manager_.ResumePausedLog(); 964 965 // Store unsent logs, including the stability log that was just saved, so 966 // that they're not lost in case of a crash before upload time. 967 log_manager_.PersistUnsentLogs(); 968 969 has_initial_stability_log_ = true; 970} 971 972void MetricsService::PrepareInitialMetricsLog() { 973 DCHECK(state_ == INIT_TASK_DONE || state_ == SENDING_INITIAL_STABILITY_LOG); 974 975 std::vector<variations::ActiveGroupId> synthetic_trials; 976 GetCurrentSyntheticFieldTrials(&synthetic_trials); 977 initial_metrics_log_->RecordEnvironment(metrics_providers_.get(), 978 synthetic_trials, 979 GetInstallDate()); 980 base::TimeDelta incremental_uptime; 981 base::TimeDelta uptime; 982 GetUptimes(local_state_, &incremental_uptime, &uptime); 983 984 // Histograms only get written to the current log, so make the new log current 985 // before writing them. 986 log_manager_.PauseCurrentLog(); 987 log_manager_.BeginLoggingWithLog(initial_metrics_log_.Pass()); 988 989 // Note: Some stability providers may record stability stats via histograms, 990 // so this call has to be after BeginLoggingWithLog(). 991 MetricsLog* current_log = log_manager_.current_log(); 992 current_log->RecordStabilityMetrics(metrics_providers_.get(), 993 base::TimeDelta(), base::TimeDelta()); 994 current_log->RecordGeneralMetrics(metrics_providers_.get()); 995 RecordCurrentHistograms(); 996 997 log_manager_.FinishCurrentLog(); 998 log_manager_.ResumePausedLog(); 999 1000 // Store unsent logs, including the initial log that was just saved, so 1001 // that they're not lost in case of a crash before upload time. 1002 log_manager_.PersistUnsentLogs(); 1003 1004 DCHECK(!log_manager_.has_staged_log()); 1005 log_manager_.StageNextLogForUpload(); 1006} 1007 1008void MetricsService::SendStagedLog() { 1009 DCHECK(log_manager_.has_staged_log()); 1010 if (!log_manager_.has_staged_log()) 1011 return; 1012 1013 DCHECK(!log_upload_in_progress_); 1014 log_upload_in_progress_ = true; 1015 1016 if (!log_uploader_) { 1017 log_uploader_ = client_->CreateUploader( 1018 kServerUrl, kMimeType, 1019 base::Bind(&MetricsService::OnLogUploadComplete, 1020 self_ptr_factory_.GetWeakPtr())); 1021 } 1022 1023 const std::string hash = 1024 base::HexEncode(log_manager_.staged_log_hash().data(), 1025 log_manager_.staged_log_hash().size()); 1026 bool success = log_uploader_->UploadLog(log_manager_.staged_log(), hash); 1027 UMA_HISTOGRAM_BOOLEAN("UMA.UploadCreation", success); 1028 if (!success) { 1029 // Skip this upload and hope things work out next time. 1030 log_manager_.DiscardStagedLog(); 1031 scheduler_->UploadCancelled(); 1032 log_upload_in_progress_ = false; 1033 return; 1034 } 1035 1036 HandleIdleSinceLastTransmission(true); 1037} 1038 1039 1040void MetricsService::OnLogUploadComplete(int response_code) { 1041 DCHECK(log_upload_in_progress_); 1042 log_upload_in_progress_ = false; 1043 1044 // Log a histogram to track response success vs. failure rates. 1045 UMA_HISTOGRAM_ENUMERATION("UMA.UploadResponseStatus.Protobuf", 1046 ResponseCodeToStatus(response_code), 1047 NUM_RESPONSE_STATUSES); 1048 1049 bool upload_succeeded = response_code == 200; 1050 1051 // Provide boolean for error recovery (allow us to ignore response_code). 1052 bool discard_log = false; 1053 const size_t log_size = log_manager_.staged_log().length(); 1054 if (upload_succeeded) { 1055 UMA_HISTOGRAM_COUNTS_10000("UMA.LogSize.OnSuccess", log_size / 1024); 1056 } else if (log_size > kUploadLogAvoidRetransmitSize) { 1057 UMA_HISTOGRAM_COUNTS("UMA.Large Rejected Log was Discarded", 1058 static_cast<int>(log_size)); 1059 discard_log = true; 1060 } else if (response_code == 400) { 1061 // Bad syntax. Retransmission won't work. 1062 discard_log = true; 1063 } 1064 1065 if (upload_succeeded || discard_log) { 1066 log_manager_.DiscardStagedLog(); 1067 // Store the updated list to disk now that the removed log is uploaded. 1068 log_manager_.PersistUnsentLogs(); 1069 } 1070 1071 if (!log_manager_.has_staged_log()) { 1072 switch (state_) { 1073 case SENDING_INITIAL_STABILITY_LOG: 1074 PrepareInitialMetricsLog(); 1075 SendStagedLog(); 1076 state_ = SENDING_INITIAL_METRICS_LOG; 1077 break; 1078 1079 case SENDING_INITIAL_METRICS_LOG: 1080 state_ = log_manager_.has_unsent_logs() ? SENDING_OLD_LOGS 1081 : SENDING_CURRENT_LOGS; 1082 break; 1083 1084 case SENDING_OLD_LOGS: 1085 if (!log_manager_.has_unsent_logs()) 1086 state_ = SENDING_CURRENT_LOGS; 1087 break; 1088 1089 case SENDING_CURRENT_LOGS: 1090 break; 1091 1092 default: 1093 NOTREACHED(); 1094 break; 1095 } 1096 1097 if (log_manager_.has_unsent_logs()) 1098 DCHECK_LT(state_, SENDING_CURRENT_LOGS); 1099 } 1100 1101 // Error 400 indicates a problem with the log, not with the server, so 1102 // don't consider that a sign that the server is in trouble. 1103 bool server_is_healthy = upload_succeeded || response_code == 400; 1104 // Don't notify the scheduler that the upload is finished if we've only sent 1105 // the initial stability log, but not yet the initial metrics log (treat the 1106 // two as a single unit of work as far as the scheduler is concerned). 1107 if (state_ != SENDING_INITIAL_METRICS_LOG) { 1108 scheduler_->UploadFinished(server_is_healthy, 1109 log_manager_.has_unsent_logs()); 1110 } 1111 1112 if (server_is_healthy) 1113 client_->OnLogUploadComplete(); 1114} 1115 1116void MetricsService::IncrementPrefValue(const char* path) { 1117 int value = local_state_->GetInteger(path); 1118 local_state_->SetInteger(path, value + 1); 1119} 1120 1121void MetricsService::IncrementLongPrefsValue(const char* path) { 1122 int64 value = local_state_->GetInt64(path); 1123 local_state_->SetInt64(path, value + 1); 1124} 1125 1126bool MetricsService::UmaMetricsProperlyShutdown() { 1127 CHECK(clean_shutdown_status_ == CLEANLY_SHUTDOWN || 1128 clean_shutdown_status_ == NEED_TO_SHUTDOWN); 1129 return clean_shutdown_status_ == CLEANLY_SHUTDOWN; 1130} 1131 1132void MetricsService::RegisterSyntheticFieldTrial( 1133 const SyntheticTrialGroup& trial) { 1134 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { 1135 if (synthetic_trial_groups_[i].id.name == trial.id.name) { 1136 if (synthetic_trial_groups_[i].id.group != trial.id.group) { 1137 synthetic_trial_groups_[i].id.group = trial.id.group; 1138 synthetic_trial_groups_[i].start_time = base::TimeTicks::Now(); 1139 } 1140 return; 1141 } 1142 } 1143 1144 SyntheticTrialGroup trial_group = trial; 1145 trial_group.start_time = base::TimeTicks::Now(); 1146 synthetic_trial_groups_.push_back(trial_group); 1147} 1148 1149void MetricsService::RegisterMetricsProvider( 1150 scoped_ptr<metrics::MetricsProvider> provider) { 1151 DCHECK_EQ(INITIALIZED, state_); 1152 metrics_providers_.push_back(provider.release()); 1153} 1154 1155void MetricsService::CheckForClonedInstall( 1156 scoped_refptr<base::SingleThreadTaskRunner> task_runner) { 1157 state_manager_->CheckForClonedInstall(task_runner); 1158} 1159 1160void MetricsService::GetCurrentSyntheticFieldTrials( 1161 std::vector<variations::ActiveGroupId>* synthetic_trials) { 1162 DCHECK(synthetic_trials); 1163 synthetic_trials->clear(); 1164 const MetricsLog* current_log = log_manager_.current_log(); 1165 for (size_t i = 0; i < synthetic_trial_groups_.size(); ++i) { 1166 if (synthetic_trial_groups_[i].start_time <= current_log->creation_time()) 1167 synthetic_trials->push_back(synthetic_trial_groups_[i].id); 1168 } 1169} 1170 1171scoped_ptr<MetricsLog> MetricsService::CreateLog(MetricsLog::LogType log_type) { 1172 return make_scoped_ptr(new MetricsLog(state_manager_->client_id(), 1173 session_id_, 1174 log_type, 1175 client_, 1176 local_state_)); 1177} 1178 1179void MetricsService::RecordCurrentHistograms() { 1180 DCHECK(log_manager_.current_log()); 1181 histogram_snapshot_manager_.PrepareDeltas( 1182 base::Histogram::kNoFlags, base::Histogram::kUmaTargetedHistogramFlag); 1183} 1184 1185void MetricsService::RecordCurrentStabilityHistograms() { 1186 DCHECK(log_manager_.current_log()); 1187 histogram_snapshot_manager_.PrepareDeltas( 1188 base::Histogram::kNoFlags, base::Histogram::kUmaStabilityHistogramFlag); 1189} 1190 1191void MetricsService::LogCleanShutdown() { 1192 // Redundant hack to write pref ASAP. 1193 MarkAppCleanShutdownAndCommit(&clean_exit_beacon_, local_state_); 1194 1195 // Redundant setting to assure that we always reset this value at shutdown 1196 // (and that we don't use some alternate path, and not call LogCleanShutdown). 1197 clean_shutdown_status_ = CLEANLY_SHUTDOWN; 1198 1199 clean_exit_beacon_.WriteBeaconValue(true); 1200 RecordCurrentState(local_state_); 1201 local_state_->SetInteger(metrics::prefs::kStabilityExecutionPhase, 1202 MetricsService::SHUTDOWN_COMPLETE); 1203} 1204 1205bool MetricsService::ShouldLogEvents() { 1206 // We simply don't log events to UMA if there is a single incognito 1207 // session visible. The problem is that we always notify using the orginal 1208 // profile in order to simplify notification processing. 1209 return !client_->IsOffTheRecordSessionActive(); 1210} 1211 1212void MetricsService::RecordBooleanPrefValue(const char* path, bool value) { 1213 DCHECK(IsSingleThreaded()); 1214 local_state_->SetBoolean(path, value); 1215 RecordCurrentState(local_state_); 1216} 1217 1218void MetricsService::RecordCurrentState(PrefService* pref) { 1219 pref->SetInt64(metrics::prefs::kStabilityLastTimestampSec, 1220 base::Time::Now().ToTimeT()); 1221} 1222 1223} // namespace metrics 1224