blob: 5d786922275e362cecfc124eadeb373d65590827 [file] [log] [blame]
// Copyright 2023 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/user_education/common/session/user_education_session_manager.h"
#include <utility>
#include "base/callback_list.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_forward.h"
#include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/user_metrics.h"
#include "base/time/time.h"
#include "components/user_education/common/session/user_education_idle_observer.h"
#include "components/user_education/common/session/user_education_idle_policy.h"
#include "components/user_education/common/user_education_features.h"
#include "components/user_education/common/user_education_storage_service.h"
namespace user_education {
UserEducationSessionManager::UserEducationSessionManager() = default;
UserEducationSessionManager::~UserEducationSessionManager() = default;
void UserEducationSessionManager::Init(
UserEducationStorageService* storage_service,
std::unique_ptr<UserEducationIdleObserver> idle_observer,
std::unique_ptr<UserEducationIdlePolicy> idle_policy) {
storage_service_ = storage_service;
idle_policy_ = std::move(idle_policy);
idle_policy_->Init(this, storage_service_.get());
// Assume the application is active at application start; this avoids making
// additional system calls during startup.
UpdateLastActiveTime(storage_service_->GetCurrentTime());
// Start observing state.
SetIdleObserver(std::move(idle_observer));
}
void UserEducationSessionManager::MaybeUpdateSessionState() {
if (!storage_service_) {
return;
}
// Determine if a new session could be started.
const auto old_state = storage_service_->ReadSessionData();
const auto now = storage_service_->GetCurrentTime();
if (!idle_policy_->IsNewSession(old_state.start_time,
old_state.most_recent_active_time, now)) {
return;
}
const auto last_active = idle_observer_->MaybeGetNewLastActiveTime();
if (last_active) {
UpdateLastActiveTime(*last_active);
}
}
base::CallbackListSubscription
UserEducationSessionManager::AddNewSessionCallback(
base::RepeatingClosure new_session_callback) {
return new_session_callbacks_.Add(std::move(new_session_callback));
}
bool UserEducationSessionManager::GetNewSessionSinceStartup() const {
return new_session_since_startup_;
}
void UserEducationSessionManager::OnNewSession(
const base::Time old_start_time,
const base::Time old_active_time,
const base::Time new_active_time) {
new_session_since_startup_ = true;
base::RecordAction(
base::UserMetricsAction("UserEducation.Session.ActivePeriodStart"));
// Now starting new session. The Active Period of the old session
// is the difference between old session times.
RecordActivePeriodDuration(old_active_time - old_start_time);
// The now-elapsed Idle Period is difference between now and the
// previous most_recent_active_time.
RecordIdlePeriodDuration(new_active_time - old_active_time);
// Notify any listeners of the new session.
new_session_callbacks_.Notify();
}
void UserEducationSessionManager::OnLastActiveTimeUpdating(base::Time) {}
void UserEducationSessionManager::SetIdleObserver(
std::unique_ptr<UserEducationIdleObserver> new_observer) {
idle_observer_ = std::move(new_observer);
idle_observer_->Init(storage_service_.get());
idle_observer_subscription_ = idle_observer_->AddUpdateCallback(
base::BindRepeating(&UserEducationSessionManager::UpdateLastActiveTime,
base::Unretained(this)));
idle_observer_->StartObserving();
}
void UserEducationSessionManager::RecordActivePeriodDuration(
base::TimeDelta duration) {
// Increments of 1 minute under 1 hour.
base::UmaHistogramCustomCounts(
"UserEducation.Session.ActivePeriodDuration.Min.Under1Hour",
duration.InMinutes(), /*min=*/1,
/*exclusive_max=*/60,
/*buckets=*/60);
// Increments of 15 minutes under 24 hours.
base::UmaHistogramCustomCounts(
"UserEducation.Session.ActivePeriodDuration.Min.Under24Hours",
duration.InMinutes(), /*min=*/1,
/*exclusive_max=*/60 * 24 /* minutes per 24 hours */,
/*buckets=*/24 * 4 /* per 15 minutes */);
}
void UserEducationSessionManager::RecordIdlePeriodDuration(
base::TimeDelta duration) {
// Increments of 15 minutes under 24 hours.
base::UmaHistogramCustomCounts(
"UserEducation.Session.IdlePeriodDuration.Min.Under24Hours",
duration.InMinutes(), /*min=*/1,
/*exclusive_max=*/60 * 24 /* minutes per 24 hours */,
/*buckets=*/24 * 4 /* per 15 minute */);
// Increments of ~13 hours under 28 days.
base::UmaHistogramCustomCounts(
"UserEducation.Session.IdlePeriodDuration.Hr.Under28Days",
duration.InHours(), /*min=*/1,
/*exclusive_max=*/24 * 28 /* hours per 28 days */,
/*buckets=*/50);
}
void UserEducationSessionManager::UpdateLastActiveTime(
base::Time new_active_time) {
CHECK(idle_policy_);
OnLastActiveTimeUpdating(new_active_time);
auto session_data = storage_service_->ReadSessionData();
const auto old_start_time = session_data.start_time;
const auto old_active_time = session_data.most_recent_active_time;
session_data.most_recent_active_time = new_active_time;
const bool is_new_session = idle_policy_->IsNewSession(
old_start_time, old_active_time, new_active_time);
if (is_new_session) {
session_data.start_time = new_active_time;
++session_data.session_number;
}
// Save the session data before calling OnNewSession, since some listeners
// will be relying on the data being current.
storage_service_->SaveSessionData(session_data);
if (is_new_session) {
OnNewSession(old_start_time, old_active_time, new_active_time);
}
}
} // namespace user_education