chromium/ash/quick_pair/keyed_service/quick_pair_metrics_logger.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 "ash/quick_pair/keyed_service/quick_pair_metrics_logger.h"

#include "ash/constants/ash_pref_names.h"
#include "ash/quick_pair/common/device.h"
#include "ash/quick_pair/common/fast_pair/fast_pair_feature_usage_metrics_logger.h"
#include "ash/quick_pair/common/fast_pair/fast_pair_metrics.h"
#include "ash/quick_pair/common/pair_failure.h"
#include "ash/quick_pair/repository/fast_pair/device_metadata.h"
#include "ash/quick_pair/repository/fast_pair_repository.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "base/containers/contains.h"
#include "components/cross_device/logging/logging.h"
#include "components/prefs/pref_service.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"

namespace ash {
namespace quick_pair {

namespace {

void AttemptToRecordEngagementFunnelFlowWithMetadata(
    scoped_refptr<Device> device,
    FastPairEngagementFlowEvent event,
    DeviceMetadata* device_metadata,
    bool has_retryable_error) {
  // TODO(b/262452942): Add logic to retry fetching metadata to record funnel
  // flow. Currently we are missing logging if fetching metadata fails, and we
  // do not retry. |has_retryable_error| is currently not used, but will be if
  // we decide to retry.
  if (!device_metadata) {
    return;
  }

  RecordFastPairDeviceAndNotificationSpecificEngagementFlow(
      *device, device_metadata->GetDetails(), event);
}

void GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
    scoped_refptr<Device> device,
    FastPairEngagementFlowEvent event) {
  FastPairRepository::Get()->GetDeviceMetadata(
      device->metadata_id(),
      base::BindOnce(&AttemptToRecordEngagementFunnelFlowWithMetadata, device,
                     event));
}

void AttemptToRecordRetroactiveEngagementFunnelFlowWithMetadata(
    scoped_refptr<Device> device,
    FastPairRetroactiveEngagementFlowEvent event,
    DeviceMetadata* device_metadata,
    bool has_retryable_error) {
  // TODO(b/262452942): Add logic to retry fetching metadata to record funnel
  // flow. Currently we are missing logging if fetching metadata fails, and we
  // do not retry.
  if (!device_metadata) {
    return;
  }

  RecordFastPairDeviceAndNotificationSpecificRetroactiveEngagementFlow(
      *device, device_metadata->GetDetails(), event);
}

void GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
    scoped_refptr<Device> device,
    FastPairRetroactiveEngagementFlowEvent event) {
  FastPairRepository::Get()->GetDeviceMetadata(
      device->metadata_id(),
      base::BindOnce(
          &AttemptToRecordRetroactiveEngagementFunnelFlowWithMetadata, device,
          event));
}

PrefService* GetLastActiveUserPrefService() {
  return Shell::Get()->session_controller()->GetLastActiveUserPrefService();
}

}  // namespace

QuickPairMetricsLogger::QuickPairMetricsLogger(
    ScannerBroker* scanner_broker,
    PairerBroker* pairer_broker,
    UIBroker* ui_broker,
    RetroactivePairingDetector* retroactive_pairing_detector)
    : feature_usage_metrics_logger_(
          std::make_unique<FastPairFeatureUsageMetricsLogger>()) {
  device::BluetoothAdapterFactory::Get()->GetAdapter(base::BindOnce(
      &QuickPairMetricsLogger::OnGetAdapter, weak_ptr_factory_.GetWeakPtr()));

  scanner_broker_observation_.Observe(scanner_broker);
  retroactive_pairing_detector_observation_.Observe(
      retroactive_pairing_detector);
  pairer_broker_observation_.Observe(pairer_broker);
  ui_broker_observation_.Observe(ui_broker);
}

QuickPairMetricsLogger::~QuickPairMetricsLogger() = default;

void QuickPairMetricsLogger::OnGetAdapter(
    scoped_refptr<device::BluetoothAdapter> adapter) {
  adapter_ = adapter;
  adapter_observation_.Observe(adapter_.get());
}

const device::BluetoothDevice* QuickPairMetricsLogger::GetBluetoothDevice(
    scoped_refptr<Device> device) const {
  if (!adapter_) {
    return nullptr;
  }

  device::BluetoothDevice* bt_device = nullptr;
  // First, try to get the Bluetooth Device via the classic address since it's
  // more stable than the BLE address.
  if (device->classic_address()) {
    bt_device = adapter_->GetDevice(device->classic_address().value());
    CD_LOG(VERBOSE, Feature::FP)
        << __func__
        << ": Structured Classic device found: " << (bt_device ? "yes" : "no");
  }

  if (!bt_device) {
    bt_device = adapter_->GetDevice(device->ble_address());
    CD_LOG(VERBOSE, Feature::FP)
        << __func__
        << ": Structured LE device found: " << (bt_device ? "yes" : "no");
  }

  return bt_device;
}

