chromium/chrome/browser/metrics/structured/lacros_structured_metrics_delegate.cc

// Copyright 2021 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/metrics/structured/lacros_structured_metrics_delegate.h"

#include <vector>

#include "base/task/sequenced_task_runner.h"
#include "chromeos/crosapi/mojom/structured_metrics_service.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/metrics/structured/event.h"
#include "components/metrics/structured/structured_metrics_client.h"
#include "mojo/public/cpp/bindings/remote.h"

namespace metrics::structured {

LacrosStructuredMetricsDelegate::LacrosStructuredMetricsDelegate() = default;
LacrosStructuredMetricsDelegate::~LacrosStructuredMetricsDelegate() = default;

void LacrosStructuredMetricsDelegate::SetSequence(
    const scoped_refptr<base::SequencedTaskRunner> sequence_task_runner) {
  sequence_task_runner_ = sequence_task_runner;
}

void LacrosStructuredMetricsDelegate::RecordEvent(Event&& event) {
  DCHECK(IsReadyToRecord());

  // No-op if not properly initialized.
  if (!IsReadyToRecord())
    return;

  // Ensure that the task is always run on the designated
  // |sequence_task_runner_|. Lacros::GetRemote requires that the remote be
  // called within the same sequence.
  if (!sequence_task_runner_->RunsTasksInCurrentSequence()) {
    sequence_task_runner_->PostTask(
        FROM_HERE,
        base::BindOnce(&LacrosStructuredMetricsDelegate::RecordEvent,
                       weak_ptr_factory_.GetWeakPtr(), std::move(event)));
    return;
  }

  auto* service = chromeos::LacrosService::Get();

  // If the service is not available yet, keep it in memory until the next
  // record call and service is available.
  if (!service ||
      !service->IsAvailable<crosapi::mojom::StructuredMetricsService>()) {
    // Notify observers.
    for (auto& observer : observers_)
      observer.OnRecord(event);
    enqueued_events_.emplace_back(std::move(event));
    return;
  }

  const auto& remote =
      service->GetRemote<crosapi::mojom::StructuredMetricsService>();

  // Lacros service has no observer to callback when a service is bound and
  // ready. Thus, events will be enqueued best effort.
  if (!enqueued_events_.empty()) {
    for (auto& observer : observers_)
      observer.OnFlush();

    remote->Record(std::move(enqueued_events_));
    enqueued_events_.clear();
  }

  // Notify observers.
  for (auto& observer : observers_)
    observer.OnRecord(event);

  std::vector<Event> events;
  events.emplace_back(std::move(event));
  remote->Record(std::move(events));
}

bool LacrosStructuredMetricsDelegate::IsReadyToRecord() const {
  return static_cast<bool>(sequence_task_runner_);
}

void LacrosStructuredMetricsDelegate::AddObserver(Observer* observer) {
  observers_.AddObserver(observer);
}

void LacrosStructuredMetricsDelegate::RemoveObserver(Observer* observer) {
  observers_.RemoveObserver(observer);
}

}  // namespace metrics::structured