chromium/chromeos/ash/components/nearby/presence/nearby_presence_service_impl.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 "chromeos/ash/components/nearby/presence/nearby_presence_service_impl.h"

#include <vector>

#include "base/check.h"
#include "base/logging.h"
#include "chromeos/ash/components/nearby/presence/conversions/nearby_presence_conversions.h"
#include "chromeos/ash/components/nearby/presence/credentials/nearby_presence_credential_manager_impl.h"
#include "chromeos/ash/components/nearby/presence/metrics/nearby_presence_metrics.h"
#include "chromeos/ash/components/nearby/presence/nearby_presence_connections_manager.h"
#include "chromeos/ash/components/nearby/presence/nearby_presence_service_enum_coversions.h"
#include "chromeos/ash/components/nearby/presence/prefs/nearby_presence_prefs.h"
#include "chromeos/ash/services/nearby/public/mojom/nearby_presence.mojom.h"
#include "components/cross_device/logging/logging.h"
#include "components/prefs/pref_service.h"
#include "components/push_notification/push_notification_service.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace {

::nearby::presence::PresenceDevice BuildPresenceDevice(
    ash::nearby::presence::mojom::PresenceDevicePtr device) {
  ::nearby::presence::PresenceDevice presence_device(device->endpoint_id);
  presence_device.SetDeviceIdentityMetaData(
      ash::nearby::presence::MetadataFromMojom(device->metadata.get()));
  for (auto action : device->actions) {
    presence_device.AddAction(static_cast<uint32_t>(action));
  }

  if (device->decrypt_shared_credential.get()) {
    presence_device.SetDecryptSharedCredential(
        ash::nearby::presence::SharedCredentialFromMojom(
            device->decrypt_shared_credential.get()));
  }

  return presence_device;
}

}  // namespace