void QuickPairMetricsLogger::DevicePairedChanged(
    device::BluetoothAdapter* adapter,
    device::BluetoothDevice* device,
    bool new_paired_status) {
  // This event fires whenever a device pairing has changed with the adapter.
  // If the |new_paired_status| is false, it means a device was unpaired with
  // the adapter, so we early return since it would not be a device that has
  // been paired alternatively. If the device that was paired to that fires this
  // event is a device we just paired to with Fast Pair, then we early return
  // since it also wouldn't be one that was alternatively pair to. We want to
  // only continue our check here if we have a newly paired device that was
  // paired with classic Bluetooth pairing.
  const std::string& classic_address = device->GetAddress();
  if (!new_paired_status ||
      base::Contains(fast_pair_addresses_, classic_address)) {
    return;
  }

  RecordPairingMethod(PairingMethod::kSystemPairingUi);
}

void QuickPairMetricsLogger::OnDevicePaired(scoped_refptr<Device> device) {
  AttemptRecordingFastPairEngagementFlow(
      *device, FastPairEngagementFlowEvent::kPairingSucceeded);
  GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
      device, FastPairEngagementFlowEvent::kPairingSucceeded);
  RecordPairingResult(*device, /*success=*/true);
  feature_usage_metrics_logger_->RecordUsage(/*success=*/true);

  base::TimeDelta total_pair_time =
      base::TimeTicks::Now() - device_pairing_start_timestamps_[device];
  AttemptRecordingTotalUxPairTime(*device, total_pair_time);
  RecordPairingMethod(PairingMethod::kFastPair);

  if (device->protocol() == Protocol::kFastPairInitial) {
    RecordInitialSuccessFunnelFlow(
        FastPairInitialSuccessFunnelEvent::kPairingComplete);
  }

  // The classic address is assigned to the Device during the
  // initial Fast Pair pairing protocol during the key exchange, and if it
  // doesn't exist, then it wasn't properly paired during initial Fast Pair
  // pairing. We want to save the addresses here in the event that the
  // Bluetooth adapter pairing event fires, so we can detect when a device
  // was paired solely via classic bluetooth, instead of Fast Pair.
  if (device->classic_address()) {
    fast_pair_addresses_.insert(device->classic_address().value());
  }
}

void QuickPairMetricsLogger::OnPairFailure(scoped_refptr<Device> device,
                                           PairFailure failure) {
  AttemptRecordingFastPairEngagementFlow(
      *device, FastPairEngagementFlowEvent::kPairingFailed);
  GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
      device, FastPairEngagementFlowEvent::kPairingFailed);
  base::TimeDelta total_pair_time =
      base::TimeTicks::Now() - device_pairing_start_timestamps_[device];
  device_pairing_start_timestamps_.erase(device);
  AttemptRecordingTotalUxPairTime(*device, total_pair_time);

  feature_usage_metrics_logger_->RecordUsage(/*success=*/false);
  RecordPairingFailureReason(*device, failure);
  RecordPairingResult(*device, /*success=*/false);
  RecordStructuredPairFailure(*device, failure);
}

