chromium/chrome/browser/chromeos/reporting/websites/website_usage_observer.cc

// 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 "chrome/browser/chromeos/reporting/websites/website_usage_observer.h"

#include <memory>
#include <optional>

#include "base/functional/bind.h"
#include "base/json/values_util.h"
#include "base/memory/weak_ptr.h"
#include "base/sequence_checker.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/apps/app_service/metrics/website_metrics.h"
#include "chrome/browser/chromeos/reporting/metric_default_utils.h"
#include "chrome/browser/chromeos/reporting/metric_reporting_prefs.h"
#include "chrome/browser/chromeos/reporting/websites/website_metrics_retriever_interface.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/reporting/metrics/reporting_settings.h"
#include "url/gurl.h"

namespace reporting {

WebsiteUsageObserver::WebsiteUsageObserver(
    base::WeakPtr<Profile> profile,
    const ReportingSettings* reporting_settings,
    std::unique_ptr<WebsiteMetricsRetrieverInterface> website_metrics_retriever)
    : profile_(profile),
      reporting_settings_(reporting_settings),
      website_metrics_retriever_(std::move(website_metrics_retriever)) {
  CHECK(website_metrics_retriever_);
  website_metrics_retriever_->GetWebsiteMetrics(
      base::BindOnce(&WebsiteUsageObserver::InitUsageObserver,
                     weak_ptr_factory_.GetWeakPtr()));
}

WebsiteUsageObserver::~WebsiteUsageObserver() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}

void WebsiteUsageObserver::OnUrlUsage(const GURL& url,
                                      base::TimeDelta running_time) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  CHECK(reporting_settings_);
  if (!profile_ || !IsWebsiteUsageTelemetryEnabled() ||
      !IsWebsiteUrlAllowlisted(url, reporting_settings_,
                               kReportWebsiteTelemetryAllowlist)) {
    return;
  }
  if (running_time < metrics::kMinimumWebsiteUsageTime) {
    // Skip if there is no usage in millisecond granularity. Needed because we
    // track website usage in milliseconds while `base::TimeDelta` internals use
    // microsecond granularity.
    return;
  }
  if (!profile_->GetPrefs()->HasPrefPath(kWebsiteUsage)) {
    // No data in the pref store, so we create an empty dictionary for now.
    profile_->GetPrefs()->SetDict(kWebsiteUsage, base::Value::Dict());
  }

  CreateOrUpdateWebsiteUsageEntry(url, running_time);
}

void WebsiteUsageObserver::InitUsageObserver(
    ::apps::WebsiteMetrics* website_metrics) {
  if (!website_metrics) {
    // This can happen if the `WebsiteMetrics` component initialization failed
    // (for example, component was destructed). We just abort initialization of
    // the usage observer when this happens.
    return;
  }
  observer_.Observe(website_metrics);
}

void WebsiteUsageObserver::OnWebsiteMetricsDestroyed() {
  observer_.Reset();
}

void WebsiteUsageObserver::CreateOrUpdateWebsiteUsageEntry(
    const GURL& url,
    const base::TimeDelta& running_time) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  CHECK(profile_);
  ScopedDictPrefUpdate usage_dict_pref(profile_->GetPrefs(), kWebsiteUsage);
  const std::string& url_string = url.spec();
  if (!usage_dict_pref->contains(url_string)) {
    // Create a new entry in the pref store for given URL if one does not exist
    // already.
    usage_dict_pref->Set(url_string, base::TimeDeltaToValue(running_time));
    return;
  }

  // Aggregate and update the running time otherwise.
  const std::optional<const base::TimeDelta> saved_running_time_value =
      base::ValueToTimeDelta(usage_dict_pref->Find(url_string));
  if (saved_running_time_value.has_value()) {
    usage_dict_pref->Set(
        url_string, base::TimeDeltaToValue(saved_running_time_value.value() +
                                           running_time));
  }
}

bool WebsiteUsageObserver::IsWebsiteUsageTelemetryEnabled() const {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  CHECK(reporting_settings_);
  const base::Value::List* allowed_telemetry_types;
  if (!reporting_settings_->GetList(kReportWebsiteTelemetry,
                                    &allowed_telemetry_types)) {
    // Policy likely unset. Disallow website usage telemetry tracking in any
    // case.
    return false;
  }

  // `allowed_telemetry_types` is not expected to change as we iterate and check
  // for `usage` telemetry type below since this is triggered on the main
  // (owning) sequence.
  CHECK(allowed_telemetry_types);
  auto it = std::find_if(
      allowed_telemetry_types->begin(), allowed_telemetry_types->end(),
      [](const base::Value& telemetry_type_value) {
        return telemetry_type_value.GetString() == kWebsiteTelemetryUsageType;
      });
  const auto is_usage_telemetry_reporting_enabled =
      (it != allowed_telemetry_types->end());
  return is_usage_telemetry_reporting_enabled;
}

}  // namespace reporting