namespace ash::nearby::presence {

NearbyPresenceServiceImpl::NearbyPresenceServiceImpl(
    PrefService* pref_service,
    ash::nearby::NearbyProcessManager* process_manager,
    signin::IdentityManager* identity_manager,
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
    push_notification::PushNotificationService* push_notification_service)
    : PushNotificationClient(push_notification::ClientId::kNearbyPresence),
      pref_service_(pref_service),
      process_manager_(process_manager),
      identity_manager_(identity_manager),
      url_loader_factory_(url_loader_factory),
      push_notification_service_(push_notification_service) {
  CHECK(pref_service_);
  CHECK(process_manager_);
  CHECK(identity_manager_);
  CHECK(url_loader_factory_);

  CHECK(push_notification_service_);
  push_notification_service_->GetPushNotificationClientManager()
      ->AddPushNotificationClient(this);
}

NearbyPresenceServiceImpl::~NearbyPresenceServiceImpl() {
  push_notification_service_->GetPushNotificationClientManager()
      ->RemovePushNotificationClient(GetClientId());
}

void NearbyPresenceServiceImpl::StartScan(
    ScanFilter scan_filter,
    ScanDelegate* scan_delegate,
    base::OnceCallback<void(std::unique_ptr<ScanSession>, enums::StatusCode)>
        on_start_scan_callback) {
  if (!SetProcessReference()) {
    LOG(ERROR) << "Failed to create process reference.";
    std::move(on_start_scan_callback)
        .Run(/*scan_session=*/nullptr,
             /*status=*/enums::StatusCode::kFailedToStartProcess);

    metrics::RecordScanRequestResult(enums::StatusCode::kFailedToStartProcess);
    return;
  }

  if (!scan_observer_.is_bound()) {
    process_reference_->GetNearbyPresence()->SetScanObserver(
        scan_observer_.BindNewPipeAndPassRemote());
  }

  CHECK(scan_delegate);
  std::vector<PresenceIdentityType> identity_types;
  identity_types.push_back(
      ConvertToMojomIdentityType(scan_filter.identity_type_));
  std::vector<mojom::PresenceScanFilterPtr> filters;
  auto filter = PresenceFilter::New(mojom::PresenceDeviceType::kChromeos);
  filters.push_back(std::move(filter));

  start_scan_start_time_ = base::TimeTicks::Now();

  process_reference_->GetNearbyPresence()->StartScan(
      mojom::ScanRequest::New(/*account_name=*/std::string(), identity_types,
                              std::move(filters)),
      base::BindOnce(&NearbyPresenceServiceImpl::OnScanStarted,
                     weak_ptr_factory_.GetWeakPtr(), scan_delegate,
                     std::move(on_start_scan_callback)));
}

void NearbyPresenceServiceImpl::Initialize(
    base::OnceClosure on_initialized_callback) {
  if (!SetProcessReference()) {
    LOG(ERROR) << "Failed to create process reference.";
    return;
  }

  CHECK(process_reference_);
  CHECK(process_reference_->GetNearbyPresence());
  CHECK(NearbyPresenceCredentialManagerImpl::Creator::Get());
  NearbyPresenceCredentialManagerImpl::Creator::Get()->Create(
      pref_service_, identity_manager_, url_loader_factory_,
      process_reference_->GetNearbyPresence(),
      base::BindOnce(&NearbyPresenceServiceImpl::OnCredentialManagerInitialized,
                     weak_ptr_factory_.GetWeakPtr(),
                     std::move(on_initialized_callback)));
}

void NearbyPresenceServiceImpl::UpdateCredentials() {
  // If the `credential_manager_` field is non-null, it means the initialization
  // flow has already occurred, and we can move forward with updating
  // credentials.
  if (credential_manager_) {
    CD_LOG(VERBOSE, Feature::NEARBY_INFRA)
        << __func__ << ": Initiating updating credentials.";
    credential_manager_->UpdateCredentials();
    return;
  }

  CD_LOG(VERBOSE, Feature::NEARBY_INFRA)
      << __func__
      << ": Attempted to update credentials, but "
         "CredentialManager was not yet initialized.";

  // Otherwise, initialize a `CredentialManager` before updating credentials.
  Initialize(
      base::BindOnce(&NearbyPresenceServiceImpl::
                         UpdateCredentialsAfterCredentialManagerInitialized,
                     weak_ptr_factory_.GetWeakPtr()));
}

std::unique_ptr<NearbyPresenceConnectionsManager>
NearbyPresenceServiceImpl::CreateNearbyPresenceConnectionsManager() {
  return std::make_unique<NearbyPresenceConnectionsManager>(process_manager_);
}

void NearbyPresenceServiceImpl::Shutdown() {
  process_reference_.reset();
  scan_delegate_set_.clear();
}

void NearbyPresenceServiceImpl::OnDeviceFound(mojom::PresenceDevicePtr device) {
  auto build_device = BuildPresenceDevice(std::move(device));
  metrics::RecordDeviceFoundLatency(base::TimeTicks::Now() -
                                    start_scan_start_time_);
  for (ScanDelegate* delegate : scan_delegate_set_) {
    delegate->OnPresenceDeviceFound(build_device);
  }
}

void NearbyPresenceServiceImpl::OnDeviceChanged(
    mojom::PresenceDevicePtr device) {
  auto build_device = BuildPresenceDevice(std::move(device));
  for (ScanDelegate* delegate : scan_delegate_set_) {
    delegate->OnPresenceDeviceChanged(build_device);
  }
}

void NearbyPresenceServiceImpl::OnDeviceLost(mojom::PresenceDevicePtr device) {
  auto build_device = BuildPresenceDevice(std::move(device));
  for (ScanDelegate* delegate : scan_delegate_set_) {
    delegate->OnPresenceDeviceLost(build_device);
  }
}

void NearbyPresenceServiceImpl::OnMessageReceived(
    base::flat_map<std::string, std::string> message) {
  CD_LOG(VERBOSE, Feature::NEARBY_INFRA)
      << __func__ << ": Push notification message recieved.";
  if ((message.at(push_notification::kNotificationClientIdKey) ==
       kNearbyPresencePushNotificationClientId) &&
      (message.at(push_notification::kNotificationTypeIdKey) ==
       kNearbyPresencePushNotificationTypeId)) {
    // TODO(b/319286048): Check for action specific information.
    CD_LOG(ERROR, Feature::NEARBY_INFRA)
        << __func__
        << ": Push notification message is correctly "
           "formatted. Updating credentials now.";
    UpdateCredentials();
  } else {
    CD_LOG(VERBOSE, Feature::NEARBY_INFRA)
        << __func__
        << ": Push notification message is malformed. Discarding message.";
  }
}

bool NearbyPresenceServiceImpl::SetProcessReference() {
  if (!process_reference_) {
    process_reference_ = process_manager_->GetNearbyProcessReference(
        base::BindOnce(&NearbyPresenceServiceImpl::OnNearbyProcessStopped,
                       weak_ptr_factory_.GetWeakPtr()));

    if (!process_reference_) {
      // TODO(b/277819923): add log here.
      return false;
    }
  }
  return true;
}

void NearbyPresenceServiceImpl::OnScanStarted(
    ScanDelegate* scan_delegate,
    base::OnceCallback<void(std::unique_ptr<ScanSession>, enums::StatusCode)>
        on_start_scan_callback,
    mojo::PendingRemote<mojom::ScanSession> pending_remote,
    mojo_base::mojom::AbslStatusCode status) {
  std::unique_ptr<ScanSession> scan_session;
  if (status == mojo_base::mojom::AbslStatusCode::kOk) {
    scan_session = std::make_unique<ScanSession>(
        std::move(pending_remote),
        base::BindOnce(&NearbyPresenceServiceImpl::OnScanSessionDisconnect,
                       weak_ptr_factory_.GetWeakPtr(), scan_delegate));
    scan_delegate_set_.insert(scan_delegate);
  }
  std::move(on_start_scan_callback)
      .Run(std::move(scan_session), enums::ConvertToPresenceStatus(status));

  metrics::RecordScanRequestResult(enums::ConvertToPresenceStatus(status));
}

void NearbyPresenceServiceImpl::OnScanSessionDisconnect(
    ScanDelegate* scan_delegate) {
  CHECK(scan_delegate);
  for (auto it = scan_delegate_set_.begin(); it != scan_delegate_set_.end();
       it++) {
    if (*it == scan_delegate) {
      scan_delegate->OnScanSessionInvalidated();
      scan_delegate_set_.erase(it);
      return;
    }
  }
}

void NearbyPresenceServiceImpl::OnNearbyProcessStopped(
    ash::nearby::NearbyProcessManager::NearbyProcessShutdownReason
        shutdown_reason) {
  LOG(WARNING) << __func__ << ": Nearby process stopped.";
  metrics::RecordNearbyProcessShutdownReason(shutdown_reason);
  Shutdown();
}

void NearbyPresenceServiceImpl::OnCredentialManagerInitialized(
    base::OnceClosure on_initialized_callback,
    std::unique_ptr<NearbyPresenceCredentialManager>
        initialized_credential_manager) {
  credential_manager_ = std::move(initialized_credential_manager);
  std::move(on_initialized_callback).Run();
}

void NearbyPresenceServiceImpl::
    UpdateCredentialsAfterCredentialManagerInitialized() {
  CHECK(credential_manager_);
  credential_manager_->UpdateCredentials();
}
}  // namespace ash::nearby::presence