chromium/chromeos/ash/services/multidevice_setup/host_status_provider_impl.cc

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

#include "chromeos/ash/services/multidevice_setup/host_status_provider_impl.h"

#include <algorithm>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/time.h"
#include "chromeos/ash/components/multidevice/logging/logging.h"
#include "chromeos/ash/services/multidevice_setup/eligible_host_devices_provider.h"

namespace ash {

namespace multidevice_setup {

namespace {

constexpr base::TimeDelta kHostStatusLoggingPeriod = base::Minutes(30);

}  // namespace

// static
HostStatusProviderImpl::Factory*
    HostStatusProviderImpl::Factory::test_factory_ = nullptr;

// static
std::unique_ptr<HostStatusProvider> HostStatusProviderImpl::Factory::Create(
    EligibleHostDevicesProvider* eligible_host_devices_provider,
    HostBackendDelegate* host_backend_delegate,
    HostVerifier* host_verifier,
    device_sync::DeviceSyncClient* device_sync_client) {
  if (test_factory_) {
    return test_factory_->CreateInstance(eligible_host_devices_provider,
                                         host_backend_delegate, host_verifier,
                                         device_sync_client);
  }

  return base::WrapUnique(new HostStatusProviderImpl(
      eligible_host_devices_provider, host_backend_delegate, host_verifier,
      device_sync_client));
}

// static
void HostStatusProviderImpl::Factory::SetFactoryForTesting(
    Factory* test_factory) {
  test_factory_ = test_factory;
}

HostStatusProviderImpl::Factory::~Factory() = default;

HostStatusProviderImpl::HostStatusProviderImpl(
    EligibleHostDevicesProvider* eligible_host_devices_provider,
    HostBackendDelegate* host_backend_delegate,
    HostVerifier* host_verifier,
    device_sync::DeviceSyncClient* device_sync_client)
    : eligible_host_devices_provider_(eligible_host_devices_provider),
      host_backend_delegate_(host_backend_delegate),
      host_verifier_(host_verifier),
      current_status_and_device_(mojom::HostStatus::kNoEligibleHosts,
                                 std::nullopt /* host_device */) {
  host_backend_delegate_->AddObserver(this);
  host_verifier_->AddObserver(this);
  eligible_host_devices_provider_->AddObserver(this);

  CheckForUpdatedStatusAndNotifyIfChanged(
      /*force_notify_host_status_change=*/false);

  RecordMultiDeviceHostStatus();
  host_status_metric_timer_.Start(
      FROM_HERE, kHostStatusLoggingPeriod,
      base::BindRepeating(&HostStatusProviderImpl::RecordMultiDeviceHostStatus,
                          base::Unretained(this)));
}

HostStatusProviderImpl::~HostStatusProviderImpl() {
  host_backend_delegate_->RemoveObserver(this);
  host_verifier_->RemoveObserver(this);
  eligible_host_devices_provider_->RemoveObserver(this);
}

HostStatusProvider::HostStatusWithDevice
HostStatusProviderImpl::GetHostWithStatus() const {
  return current_status_and_device_;
}

void HostStatusProviderImpl::OnHostChangedOnBackend() {
  CheckForUpdatedStatusAndNotifyIfChanged(
      /*force_notify_host_status_change=*/false);
}

void HostStatusProviderImpl::OnPendingHostRequestChange() {
  CheckForUpdatedStatusAndNotifyIfChanged(
      /*force_notify_host_status_change=*/false);
}

void HostStatusProviderImpl::OnHostVerified() {
  CheckForUpdatedStatusAndNotifyIfChanged(
      /*force_notify_host_status_change=*/false);
}

void HostStatusProviderImpl::OnEligibleDevicesSynced() {
  CheckForUpdatedStatusAndNotifyIfChanged(
      /*force_notify_host_status_change=*/true);
}

void HostStatusProviderImpl::CheckForUpdatedStatusAndNotifyIfChanged(
    bool force_notify_host_status_change) {
  HostStatusWithDevice current_status_and_device = GetCurrentStatus();
  if (current_status_and_device == current_status_and_device_) {
    if (force_notify_host_status_change) {
      // If the RemoteDevice the host device references has changed, but not its
      // contents, fire a host status change. Note that since the status doesn't
      // actually change, neither logging nor metric collection should occur.
      NotifyHostStatusChange(current_status_and_device_.host_status(),
                             current_status_and_device_.host_device());
    }
    return;
  }

  PA_LOG(INFO) << "HostStatusProviderImpl::"
               << "CheckForUpdatedStatusAndNotifyIfChanged(): Host status "
               << "changed. New status: "
               << current_status_and_device.host_status()
               << ", Old status: " << current_status_and_device_.host_status()
               << ", Host device: "
               << (current_status_and_device.host_device()
                       ? current_status_and_device.host_device()
                             ->GetInstanceIdDeviceIdForLogs()
                       : "[no host]");

  current_status_and_device_ = current_status_and_device;
  NotifyHostStatusChange(current_status_and_device_.host_status(),
                         current_status_and_device_.host_device());
  RecordMultiDeviceHostStatus();
}

HostStatusProvider::HostStatusWithDevice
HostStatusProviderImpl::GetCurrentStatus() {
  if (host_verifier_->IsHostVerified()) {
    return HostStatusWithDevice(
        mojom::HostStatus::kHostVerified,
        *host_backend_delegate_->GetMultiDeviceHostFromBackend());
  }

  if (host_backend_delegate_->GetMultiDeviceHostFromBackend() &&
      !host_backend_delegate_->HasPendingHostRequest()) {
    return HostStatusWithDevice(
        mojom::HostStatus::kHostSetButNotYetVerified,
        *host_backend_delegate_->GetMultiDeviceHostFromBackend());
  }

  if (host_backend_delegate_->HasPendingHostRequest() &&
      host_backend_delegate_->GetPendingHostRequest()) {
    return HostStatusWithDevice(
        mojom::HostStatus::kHostSetLocallyButWaitingForBackendConfirmation,
        *host_backend_delegate_->GetPendingHostRequest());
  }

  if (!eligible_host_devices_provider_->GetEligibleHostDevices().empty()) {
    return HostStatusWithDevice(
        mojom::HostStatus::kEligibleHostExistsButNoHostSet,
        std::nullopt /* host_device */);
  }

  return HostStatusWithDevice(mojom::HostStatus::kNoEligibleHosts,
                              std::nullopt /* host_device */);
}

void HostStatusProviderImpl::RecordMultiDeviceHostStatus() {
  base::UmaHistogramEnumeration("MultiDevice.Setup.HostStatus",
                                current_status_and_device_.host_status());
}

}  // namespace multidevice_setup

}  // namespace ash