void QuickPairMetricsLogger::OnDiscoveryAction(scoped_refptr<Device> device,
                                               DiscoveryAction action) {
  switch (action) {
    case DiscoveryAction::kPairToDevice: {
      switch (device->protocol()) {
        case Protocol::kFastPairSubsequent:
          RecordSubsequentSuccessFunnelFlow(
              FastPairSubsequentSuccessFunnelEvent::kNotificationsClicked);
          RecordStructuredPairingStarted(*device, GetBluetoothDevice(device));
          break;
        case Protocol::kFastPairInitial:
          RecordInitialSuccessFunnelFlow(
              FastPairInitialSuccessFunnelEvent::kNotificationsClicked);
          RecordStructuredPairingStarted(*device, GetBluetoothDevice(device));
          break;
        case Protocol::kFastPairRetroactive:
          break;
      }

      if (base::Contains(discovery_learn_more_devices_, device)) {
        AttemptRecordingFastPairEngagementFlow(
            *device, FastPairEngagementFlowEvent::
                         kDiscoveryUiConnectPressedAfterLearnMorePressed);
        GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
            device, FastPairEngagementFlowEvent::
                        kDiscoveryUiConnectPressedAfterLearnMorePressed);
        discovery_learn_more_devices_.erase(device);
        break;
      }

      PrefService* pref = GetLastActiveUserPrefService();
      if (pref->FindPreference(ash::prefs::kUserPairedWithFastPair)) {
        pref->SetBoolean(ash::prefs::kUserPairedWithFastPair, true);
      }

      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kDiscoveryUiConnectPressed);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kDiscoveryUiConnectPressed);
      device_pairing_start_timestamps_[device] = base::TimeTicks::Now();
      break;
    }
    case DiscoveryAction::kLearnMore:
      // We need to record whether or not the Discovery UI for this
      // device has had the Learn More button pressed because since the
      // Learn More button is not a terminal state, we need to record
      // if the subsequent terminal states were reached after the user
      // has learned more about saving their accounts. So we will check
      // this map when the user dismisses or saves their account in order
      // to capture whether or not the user elected to learn more beforehand.
      discovery_learn_more_devices_.insert(device);

      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kDiscoveryUiLearnMorePressed);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kDiscoveryUiLearnMorePressed);
      break;
    case DiscoveryAction::kDismissedByUser:
      if (base::Contains(discovery_learn_more_devices_, device)) {
        AttemptRecordingFastPairEngagementFlow(
            *device, FastPairEngagementFlowEvent::
                         kDiscoveryUiDismissedByUserAfterLearnMorePressed);
        GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
            device, FastPairEngagementFlowEvent::
                        kDiscoveryUiDismissedByUserAfterLearnMorePressed);
        discovery_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kDiscoveryUiDismissedByUser);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kDiscoveryUiDismissedByUser);
      break;
    case DiscoveryAction::kDismissedByOs:
      if (base::Contains(discovery_learn_more_devices_, device)) {
        AttemptRecordingFastPairEngagementFlow(
            *device, FastPairEngagementFlowEvent::
                         kDiscoveryUiDismissedAfterLearnMorePressed);
        GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
            device, FastPairEngagementFlowEvent::
                        kDiscoveryUiDismissedAfterLearnMorePressed);
        discovery_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kDiscoveryUiDismissed);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kDiscoveryUiDismissed);
      break;
    case DiscoveryAction::kDismissedByTimeout:
      if (base::Contains(discovery_learn_more_devices_, device)) {
        AttemptRecordingFastPairEngagementFlow(
            *device, FastPairEngagementFlowEvent::
                         kDiscoveryUiDismissedByTimeoutAfterLearnMorePressed);
        GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
            device, FastPairEngagementFlowEvent::
                        kDiscoveryUiDismissedByTimeoutAfterLearnMorePressed);
        discovery_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kDiscoveryUiDismissedByTimeout);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kDiscoveryUiDismissedByTimeout);
      break;
  }
}

void QuickPairMetricsLogger::OnPairingFailureAction(
    scoped_refptr<Device> device,
    PairingFailedAction action) {
  switch (action) {
    case PairingFailedAction::kNavigateToSettings:
      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kErrorUiSettingsPressed);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kErrorUiSettingsPressed);
      break;
    case PairingFailedAction::kDismissedByUser:
      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kErrorUiDismissedByUser);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kErrorUiDismissedByUser);
      break;
    case PairingFailedAction::kDismissed:
      AttemptRecordingFastPairEngagementFlow(
          *device, FastPairEngagementFlowEvent::kErrorUiDismissed);
      GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
          device, FastPairEngagementFlowEvent::kErrorUiDismissed);
      break;
  }
}

void QuickPairMetricsLogger::OnDeviceFound(scoped_refptr<Device> device) {
  AttemptRecordingFastPairEngagementFlow(
      *device, FastPairEngagementFlowEvent::kDiscoveryUiShown);
  GetDeviceMetadataAndLogEngagementFunnelWithMetadata(
      device, FastPairEngagementFlowEvent::kDiscoveryUiShown);
  RecordStructuredDiscoveryNotificationShown(*device,
                                             GetBluetoothDevice(device));
}

