| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/guest_os/guest_os_engagement_metrics.h" |
| |
| #include <string> |
| #include <utility> |
| |
| #include "base/logging.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/system/sys_info.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/default_tick_clock.h" |
| #include "chromeos/ash/components/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/dbus/power_manager/idle.pb.h" |
| #include "components/exo/wm_helper.h" |
| #include "components/guest_os/guest_os_prefs.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/session_manager/core/session_manager.h" |
| |
| namespace guest_os { |
| |
| namespace { |
| |
| constexpr base::TimeDelta kUpdateEngagementTimePeriod = base::Minutes(1); |
| constexpr base::TimeDelta kSaveEngagementTimeToPrefsPeriod = base::Minutes(30); |
| |
| int GetDayId(const base::Clock* clock) { |
| return clock->Now().LocalMidnight().since_origin().InDays(); |
| } |
| |
| } // namespace |
| |
| GuestOsEngagementMetrics::GuestOsEngagementMetrics( |
| PrefService* pref_service, |
| WindowMatcher window_matcher, |
| const std::string& pref_prefix, |
| const std::string& uma_name) |
| : GuestOsEngagementMetrics(pref_service, |
| window_matcher, |
| pref_prefix, |
| uma_name, |
| base::DefaultClock::GetInstance(), |
| base::DefaultTickClock::GetInstance()) {} |
| |
| // Private, for testing use only |
| GuestOsEngagementMetrics::GuestOsEngagementMetrics( |
| PrefService* pref_service, |
| WindowMatcher window_matcher, |
| const std::string& pref_prefix, |
| const std::string& uma_name, |
| const base::Clock* clock, |
| const base::TickClock* tick_clock) |
| : pref_service_(pref_service), |
| window_matcher_(window_matcher), |
| pref_prefix_(pref_prefix), |
| uma_name_(uma_name), |
| clock_(clock), |
| tick_clock_(tick_clock), |
| last_update_ticks_(tick_clock_->NowTicks()) { |
| // WMHelper, SessionManager and PowerManagerClient may not exist in tests. |
| if (exo::WMHelper::HasInstance()) |
| exo::WMHelper::GetInstance()->AddActivationObserver(this); |
| |
| if (session_manager::SessionManager::Get()) { |
| session_active_ = |
| (session_manager::SessionManager::Get()->session_state() == |
| session_manager::SessionState::ACTIVE); |
| session_manager::SessionManager::Get()->AddObserver(this); |
| } |
| |
| if (chromeos::PowerManagerClient::Get()) |
| chromeos::PowerManagerClient::Get()->AddObserver(this); |
| |
| DCHECK(pref_service_); |
| RestoreEngagementTimeFromPrefs(); |
| update_engagement_time_timer_.Start( |
| FROM_HERE, kUpdateEngagementTimePeriod, this, |
| &GuestOsEngagementMetrics::UpdateEngagementTime); |
| save_engagement_time_to_prefs_timer_.Start( |
| FROM_HERE, kSaveEngagementTimeToPrefsPeriod, this, |
| &GuestOsEngagementMetrics::SaveEngagementTimeToPrefs); |
| } |
| |
| GuestOsEngagementMetrics::~GuestOsEngagementMetrics() { |
| save_engagement_time_to_prefs_timer_.Stop(); |
| update_engagement_time_timer_.Stop(); |
| UpdateEngagementTime(); |
| SaveEngagementTimeToPrefs(); |
| |
| // Observers for WMHelper, SessionManager and PowerManagerClient may not |
| // have been added in tests. |
| if (chromeos::PowerManagerClient::Get()) |
| chromeos::PowerManagerClient::Get()->RemoveObserver(this); |
| if (session_manager::SessionManager::Get()) |
| session_manager::SessionManager::Get()->RemoveObserver(this); |
| |
| // If WMHelper is already destroyed, do nothing. |
| // TODO(crbug.com/748380): Fix shutdown order. |
| if (exo::WMHelper::HasInstance()) |
| exo::WMHelper::GetInstance()->RemoveActivationObserver(this); |
| } |
| |
| void GuestOsEngagementMetrics::SetBackgroundActive(bool background_active) { |
| if (background_active_ == background_active) |
| return; |
| UpdateEngagementTime(); |
| background_active_ = background_active; |
| } |
| |
| void GuestOsEngagementMetrics::OnWindowActivated( |
| wm::ActivationChangeObserver::ActivationReason reason, |
| aura::Window* gained_active, |
| aura::Window* lost_active) { |
| UpdateEngagementTime(); |
| matched_window_active_ = gained_active && window_matcher_.Run(gained_active); |
| } |
| |
| void GuestOsEngagementMetrics::OnSessionStateChanged() { |
| UpdateEngagementTime(); |
| session_active_ = session_manager::SessionManager::Get()->session_state() == |
| session_manager::SessionState::ACTIVE; |
| } |
| |
| void GuestOsEngagementMetrics::ScreenIdleStateChanged( |
| const power_manager::ScreenIdleState& proto) { |
| UpdateEngagementTime(); |
| screen_dimmed_ = proto.dimmed(); |
| } |
| |
| void GuestOsEngagementMetrics::RestoreEngagementTimeFromPrefs() { |
| // Restore accumulated results only if they were recorded on the same OS |
| // version. |
| if (pref_service_->GetString(pref_prefix_ + |
| prefs::kEngagementTimeOsVersion) == |
| base::SysInfo::OperatingSystemVersion()) { |
| day_id_ = |
| pref_service_->GetInteger(pref_prefix_ + prefs::kEngagementTimeDayId); |
| engagement_time_total_ = |
| pref_service_->GetTimeDelta(pref_prefix_ + prefs::kEngagementTimeTotal); |
| engagement_time_foreground_ = pref_service_->GetTimeDelta( |
| pref_prefix_ + prefs::kEngagementTimeForeground); |
| engagement_time_background_ = pref_service_->GetTimeDelta( |
| pref_prefix_ + prefs::kEngagementTimeBackground); |
| } else { |
| ResetEngagementTimePrefs(); |
| } |
| |
| RecordEngagementTimeToUmaIfNeeded(); |
| } |
| |
| void GuestOsEngagementMetrics::SaveEngagementTimeToPrefs() { |
| DCHECK(pref_service_); |
| |
| pref_service_->SetString(pref_prefix_ + prefs::kEngagementTimeOsVersion, |
| base::SysInfo::OperatingSystemVersion()); |
| pref_service_->SetInteger(pref_prefix_ + prefs::kEngagementTimeDayId, |
| day_id_); |
| pref_service_->SetTimeDelta(pref_prefix_ + prefs::kEngagementTimeTotal, |
| engagement_time_total_); |
| pref_service_->SetTimeDelta(pref_prefix_ + prefs::kEngagementTimeForeground, |
| engagement_time_foreground_); |
| pref_service_->SetTimeDelta(pref_prefix_ + prefs::kEngagementTimeBackground, |
| engagement_time_background_); |
| } |
| |
| void GuestOsEngagementMetrics::UpdateEngagementTime() { |
| VLOG(2) << "last state: screen_dimmed=" << screen_dimmed_ |
| << " session_active=" << session_active_ |
| << " background_active=" << background_active_ |
| << " matched_window_active=" << matched_window_active_; |
| |
| base::TimeTicks now = tick_clock_->NowTicks(); |
| base::TimeDelta elapsed = now - last_update_ticks_; |
| |
| if (ShouldAccumulateEngagementTotalTime()) { |
| VLOG(2) << "accumulate to total time " << elapsed; |
| engagement_time_total_ += elapsed; |
| if (ShouldAccumulateEngagementForegroundTime()) { |
| VLOG(2) << "accumulate to foreground time " << elapsed; |
| engagement_time_foreground_ += elapsed; |
| } else if (ShouldAccumulateEngagementBackgroundTime()) { |
| VLOG(2) << "accumulate to background time " << elapsed; |
| engagement_time_background_ += elapsed; |
| } |
| } |
| |
| last_update_ticks_ = now; |
| RecordEngagementTimeToUmaIfNeeded(); |
| } |
| |
| void GuestOsEngagementMetrics::RecordEngagementTimeToUmaIfNeeded() { |
| if (!ShouldRecordEngagementTimeToUma()) |
| return; |
| VLOG(2) << "day changed, recording engagement time to UMA"; |
| UmaHistogramCustomTimes(uma_name_ + ".EngagementTime.Total", |
| engagement_time_total_, base::Seconds(1), |
| base::Days(1) + kUpdateEngagementTimePeriod, 50); |
| UmaHistogramCustomTimes( |
| uma_name_ + ".EngagementTime." + uma_name_ + "Total", |
| engagement_time_foreground_ + engagement_time_background_, |
| base::Seconds(1), base::Days(1) + kUpdateEngagementTimePeriod, 50); |
| UmaHistogramCustomTimes(uma_name_ + ".EngagementTime.Foreground", |
| engagement_time_foreground_, base::Seconds(1), |
| base::Days(1) + kUpdateEngagementTimePeriod, 50); |
| UmaHistogramCustomTimes(uma_name_ + ".EngagementTime.Background", |
| engagement_time_background_, base::Seconds(1), |
| base::Days(1) + kUpdateEngagementTimePeriod, 50); |
| ResetEngagementTimePrefs(); |
| } |
| |
| void GuestOsEngagementMetrics::ResetEngagementTimePrefs() { |
| day_id_ = GetDayId(clock_); |
| engagement_time_total_ = base::TimeDelta(); |
| engagement_time_foreground_ = base::TimeDelta(); |
| engagement_time_background_ = base::TimeDelta(); |
| SaveEngagementTimeToPrefs(); |
| } |
| |
| bool GuestOsEngagementMetrics::ShouldAccumulateEngagementTotalTime() const { |
| return session_active_ && !screen_dimmed_; |
| } |
| |
| bool GuestOsEngagementMetrics::ShouldAccumulateEngagementForegroundTime() |
| const { |
| return matched_window_active_; |
| } |
| |
| bool GuestOsEngagementMetrics::ShouldAccumulateEngagementBackgroundTime() |
| const { |
| return background_active_; |
| } |
| |
| bool GuestOsEngagementMetrics::ShouldRecordEngagementTimeToUma() const { |
| return day_id_ != GetDayId(clock_); |
| } |
| |
| std::unique_ptr<GuestOsEngagementMetrics> |
| GuestOsEngagementMetrics::GetEngagementMetricsForTesting( |
| PrefService* pref_service, |
| WindowMatcher window_matcher, |
| const std::string& pref_prefix, |
| const std::string& uma_name, |
| const base::Clock* clock, |
| const base::TickClock* tick_clock) { |
| GuestOsEngagementMetrics* metrics = new GuestOsEngagementMetrics( |
| pref_service, window_matcher, pref_prefix, uma_name, clock, tick_clock); |
| // WrapUnique is needed because the constructor is private. |
| return base::WrapUnique(metrics); |
| } |
| |
| } // namespace guest_os |