chromium/chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ash/child_accounts/family_user_chrome_activity_metrics.h"

#include "base/check.h"
#include "base/containers/contains.h"
#include "base/metrics/histogram_functions.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_time_limit_utils.h"
#include "chrome/browser/ash/child_accounts/time_limits/app_types.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/common/pref_names.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"

namespace ash {

namespace {

constexpr base::TimeDelta kMinChromeDuration = base::Minutes(1);
constexpr base::TimeDelta kMaxChromeDuration = base::Days(7);
constexpr int kChromeDurationBuckets = 100;

}  // namespace

// static
const char FamilyUserChromeActivityMetrics::
    kChromeBrowserEngagementDurationHistogramName[] =
        "FamilyUser.ChromeBrowserEngagement.Duration2";

// static
void FamilyUserChromeActivityMetrics::RegisterProfilePrefs(
    PrefRegistrySimple* registry) {
  registry->RegisterTimeDeltaPref(
      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
      base::TimeDelta());
}

FamilyUserChromeActivityMetrics::FamilyUserChromeActivityMetrics(
    Profile* profile)
    : pref_service_(profile->GetPrefs()), app_service_wrapper_(profile) {
  DCHECK(pref_service_);
  app_service_wrapper_.AddObserver(this);
  UsageTimeStateNotifier::GetInstance()->AddObserver(this);
}

FamilyUserChromeActivityMetrics::~FamilyUserChromeActivityMetrics() {
  DCHECK_EQ(base::Time(), active_duration_start_);
  app_service_wrapper_.RemoveObserver(this);
  UsageTimeStateNotifier::GetInstance()->RemoveObserver(this);
}

void FamilyUserChromeActivityMetrics::OnNewDay() {
  base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
  if (unreported_duration <= base::TimeDelta())
    return;
  base::UmaHistogramCustomTimes(kChromeBrowserEngagementDurationHistogramName,
                                unreported_duration, kMinChromeDuration,
                                kMaxChromeDuration, kChromeDurationBuckets);
  pref_service_->ClearPref(
      prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
}

void FamilyUserChromeActivityMetrics::SetActiveSessionStartForTesting(
    base::Time time) {
  active_duration_start_ = time;
}

void FamilyUserChromeActivityMetrics::OnAppActive(
    const app_time::AppId& app_id,
    const base::UnguessableToken& instance_id,
    base::Time timestamp) {
  if (app_id != app_time::GetChromeAppId())
    return;

  if (active_browser_instances_.empty())
    UpdateUserEngagement(/*is_user_active=*/true);

  active_browser_instances_.insert(instance_id);
}

void FamilyUserChromeActivityMetrics::OnAppInactive(
    const app_time::AppId& app_id,
    const base::UnguessableToken& instance_id,
    base::Time timestamp) {
  if (app_id != app_time::GetChromeAppId())
    return;

  // OnAppInactive might get called for the same instance multiple times. The
  // |instance| might have already been removed from
  // |active_browser_instances_|.
  if (!base::Contains(active_browser_instances_, instance_id))
    return;

  active_browser_instances_.erase(instance_id);
  if (active_browser_instances_.empty())
    UpdateUserEngagement(/*is_user_active=*/false);
}

void FamilyUserChromeActivityMetrics::OnUsageTimeStateChange(
    UsageTimeStateNotifier::UsageTimeState state) {
  if (active_browser_instances_.empty())
    return;

  // We should exclude the duration when UsageTimeState is not active and
  // `active_browser_instances_` has instance. For example, when screen is off,
  // the browser might still be active.
  UpdateUserEngagement(state == UsageTimeStateNotifier::UsageTimeState::ACTIVE);
}

void FamilyUserChromeActivityMetrics::UpdateUserEngagement(
    bool is_user_active) {
  base::Time now = base::Time::Now();
  if (is_user_active) {
    active_duration_start_ = now;
  } else {
    if (base::Time() < active_duration_start_ && active_duration_start_ < now) {
      base::TimeDelta unreported_duration = pref_service_->GetTimeDelta(
          prefs::kFamilyUserMetricsChromeBrowserEngagementDuration);
      pref_service_->SetTimeDelta(
          prefs::kFamilyUserMetricsChromeBrowserEngagementDuration,
          unreported_duration + now - active_duration_start_);
    }

    active_duration_start_ = base::Time();
  }
}

}  // namespace ash