void QuickPairMetricsLogger::OnPairingStart(scoped_refptr<Device> device) {
  RecordFastPairInitializePairingProcessEvent(
      *device, FastPairInitializePairingProcessEvent::kInitializationStarted);

  switch (device->protocol()) {
    case Protocol::kFastPairSubsequent:
      RecordSubsequentSuccessFunnelFlow(
          FastPairSubsequentSuccessFunnelEvent::kInitializationStarted);
      break;
    case Protocol::kFastPairInitial:
      RecordInitialSuccessFunnelFlow(
          FastPairInitialSuccessFunnelEvent::kInitializationStarted);
      break;
    case Protocol::kFastPairRetroactive:
      RecordRetroactiveSuccessFunnelFlow(
          FastPairRetroactiveSuccessFunnelEvent::kInitializationStarted);
      break;
  }
}

void QuickPairMetricsLogger::OnHandshakeComplete(scoped_refptr<Device> device) {
  RecordFastPairInitializePairingProcessEvent(
      *device, FastPairInitializePairingProcessEvent::kInitializationComplete);

  switch (device->protocol()) {
    case Protocol::kFastPairSubsequent:
      RecordSubsequentSuccessFunnelFlow(
          FastPairSubsequentSuccessFunnelEvent::kPairingStarted);
      break;
    case Protocol::kFastPairInitial:
      RecordInitialSuccessFunnelFlow(
          FastPairInitialSuccessFunnelEvent::kPairingStarted);
      break;
    case Protocol::kFastPairRetroactive:
      RecordRetroactiveSuccessFunnelFlow(
          FastPairRetroactiveSuccessFunnelEvent::kWritingAccountKey);
      break;
  }
}

void QuickPairMetricsLogger::OnPairingComplete(scoped_refptr<Device> device) {
  switch (device->protocol()) {
    case Protocol::kFastPairSubsequent:
      RecordSubsequentSuccessFunnelFlow(
          FastPairSubsequentSuccessFunnelEvent::kProcessComplete);
      RecordStructuredPairingComplete(*device, GetBluetoothDevice(device));
      break;
    case Protocol::kFastPairInitial:
      RecordInitialSuccessFunnelFlow(
          FastPairInitialSuccessFunnelEvent::kProcessComplete);
      RecordStructuredPairingComplete(*device, GetBluetoothDevice(device));
      break;
    case Protocol::kFastPairRetroactive:
      break;
  }
}

void QuickPairMetricsLogger::OnRetroactivePairFound(
    scoped_refptr<Device> device) {
  // When a device for the retroactive pairing scenario is detected, the
  // corresponding "Associate Account" retroactive pairing notification is
  // presenting to the user.
  AttemptRecordingFastPairRetroactiveEngagementFlow(
      *device,
      FastPairRetroactiveEngagementFlowEvent::kAssociateAccountUiShown);
  GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
      device, FastPairRetroactiveEngagementFlowEvent::kAssociateAccountUiShown);
  RecordRetroactiveSuccessFunnelFlow(
      FastPairRetroactiveSuccessFunnelEvent::kDeviceDetected);
  RecordStructuredPairingStarted(*device, GetBluetoothDevice(device));
}

void QuickPairMetricsLogger::OnAssociateAccountAction(
    scoped_refptr<Device> device,
    AssociateAccountAction action) {
  switch (action) {
    case AssociateAccountAction::kAssociateAccount: {
      if (base::Contains(associate_account_learn_more_devices_, device)) {
        AttemptRecordingFastPairRetroactiveEngagementFlow(
            *device, FastPairRetroactiveEngagementFlowEvent::
                         kAssociateAccountSavePressedAfterLearnMorePressed);
        GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
            device, FastPairRetroactiveEngagementFlowEvent::
                        kAssociateAccountSavePressedAfterLearnMorePressed);
        associate_account_learn_more_devices_.erase(device);
        break;
      }

      PrefService* pref = GetLastActiveUserPrefService();
      if (pref->FindPreference(ash::prefs::kUserPairedWithFastPair)) {
        pref->SetBoolean(ash::prefs::kUserPairedWithFastPair, true);
      }

      AttemptRecordingFastPairRetroactiveEngagementFlow(
          *device,
          FastPairRetroactiveEngagementFlowEvent::kAssociateAccountSavePressed);
      GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
          device,
          FastPairRetroactiveEngagementFlowEvent::kAssociateAccountSavePressed);
      RecordRetroactiveSuccessFunnelFlow(
          FastPairRetroactiveSuccessFunnelEvent::kSaveRequested);
      break;
    }
    case AssociateAccountAction::kLearnMore:
      // We need to record whether or not the Associate Account UI for this
      // device has had the Learn More button pressed because since the
      // Learn More button is not a terminal state, we need to record
      // if the subsequent terminal states were reached after the user
      // has learned more about saving their accounts. So we will check
      // this map when the user dismisses or saves their account in order
      // to capture whether or not the user elected to learn more beforehand.
      associate_account_learn_more_devices_.insert(device);

      AttemptRecordingFastPairRetroactiveEngagementFlow(
          *device, FastPairRetroactiveEngagementFlowEvent::
                       kAssociateAccountLearnMorePressed);
      GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
          device, FastPairRetroactiveEngagementFlowEvent::
                      kAssociateAccountLearnMorePressed);
      break;
    case AssociateAccountAction::kDismissedByUser:
      if (base::Contains(associate_account_learn_more_devices_, device)) {
        AttemptRecordingFastPairRetroactiveEngagementFlow(
            *device, FastPairRetroactiveEngagementFlowEvent::
                         kAssociateAccountDismissedByUserAfterLearnMorePressed);
        GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
            device, FastPairRetroactiveEngagementFlowEvent::
                        kAssociateAccountDismissedByUserAfterLearnMorePressed);
        associate_account_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairRetroactiveEngagementFlow(
          *device, FastPairRetroactiveEngagementFlowEvent::
                       kAssociateAccountUiDismissedByUser);
      GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
          device, FastPairRetroactiveEngagementFlowEvent::
                      kAssociateAccountUiDismissedByUser);
      break;
    case AssociateAccountAction::kDismissedByTimeout:
      if (base::Contains(associate_account_learn_more_devices_, device)) {
        AttemptRecordingFastPairRetroactiveEngagementFlow(
            *device,
            FastPairRetroactiveEngagementFlowEvent::
                kAssociateAccountDismissedByTimeoutAfterLearnMorePressed);
        GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
            device,
            FastPairRetroactiveEngagementFlowEvent::
                kAssociateAccountDismissedByTimeoutAfterLearnMorePressed);
        associate_account_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairRetroactiveEngagementFlow(
          *device, FastPairRetroactiveEngagementFlowEvent::
                       kAssociateAccountUiDismissedByTimeout);
      GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
          device, FastPairRetroactiveEngagementFlowEvent::
                      kAssociateAccountUiDismissedByTimeout);
      break;
    case AssociateAccountAction::kDismissedByOs:
      if (base::Contains(associate_account_learn_more_devices_, device)) {
        AttemptRecordingFastPairRetroactiveEngagementFlow(
            *device, FastPairRetroactiveEngagementFlowEvent::
                         kAssociateAccountDismissedAfterLearnMorePressed);
        GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
            device, FastPairRetroactiveEngagementFlowEvent::
                        kAssociateAccountDismissedAfterLearnMorePressed);
        associate_account_learn_more_devices_.erase(device);
        break;
      }

      AttemptRecordingFastPairRetroactiveEngagementFlow(
          *device,
          FastPairRetroactiveEngagementFlowEvent::kAssociateAccountUiDismissed);
      GetDeviceMetadataAndLogRetroactiveEngagementFunnelWithMetadata(
          device,
          FastPairRetroactiveEngagementFlowEvent::kAssociateAccountUiDismissed);
      break;
  }
}

void QuickPairMetricsLogger::OnAccountKeyWrite(
    scoped_refptr<Device> device,
    std::optional<AccountKeyFailure> error) {
  switch (device->protocol()) {
    case Protocol::kFastPairSubsequent:
      // TODO(b/259443372): Record this case once we implement account key
      // writing in all scenarios,
      NOTREACHED();
    case Protocol::kFastPairInitial:
      break;
    case Protocol::kFastPairRetroactive:
      RecordRetroactivePairingResult(/*success=*/!error.has_value());

      if (!error.has_value()) {
        RecordRetroactiveSuccessFunnelFlow(
            FastPairRetroactiveSuccessFunnelEvent::kAccountKeyWrittenToDevice);
        RecordStructuredPairingComplete(*device, GetBluetoothDevice(device));
      }
      // TODO(jackshira): Log new PairFailure case here.
      break;
  }

  if (error.has_value()) {
    RecordAccountKeyResult(*device, /*success=*/false);
    RecordAccountKeyFailureReason(*device, error.value());
    feature_usage_metrics_logger_->RecordUsage(/*success=*/false);
    return;
  }

  RecordAccountKeyResult(*device, /*success=*/true);
  feature_usage_metrics_logger_->RecordUsage(/*success=*/true);
}

void QuickPairMetricsLogger::OnCompanionAppAction(scoped_refptr<Device> device,
                                                  CompanionAppAction action) {}

void QuickPairMetricsLogger::OnDeviceLost(scoped_refptr<Device> device) {}

}  // namespace quick_pair
}  // namespace ash